Files
2013-03-25 21:56:44 -04:00

199 lines
6.3 KiB
Python
Executable File

#/usr/bin/env python
import getopt
import xmlrpclib
import traceback
import struct
import debugger #needed on old ID for removeHook
from immlib import *
LICENSE="BSD 3-clause non-attribution" #yay!
copyright="(C) Immunity, Inc., jms@bughunter.ca"
"""
This script supports the SQLOLEDB method of executing queries and, when
combined with sql_listener.py will send you all the queries executed by a web
application. Server-side filtering (necessary to avoid sending thousands of
queries a second to you on a busy server) is stubbed in for later. We hooked
IIS rather than SQL Server because common practice is to have your SQL tier
un-routable, but the web tier is likely to have Internet access.
Somewhat later we'll have this integrate into SPIKE Proxy and other tools to
automate detection of blind-sql attacks/detection and sql injection in
general.
In order to use this script:
1. Run a few queries against your target server, this will start up two
dllhost.exe's
2. Load Immunity Debugger and attach to the second dllhost.exe (this can be
slightly tricky if the PID for the second one is lower than the first, but
eventually we'll automate it)
3. run !sqlhooker -s myhostip:myport. For example, I use !sqlhooker
192.168.1.1:8081, and then on my .1 machine I run "python sql_listener.py
8081".
Here's an example snippet of ASP script this would work against:
_start cut_
set conn = server.createObject("ADODB.Connection")
set rs = server.createObject("ADODB.Recordset")
query = "select count(*) from users where userName='" & userName & "' and userPass='" & password & "'"
conn.Open "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=myDB; User Id=sa; Password="
rs.activeConnection = conn
rs.open query
_end cut_
We currently support:
WinXPPro Sp2, IIS 5.0 SQLServer 2000
Win2K3, IIS 6.0, SQLServer 2000
Win2K, IIS 5.0, SQLServer 2000
Win2K Old,IIS 5.0, SQLServer 2000
If anyone has requests for other database systems, they should email us, along
with the necessary information to get an application running, and we will
spend the time to find you hook spots. Or just submit a patch to
forum.immunityinc.com.
"""
class ole_hooker(LogBpHook):
def __init__(self,hook_version,xmlhost=None,xmlport=0):
LogBpHook.__init__(self)
self.imm = Debugger()
self.hook_version = hook_version
self.xmlhost = xmlhost
self.xmlport = int(xmlport)
def run(self,regs):
'''
Called everytime the SQL hook is hit.
'''
self.imm.log("Hook version: %s" % self.hook_version)
if self.hook_version == "winxp_pro_sp2" or self.hook_version == "win2k3":
sql_addr = regs['EDI']
if self.hook_version == "win2k":
sql_addr = regs['ESI']
if self.hook_version == "win2k_old":
buffer_ptr = self.imm.readMemory(regs['ESP'] + 4, 4)
buffer_ptr = struct.unpack("L", buffer_ptr)
sql_addr = buffer_ptr[0]
sql_query = self.imm.readWString(sql_addr)
sql_query = sql_query.replace("\x00","")
self.imm.log("SQL Query: %s" % sql_query)
using_xml_rpc = False
if self.xmlport != 0:
server = xmlrpclib.ServerProxy("http://%s:%d/"%(self.xmlhost,self.xmlport), allow_none=True)
self.imm.log("Using server: %s:%d"%(self.xmlhost, self.xmlport))
using_xml_rpc = True
else:
server = None
if using_xml_rpc:
#send our xml request to the remove side
#if self.filter matches...(stub for now)
try:
result = server.sendsql(("sqlquery",[sql_query]))
except:
data=traceback.format_exc()
self.imm.log("Failed to connect to remote server, sorry")
self.imm.logLines("Error was: %s"%data)
return
#Now parse what we got back - a command and list of arguments
command, arguments = result
if command=="NEWFILTER":
#stub
self.filter=arguments[0]
elif command=="UNHOOK":
#stub
self.imm.log("Unhook called")
#etc
return
def usage(imm):
imm.log("!sqlhooker.py")
imm.log("-u (to uninstall hook)")
imm.log("-s host:port (Server to send XML-RPC data to)")
def main(args):
imm = Debugger()
xmlhost = None
xmlport = 0
sql_oledb = imm.getModule("sqloledb.dll")
if not sql_oledb.isAnalysed():
imm.analyseCode(sql_oledb.getCodebase())
try:
opts,argo = getopt.getopt(args, "ius:")
except:
return usage(imm)
for o,a in opts:
if o == "-u":
if hasattr(imm, "removeHook"):
imm.removeHook("query")
elif hasattr(debugger, "remove_hook"):
debugger.remove_hook("query")
else:
imm.log("Could not remove hook - no remove hook function found!")
return "Removed hook on SQL function."
if o == "-s":
xmlhost,xmlport = a.split(":")
# Various versions, we need to match on
winxp_pro_sp2 = "2000.085.1117.00 (xpsp_sp2_rtm."
win2k3 = "2000.086.3959.00 (srv03_sp2_rtm"
win2k = "2000.081.9031.018"
win2k_old = "2000.080.0194"
version = sql_oledb.getVersion()
sql_base = sql_oledb.getBaseAddress()
if version == winxp_pro_sp2:
offset = 0xF6F5
hook_version = "winxp_pro_sp2"
if version == win2k3:
offset = 0x6522
hook_version = "win2k3"
if version == win2k:
offset = 0xFA2D
hook_version = "win2k"
if version == win2k_old:
offset = 0x4034
hook_version = "win2k_old"
bp_address = sql_base + offset
# Set a hook
hooker = ole_hooker(hook_version,xmlhost,xmlport)
hooker.add("query",bp_address)
return "SQL Hooks in Place. Ready for Test Cases."