From a0290a257b50defeded72771277ca514cd908982 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Sat, 27 Mar 2010 21:50:19 +0000 Subject: [PATCH] Added support to connect directly also to Oracle - see #158 --- lib/core/common.py | 4 +- lib/request/connect.py | 3 ++ lib/request/inject.py | 6 +++ plugins/dbms/oracle/connector.py | 64 ++++++++++++++++++++++++++++-- plugins/dbms/oracle/enumeration.py | 3 +- plugins/dbms/oracle/fingerprint.py | 27 +++++++++---- sqlmap.py | 3 ++ 7 files changed, 98 insertions(+), 12 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 139b9f2b7..c2c3064e5 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -628,7 +628,7 @@ def parseTargetDirect(): dbmsDict = { "Microsoft SQL Server": [MSSQL_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/"], "MySQL": [MYSQL_ALIASES, "python-mysqldb", "http://mysql-python.sourceforge.net/"], "PostgreSQL": [PGSQL_ALIASES, "python-psycopg2", "http://initd.org/psycopg/"], - "Oracle": [ORACLE_ALIASES, "", ""], + "Oracle": [ORACLE_ALIASES, "python cx_Oracle", "http://cx-oracle.sourceforge.net/"], "SQLite": [SQLITE_ALIASES, "", ""], "Access": [ACCESS_ALIASES, "", ""], "Firebird": [FIREBIRD_ALIASES, "", ""] } @@ -642,6 +642,8 @@ def parseTargetDirect(): import MySQLdb elif dbmsName == "PostgreSQL": import psycopg2 + elif dbmsName == "Oracle": + import cx_Oracle except ImportError, _: errMsg = "sqlmap requires %s third-party library " % data[1] errMsg += "in order to directly connect to the database " diff --git a/lib/request/connect.py b/lib/request/connect.py index 54eae7bf3..6857253df 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -268,6 +268,9 @@ class Connect: values = None select = False + if kb.dbms == "Oracle" and value.startswith("SELECT ") and " FROM " not in value: + value = "%s FROM DUAL" % value + for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlStatement in sqlStatements: if value.lower().startswith(sqlStatement) and sqlTitle == "SQL SELECT statement": diff --git a/lib/request/inject.py b/lib/request/inject.py index 497e975b0..f34f1a701 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -356,6 +356,9 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, values = None select = False + if kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression: + expression = "%s FROM DUAL" % expression + for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlStatement in sqlStatements: if expression.lower().startswith(sqlStatement) and sqlTitle == "SQL SELECT statement": @@ -419,6 +422,9 @@ def goStacked(expression, silent=False): values = None select = False + if kb.dbms == "Oracle" and expression.startswith("SELECT ") and " FROM " not in expression: + expression = "%s FROM DUAL" % expression + for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlStatement in sqlStatements: if expression.lower().startswith(sqlStatement) and sqlTitle == "SQL SELECT statement": diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index e33d9226d..9e5ec611f 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -22,14 +22,72 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ +try: + import cx_Oracle +except ImportError, _: + pass + +from lib.core.data import logger +from lib.core.exception import sqlmapConnectionException + from plugins.generic.connector import Connector as GenericConnector class Connector(GenericConnector): """ - Homepage: - User guide: - API: + Homepage: http://cx-oracle.sourceforge.net/ + User guide: http://cx-oracle.sourceforge.net/README.txt + API: http://cx-oracle.sourceforge.net/html/index.html + Debian package: - + License: http://cx-oracle.sourceforge.net/LICENSE.txt + + Possible connectors: - """ def __init__(self): GenericConnector.__init__(self) + + def connect(self): + self.initConnection() + self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) + + try: + self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) + logger.info("successfully connected as SYSDBA") + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError), _: + try: + self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError), msg: + raise sqlmapConnectionException, msg + + self.setCursor() + self.connected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except cx_Oracle.InterfaceError, msg: + logger.log(8, msg) + return None + + def execute(self, query): + logger.debug(query) + + try: + self.cursor.execute(query) + except (cx_Oracle.DatabaseError), msg: + logger.log(8, msg) + except cx_Oracle.InternalError, msg: + raise sqlmapConnectionException, msg + + self.connector.commit() + + def select(self, query): + self.execute(query) + return self.fetchall() + + def setCursor(self): + self.cursor = self.connector.cursor() + + def close(self): + self.cursor.close() + self.connector.close() diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 25b47475c..644f57a21 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -22,6 +22,7 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ +from lib.core.common import getRange from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -49,7 +50,7 @@ class Enumeration(GenericEnumeration): # Set containing the list of DBMS administrators areAdmins = set() - if kb.unionPosition: + if kb.unionPosition or conf.direct: if query2: query = rootQuery["inband"]["query2"] condition = rootQuery["inband"]["condition2"] diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 182f4d972..384e3d3b3 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -78,6 +78,12 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): + logMsg = "testing Oracle" + logger.info(logMsg) + + if conf.direct: + conf.dbmsConnector.connect() + if conf.dbms in ORACLE_ALIASES: setDbms("Oracle") @@ -86,18 +92,25 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True - logMsg = "testing Oracle" - logger.info(logMsg) - - payload = agent.fullPayload(" AND ROWNUM=ROWNUM") - result = Request.queryPage(payload) + # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting + # directly to the Oracle database + if conf.direct: + result = True + else: + payload = agent.fullPayload(" AND ROWNUM=ROWNUM") + result = Request.queryPage(payload) if result: logMsg = "confirming Oracle" logger.info(logMsg) - payload = agent.fullPayload(" AND LENGTH(SYSDATE)=LENGTH(SYSDATE)") - result = Request.queryPage(payload) + # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does + # not work connecting directly to the Oracle database + if conf.direct: + result = True + else: + payload = agent.fullPayload(" AND LENGTH(SYSDATE)=LENGTH(SYSDATE)") + result = Request.queryPage(payload) if not result: warnMsg = "the back-end DMBS is not Oracle" diff --git a/sqlmap.py b/sqlmap.py index 351db332d..cb1629db8 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -26,6 +26,9 @@ import os import sys import time import traceback +import warnings + +warnings.filterwarnings(action="ignore", message=".*was already imported", category=UserWarning) try: import psyco