Oracle Database PL/SQL fuzzing tool.
d4ba0ce4454a94e3b86f4914ee4e6dbc5be062e4b669e80ff3b6fd13d319f285
#!/usr/bin/python
"""
Oracle Database PL/SQL Fuzzing Tool
Copyright (c) 2005, 2006 Joxean Koret, joxeankoret [at] yahoo.es
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
import sys
import cx_Oracle
global connection
funnydata = ("TEST", "SYS", "XMLREF", '" || XMLREF() || "', 'TEST" A A ', "'", '"', "A"*30, "A"*100, "A"*128,"A"*256,"A"*512,"A"*1024,
"A"*2048,"A"*3000,"A"*4000,"A"*5000,"A"*6000,"A"*7000,"A"*8000,"A"*10000,"A"*15000,"A"*20000,"A"*25000,
"A"*30000,"A"*32767, -1, -2, 0, 1, 2, 2147483647, -2147483647, 2147483648, -2147483648,
"ROWID", "PRIMARY KEY", "%s%s%s%s%s%s%s", "%x%x%x%x%x%x", "%d%d%d%d%d%d",
"GRANT DBA TO TEST", "GRANT DBA TO PUBLIC", "SELECT * FROM DBA_USERS",
"' OR '1'='1", "AA' or ""TEST"".""XMLREF"" ","V1", "TEST.V1", '"TEST"."V1"',
None)
def fuzzData(data, index):
global connection
for x in funnydata:
try:
if type(x) is int:
print "Data is number",x
else:
print "Data is " + str(x)[0:30] + " of length " + str(len(str(x)))
varList = []
for var in range(index):
varList.append(x)
cur = connection.cursor()
cur.execute(data, varList)
except:
error = str(sys.exc_info()[1])
if error.upper().find("ORA-00933") > -1 or error.upper().find("ORA-01756:") > -1 or error.upper().find("ORA-00923:") > -1:
print "*** POSSIBLE SQL INJECTION FOUND ***"
elif error.upper().find("ORA-03113") > -1:
if len(str(x)) > 50:
print "*** POSSIBLE BUFFER OVERFLOW ***"
else:
print "*** INSTANCE CRASHED ***"
print "Reconnecting ... "
connect()
elif error.upper().find("ORA-00600") > -1:
print "*** INTERNAL ERROR ***"
elif error.upper().find("PLS-00306:") > -1:
print "Currently unfuzzable :("
continue
elif error.upper().find("ORA-03114") > -1:
print "We are not connected :?"
connect()
print error
def connect():
global connection
link = "test/test@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.10)(PORT=1521)))"
link += "(CONNECT_DATA=(SERVICE_NAME=orcl)))"
connection = cx_Oracle.connect(link)
connection.rollback()
connection.commit()
def isFunc(data, index, cursorData):
global connection
try:
varList = []
data = """BEGIN
"""+ data + """("""
index = 0
for x in cursorData:
index += 1
if index == 1:
data += str(x[1]) + "=>:" + str(index)
else:
data += "," + str(x[1]) + "=>:" + str(index)
data += """);
end;"""
for var in range(index):
varList.append(None)
cur = connection.cursor()
cur.execute(data, varList)
return 0
except:
error = str(sys.exc_info()[1])
if error.upper().find("PLS-00221")> -1:
return 1
else:
return 0
def die(msg):
print msg
sys.exit(0)
def main():
global connection
fuzzPackages = """
select distinct owner "Owner",
package_name "Package",
package_name "Package",
package_name "Package",
object_name "Program_Unit"
from sys.all_arguments x
where argument_name is not null
and not exists (select 1 from sys.all_arguments y
where x.owner = y.owner
and x.package_name = y.owner
and x.object_name = y.object_name
and x.data_level = y.data_level
and y.data_type not in ('VARCHAR2', 'RAW', 'NCHAR', 'BINARY_INTEGER', 'BINARY_FLOAT',
'CHAR', 'NVARCHAR2', 'NUMBER', 'FLOAT', 'LONG RAW')
and rownum = 1)
order by owner, package_name, object_name
"""
packageProcedures = """
select position "Position",
argument_name "Argument",
data_type "Data type",
initcap(in_out) "In_Out",
owner sdev_link_owner,
package_name sdev_link_name,
'PACKAGE' sdev_link_type
from sys.all_arguments
where argument_name is not null
and owner = :1
and (:2 is null or
instr(upper(object_name),upper(:3)) > 0 or
instr(upper(package_name),upper(:4)) > 0 )
and object_name = :5
and data_type in ('VARCHAR2', 'RAW', 'NCHAR', 'BINARY_INTEGER', 'BINARY_FLOAT',
'CHAR', 'NVARCHAR2', 'NUMBER', 'FLOAT', 'LONG RAW')
order by owner, package_name, object_name, position
"""
connect()
bStart = False
try:
cursor = connection.cursor()
cursor.execute(fuzzPackages)
result = """
BEGIN
"""
pkgName = ""
func = 0
print "Running first query. It may take a long while ... "
totalProcs = 0
for pkgData in cursor.fetchall():
totalProcs += 1
if not pkgData[1] is None:
pkgName = pkgData[0] + "." + pkgData[1] + "." + pkgData[4]
else:
pkgName = pkgData[0] + "." + pkgData[4]
procCursor = connection.cursor()
procCursor.execute(packageProcedures, pkgData)
procCursorData = procCursor.fetchall()
func = isFunc(pkgName, len(procCursorData), procCursorData)
if int(func) == 0:
data = """BEGIN
""" + pkgName + """("""
else:
data = """SELECT """ + pkgName + """("""
index = 0
prevX = None
for x in procCursorData:
if x == prevX:
continue
prevX = x
index += 1
if index == 1:
if func == 0:
data += str(x[1]) + "=>:" + str(index)
else:
data += ":" + str(index)
else:
if func == 0:
data += "," + str(x[1]) + "=>:" + str(index)
else:
data += ", :" + str(index)
if func == 0:
data += """);
end;"""
else:
data += """) from dual """
print "----------"
print data
print "----------"
fuzzData(data, index)
connection.close()
except Exception, e:
print "Error",e
print "While fuzzing index",totalProcs,"relative to",pkgName
raise e
print
print "Fuzzed",totalProcs,"procedure(s) and function(s)."
print "Done."
if __name__ == "__main__":
main()