<<< Date Index >>>     <<< Thread Index >>>

Oracle PL/SQL Fuzzing Tool



Hi to all,

In the past I wrote a python tool to fuzz PL/SQL procedures, functions
and packages. With this wonderfull tool I found many vulnerabilities,
many crashes and many-many interesting issues.

I decided to release it to the public because it's a part of an Oracle
specific Vulnerability Assesment Tool I will release when it's
completely finished. It will be licensed under the GPL.

To use the attached python tool you will need a valid Oracle database
account with, at least, the CREATE SESSION privilege granted. You will
need to adapt it to your feets to fuzz a database under your control. At
least: username, password, Oracle SID and IP address.

It only fuzzes 'VARCHAR2', 'RAW', 'NCHAR', 'BINARY_INTEGER',
'BINARY_FLOAT', 'CHAR', 'NVARCHAR2', 'NUMBER', 'FLOAT' and 'LONG RAW'
datatypes, at the moment, but you can easily adapt it to fuzz other
Oracle datatypes, even user defined.

Well, if you find it interesting or if you have any question about, any
criticism, etc... Don't heasitate to contact me. Take fun.

---
Joxean Koret

-----------------------------------
Agian, agian, egün batez
jeikiko dira egiazko Ziberotarrak,
egiazko eüskaldünak,
tirano arrotzen hiltzeko 
eta gure aiten aitek ützi daikien 
lurraren popüliari erremetitzeko.
-----------------------------------
#!/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()

Attachment: signature.asc
Description: Esta parte del mensaje =?ISO-8859-1?Q?est=E1?= firmada digitalmente