From 5fdebb5d5b4e006e70815fee942ada20949d4277 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 31 Mar 2010 10:50:47 +0000 Subject: [PATCH] Added support to directly connect also to Microsoft SQL Server database. Fixed direct connection to always use the same query as of UNION query SQL injection (= one query with multiple columns/entries output). Minor fixes to Firebird/Access/SQLite connectors to use connector's execute()/fetchall() as wrapper for third-party libraries' methods. Forced conf.timeout to 10 seconds when directly connecting to database. Slightly improved regular expression to parse -d parameter. Added import check for all connectors' third-party libraries. Code refactoring: * Moved conf.direct request to direct() function in lib/request/direct.py (code reused where needed). * Back-delegated to generic connector close() and other methods. --- lib/controller/action.py | 3 -- lib/controller/handler.py | 3 ++ lib/core/common.py | 37 ++++++++------ lib/request/connect.py | 35 +------------- lib/request/direct.py | 64 +++++++++++++++++++++++++ lib/request/inject.py | 49 ++----------------- plugins/dbms/access/connector.py | 26 +++------- plugins/dbms/access/fingerprint.py | 1 + plugins/dbms/firebird/connector.py | 23 ++++----- plugins/dbms/mssqlserver/connector.py | 37 ++++++++++++++ plugins/dbms/mssqlserver/enumeration.py | 4 +- plugins/dbms/mssqlserver/fingerprint.py | 9 +++- plugins/dbms/mysql/connector.py | 13 +---- plugins/dbms/mysql/fingerprint.py | 6 +-- plugins/dbms/oracle/connector.py | 16 +------ plugins/dbms/oracle/enumeration.py | 2 +- plugins/dbms/oracle/fingerprint.py | 6 +-- plugins/dbms/postgresql/connector.py | 13 +---- plugins/dbms/postgresql/fingerprint.py | 6 +-- plugins/dbms/sqlite/connector.py | 22 +++------ plugins/generic/connector.py | 37 ++++++++------ plugins/generic/enumeration.py | 16 ++++--- 22 files changed, 205 insertions(+), 223 deletions(-) create mode 100644 lib/request/direct.py diff --git a/lib/controller/action.py b/lib/controller/action.py index 3cd59a674..d1f7e737f 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -65,9 +65,6 @@ def action(): raise sqlmapUnsupportedDBMSException, errMsg - if conf.direct: - conf.dbmsConnector.connect() - print "%s\n" % conf.dbmsHandler.getFingerprint() # Techniques options diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 1bc002aa7..a02b65a1f 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -79,6 +79,9 @@ def setHandler(): conf.dbmsConnector = dbmsConn() if conf.direct: + logger.debug("forcing timeout to 10 seconds") + conf.timeout = 10 + conf.dbmsConnector.connect() if handler.checkDbms(): diff --git a/lib/core/common.py b/lib/core/common.py index 3c8a86df9..755cbe84d 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -606,49 +606,50 @@ def parseTargetDirect(): details = None for dbms in SUPPORTED_DBMS: - details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.+?)\@)?(?P(?P.+?)\:(?P[\d]+)\/)?(?P.+?)$" % dbms, conf.direct, re.I) + details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.+?)\@)?(?P(?P.+?)\:(?P[\d]+)\/)?(?P[\w\d\.\_\-\/]+?)$" % dbms, conf.direct, re.I) if details: - conf.dbms = details.group('dbms') - + conf.dbms = details.group('dbms') + if details.group('credentials'): - conf.dbmsUser = details.group('dbmsUser') - conf.dbmsPass = details.group('dbmsPass') + conf.dbmsUser = details.group('user') + conf.dbmsPass = details.group('pass') else: conf.dbmsUser = str() conf.dbmsPass = str() - + if details.group('remote'): conf.hostname = details.group('hostname') conf.port = int(details.group('port')) else: conf.hostname = "localhost" - conf.port = 0 - - conf.dbmsDb = details.group('dbmsDb') + conf.port = 0 + + conf.dbmsDb = details.group('db') conf.parameters[None] = "direct connection" break if not details: - errMsg = "invalid target details, valid syntax is for instance: 'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME'" - errMsg += " and/or: 'access://DATABASE_FILEPATH'" + errMsg = "invalid target details, valid syntax is for instance " + errMsg += "'mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME' " + errMsg += "or 'access://DATABASE_FILEPATH'" raise sqlmapSyntaxException, errMsg - # TODO: add details for others python DBMS libraries 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, "python cx_Oracle", "http://cx-oracle.sourceforge.net/"], - "SQLite": [SQLITE_ALIASES, "", ""], - "Access": [ACCESS_ALIASES, "", ""], - "Firebird": [FIREBIRD_ALIASES, "", ""] } + "SQLite": [SQLITE_ALIASES, "python-pysqlite2", "http://pysqlite.googlecode.com/"], + "Access": [ACCESS_ALIASES, "python-pyodbc", "http://pyodbc.googlecode.com/"], + "Firebird": [FIREBIRD_ALIASES, "python-kinterbasdb", "http://kinterbasdb.sourceforge.net/"] } for dbmsName, data in dbmsDict.items(): if conf.dbms in data[0]: try: if dbmsName == "Microsoft SQL Server": + import _mssql import pymssql elif dbmsName == "MySQL": import MySQLdb @@ -656,6 +657,12 @@ def parseTargetDirect(): import psycopg2 elif dbmsName == "Oracle": import cx_Oracle + elif dbmsName == "SQLite": + import sqlite3 + elif dbmsName == "Access": + import pyodbc + elif dbmsName == "Firebird": + import kinterbasdb 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 5ec68cb4f..93d7aa534 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -42,6 +42,7 @@ from lib.core.settings import SQL_STATEMENTS from lib.request.basic import decodePage from lib.request.basic import forgeHeaders from lib.request.basic import parseResponse +from lib.request.direct import direct from lib.request.comparison import comparison @@ -265,39 +266,7 @@ class Connect: """ if conf.direct: - 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": - select = True - break - - if select: - values = conf.dbmsConnector.select(value) - else: - values = conf.dbmsConnector.execute(value) - - if values is None or len(values) == 0: - return None - elif content: - if len(values) == 1: - if len(values[0]) == 1: - return str(list(values)[0][0]), None - else: - return list(values), None - else: - return values, None - else: - for value in values: - if value[0] in (1, -1): - return True - else: - return False + return direct(value, content) get = None post = None diff --git a/lib/request/direct.py b/lib/request/direct.py new file mode 100644 index 000000000..4be59a232 --- /dev/null +++ b/lib/request/direct.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +""" +$Id$ + +This file is part of the sqlmap project, http://sqlmap.sourceforge.net. + +Copyright (c) 2007-2010 Bernardo Damele A. G. +Copyright (c) 2006 Daniele Bellucci + +sqlmap 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. + +sqlmap 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 sqlmap; if not, write to the Free Software Foundation, Inc., 51 +Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +""" + +from lib.core.agent import agent +from lib.core.data import conf +from lib.core.data import kb +from lib.core.settings import SQL_STATEMENTS + +def direct(query, content=True): + output = None + select = False + query = agent.payloadDirect(query) + + if kb.dbms == "Oracle" and query.startswith("SELECT ") and " FROM " not in query: + query = "%s FROM DUAL" % query + + for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): + for sqlStatement in sqlStatements: + if query.lower().startswith(sqlStatement) and sqlTitle == "SQL SELECT statement": + select = True + break + + if select: + output = conf.dbmsConnector.select(query) + else: + output = conf.dbmsConnector.execute(query) + + if output is None or len(output) == 0: + return None + elif content: + if len(output) == 1: + if len(output[0]) == 1: + return str(list(output)[0][0]) + else: + return list(output) + else: + return output + else: + for line in output: + if line[0] in (1, -1): + return True + else: + return False diff --git a/lib/request/inject.py b/lib/request/inject.py index f34f1a701..61b13b690 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -37,6 +37,7 @@ from lib.core.data import logger from lib.core.data import queries from lib.core.data import temp from lib.request.connect import Connect as Request +from lib.request.direct import direct from lib.core.settings import SQL_STATEMENTS from lib.techniques.inband.union.use import unionUse from lib.techniques.blind.inference import bisection @@ -352,33 +353,7 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, """ if conf.direct: - expression = agent.payloadDirect(expression) - 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": - select = True - break - - if select: - values = conf.dbmsConnector.select(expression) - else: - values = conf.dbmsConnector.execute(expression) - - if values is None or len(values) == 0: - return None - elif len(values) == 1: - if len(values[0]) == 1: - return str(list(values)[0][0]) - else: - return list(values) - else: - return values + return direct(expression) expression = cleanQuery(expression) expression = expandAsteriskForColumns(expression) @@ -418,25 +393,7 @@ def goStacked(expression, silent=False): expression = cleanQuery(expression) if conf.direct: - expression = agent.payloadDirect(expression) - 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": - select = True - break - - if select: - values = conf.dbmsConnector.select(expression) - else: - values = conf.dbmsConnector.execute(expression) - - return None, None + return direct(expression), None debugMsg = "query: %s" % expression logger.debug(debugMsg) diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index 9a4bf9f3e..cfddd0e1d 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -45,15 +45,14 @@ class Connector(GenericConnector): def __init__(self): GenericConnector.__init__(self) - def connect(self, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() + self.checkFileDb() try: #self.connector = pyodbc.connect(driver='{Microsoft Access Driver (*.mdb)}', dbq=self.db, uid='Admin') self.connector = pyodbc.connect('Driver={Microsoft Access Driver (*.mdb)};Dbq=%s;Uid=Admin;Pwd=;' % self.db) + self.connector.timeout = conf.timeout except pyodbc.OperationalError, msg: raise sqlmapConnectionException, msg[1] @@ -63,7 +62,7 @@ class Connector(GenericConnector): def fetchall(self): try: return self.cursor.fetchall() - except pyodbc.OperationalError, msg: + except pyodbc.ProgrammingError, msg: logger.log(8, msg[1]) return None @@ -80,18 +79,5 @@ class Connector(GenericConnector): self.connector.commit() def select(self, query): - try: - self.cursor.execute(query) - return self.cursor.fetchall() - except pyodbc.ProgrammingError, msg: - logger.log(8, msg[1]) - return None - - def setCursor(self): - self.cursor = self.connector.cursor() - - def close(self): - self.cursor.close() - self.connector.close() - self.closed() - + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 23f1c5054..05beb2633 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -140,6 +140,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if conf.dbms in ACCESS_ALIASES: setDbms("Microsoft Access") + if not conf.extensiveFp: return True diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index 7cdc56797..1e0632a34 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -37,20 +37,21 @@ class Connector(GenericConnector): """ Homepage: http://kinterbasdb.sourceforge.net/ User guide: http://kinterbasdb.sourceforge.net/dist_docs/usage.html + Debian package: python-kinterbasdb License: BSD """ def __init__(self): GenericConnector.__init__(self) - def connect(self, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() + if not self.hostname: + self.checkFileDb() + try: - self.connector = kinterbasdb.connect(database=self.db, user=self.user, password=self.password, timeout={'period': conf.timeout}) + self.connector = kinterbasdb.connect(host=self.hostname, database=self.db, user=self.user, password=self.password, timeout={'period': conf.timeout}) except kinterbasdb.OperationalError, msg: raise sqlmapConnectionException, msg[1] @@ -77,13 +78,5 @@ class Connector(GenericConnector): self.connector.commit() def select(self, query): - self.cursor.execute(query) - return self.cursor.fetchall() - - def setCursor(self): - self.cursor = self.connector.cursor() - - def close(self): - self.cursor.close() - self.connector.close() - self.closed() + self.execute(query) + return self.fetchall() diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 1e92f53b5..3b5574e29 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -23,10 +23,12 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ try: + import _mssql import pymssql except ImportError, _: pass +from lib.core.data import conf from lib.core.data import logger from lib.core.exception import sqlmapConnectionException @@ -45,3 +47,38 @@ class Connector(GenericConnector): def __init__(self): GenericConnector.__init__(self) + + def connect(self): + self.initConnection() + + try: + self.connector = pymssql.connect(host="%s:%d" % (self.hostname, self.port), user=self.user, password=self.password, database=self.db, login_timeout=conf.timeout, timeout=conf.timeout) + except pymssql.OperationalError, msg: + raise sqlmapConnectionException, msg + + self.setCursor() + self.connected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except (pymssql.ProgrammingError, pymssql.OperationalError, _mssql.MssqlDatabaseException), msg: + logger.log(8, msg) + return None + + def execute(self, query): + logger.debug(query) + + try: + self.cursor.execute(query) + except (pymssql.OperationalError, pymssql.ProgrammingError), msg: + logger.log(8, msg) + except pymssql.InternalError, msg: + raise sqlmapConnectionException, msg + + def select(self, query): + self.execute(query) + value = self.fetchall() + self.connector.commit() + + return value diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index f6c1f26aa..c3aa004ee 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -61,7 +61,7 @@ class Enumeration(GenericEnumeration): else: dbs = [conf.db] - if kb.unionPosition: + if kb.unionPosition or conf.direct: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db @@ -75,7 +75,7 @@ class Enumeration(GenericEnumeration): if value: kb.data.cachedTables[db] = value - if not kb.data.cachedTables: + if not kb.data.cachedTables and not conf.direct: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 52f51c329..af110d281 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -97,8 +97,13 @@ class Fingerprint(GenericFingerprint): infoMsg = "testing Microsoft SQL Server" logger.info(infoMsg) - payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") - result = Request.queryPage(payload) + # NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not work connecting + # directly to the Microsoft SQL Server database + if conf.direct: + result = True + else: + payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") + result = Request.queryPage(payload) if result: infoMsg = "confirming Microsoft SQL Server" diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 4f2f3f6c9..b5cf472d6 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -47,10 +47,7 @@ class Connector(GenericConnector): def __init__(self): GenericConnector.__init__(self) - def connect(self, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() try: @@ -83,11 +80,3 @@ class Connector(GenericConnector): 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() - self.closed() diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index eca88c568..dc58b2df4 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -154,9 +154,6 @@ class Fingerprint(GenericFingerprint): * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) """ - infoMsg = "testing MySQL" - logger.info(infoMsg) - if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): setDbms("MySQL %s" % kb.dbmsVersion[0]) @@ -168,6 +165,9 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + infoMsg = "testing MySQL" + logger.info(infoMsg) + randInt = str(randomInt(1)) payload = agent.fullPayload(" AND CONNECTION_ID()=CONNECTION_ID()") result = Request.queryPage(payload) diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 51e865c4d..06577673d 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -37,19 +37,13 @@ class Connector(GenericConnector): 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, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() self.__dsn = cx_Oracle.makedsn(self.hostname, self.port, self.db) @@ -87,11 +81,3 @@ class Connector(GenericConnector): 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() - self.closed() diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 644f57a21..f6ee4ff26 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -96,7 +96,7 @@ class Enumeration(GenericEnumeration): else: kb.data.cachedUsersRoles[user] = list(roles) - if not kb.data.cachedUsersRoles: + if not kb.data.cachedUsersRoles and not conf.direct: conditionChar = "=" if conf.user: diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 08a945461..7efc3249c 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -78,9 +78,6 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - logMsg = "testing Oracle" - logger.info(logMsg) - if conf.dbms in ORACLE_ALIASES: setDbms("Oracle") @@ -89,6 +86,9 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + logMsg = "testing Oracle" + logger.info(logMsg) + # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting # directly to the Oracle database if conf.direct: diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index f050be1de..376d1876c 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -46,10 +46,7 @@ class Connector(GenericConnector): def __init__(self): GenericConnector.__init__(self) - def connect(self, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() try: @@ -82,11 +79,3 @@ class Connector(GenericConnector): 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() - self.closed() diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 69e31ba46..1e675d447 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -86,9 +86,6 @@ class Fingerprint(GenericFingerprint): * http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2) """ - infoMsg = "testing PostgreSQL" - logger.info(infoMsg) - if conf.dbms in PGSQL_ALIASES: setDbms("PostgreSQL") @@ -97,6 +94,9 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True + infoMsg = "testing PostgreSQL" + logger.info(infoMsg) + randInt = str(randomInt(1)) payload = agent.fullPayload(" AND %s::int=%s" % (randInt, randInt)) diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index 68b9f2888..9c4d34279 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -39,7 +39,7 @@ class Connector(GenericConnector): User guide: http://docs.python.org/release/2.5/lib/module-sqlite3.html API: http://docs.python.org/library/sqlite3.html Debian package: python-pysqlite2 - License: zlib/libpng + License: MIT Possible connectors: http://wiki.python.org/moin/SQLite """ @@ -47,11 +47,9 @@ class Connector(GenericConnector): def __init__(self): GenericConnector.__init__(self) - def connect(self, reuse=True): - if reuse and self.connector: - return - + def connect(self): self.initConnection() + self.checkFileDb() try: self.connector = sqlite3.connect(database=self.db, timeout=conf.timeout) @@ -75,19 +73,11 @@ class Connector(GenericConnector): self.cursor.execute(query) except sqlite3.OperationalError, msg: logger.log(8, msg[0]) - except sqlite3.Error, msg: + except sqlite3.DatabaseError, msg: raise sqlmapConnectionException, msg[0] self.connector.commit() def select(self, query): - self.cursor.execute(query) - return self.cursor.fetchall() - - def setCursor(self): - self.cursor = self.connector.cursor() - - def close(self): - self.cursor.close() - self.connector.close() - self.closed() + self.execute(query) + return self.fetchall() diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 9f6dc852a..601932bd1 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -22,8 +22,11 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 021101301 USA """ +import os + from lib.core.data import conf from lib.core.data import logger +from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapUndefinedMethod class Connector: @@ -48,12 +51,29 @@ class Connector: logger.info(infoMsg) def closed(self): - self.connector = None - self.cursor = None infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) infoMsg += ":%d closed" % self.port logger.info(infoMsg) + self.connector = None + self.cursor = None + + def setCursor(self): + self.cursor = self.connector.cursor() + + def getCursor(self): + return self.cursor + + def close(self): + self.cursor.close() + self.connector.close() + self.closed() + + def checkFileDb(self): + if not os.path.exists(self.db): + errMsg = "the provided database file '%s' does not exist" % self.db + raise sqlmapFilePathException, errMsg + def connect(self): errMsg = "'connect' method must be defined " errMsg += "into the specific DBMS plugin" @@ -73,16 +93,3 @@ class Connector: errMsg = "'select' method must be defined " errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg - - def setCursor(self): - errMsg = "'setCursor' method must be defined " - errMsg += "into the specific DBMS plugin" - raise sqlmapUndefinedMethod, errMsg - - def getCursor(self): - return self.cursor - - def close(self): - errMsg = "'close' method must be defined " - errMsg += "into the specific DBMS plugin" - raise sqlmapUndefinedMethod, errMsg diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index f563672f2..3a8970a21 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -149,7 +149,7 @@ class Enumeration: if value: kb.data.cachedUsers = value - if not kb.data.cachedUsers: + if not kb.data.cachedUsers and not conf.direct: infoMsg = "fetching number of database users" logger.info(infoMsg) @@ -232,7 +232,7 @@ class Enumeration: else: kb.data.cachedUsersPasswords[user].append(password) - if not kb.data.cachedUsersPasswords: + if not kb.data.cachedUsersPasswords and not conf.direct: if conf.user: if "," in conf.user: users = conf.user.split(",") @@ -464,7 +464,7 @@ class Enumeration: else: kb.data.cachedUsersPrivileges[user] = list(privileges) - if not kb.data.cachedUsersPrivileges: + if not kb.data.cachedUsersPrivileges and not conf.direct: conditionChar = "=" if conf.user: @@ -649,7 +649,7 @@ class Enumeration: if value: kb.data.cachedDbs = value - if not kb.data.cachedDbs: + if not kb.data.cachedDbs and not conf.direct: infoMsg = "fetching number of databases" logger.info(infoMsg) @@ -733,7 +733,7 @@ class Enumeration: else: kb.data.cachedTables[db].append(table) - if not kb.data.cachedTables: + if not kb.data.cachedTables and not conf.direct: if conf.db: if "," in conf.db: dbs = conf.db.split(",") @@ -881,7 +881,7 @@ class Enumeration: table[conf.tbl] = columns kb.data.cachedColumns[conf.db] = table - if not kb.data.cachedColumns: + if not kb.data.cachedColumns and not conf.direct: infoMsg = "fetching number of columns " infoMsg += "for table '%s'" % conf.tbl infoMsg += " on database '%s'" % conf.db @@ -1298,8 +1298,10 @@ class Enumeration: colList = conf.col.split(",") kb.data.cachedColumns[conf.db] = {} kb.data.cachedColumns[conf.db][conf.tbl] = {} + for column in colList: kb.data.cachedColumns[conf.db][conf.tbl][column] = None + elif not kb.data.cachedColumns: if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " @@ -1359,7 +1361,7 @@ class Enumeration: index += 1 - if not kb.data.dumpedTable: + if not kb.data.dumpedTable and not conf.direct: infoMsg = "fetching number of " if conf.col: infoMsg += "columns '%s' " % colString