From 1416cd0d8667cbe28450e8a00841093c343442e2 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 26 Mar 2010 23:23:25 +0000 Subject: [PATCH] Major enhancement to directly connect to the dbms without passing via a sql injection: adapted code accordingly - see #158. This feature relies on python third-party libraries to be able to connect to the database. For the moment it has been implemented for MySQL (with python-mysqldb module) and PostgreSQL (with python-psycopg2 module). Minor layout adjustments. --- lib/controller/action.py | 5 +- lib/controller/controller.py | 15 +++-- lib/controller/handler.py | 32 +++++---- lib/core/agent.py | 22 +++++++ lib/core/common.py | 73 +++++++++++++++++++-- lib/core/convert.py | 5 ++ lib/core/dump.py | 6 +- lib/core/option.py | 46 +++++++------ lib/core/optiondict.py | 1 + lib/core/target.py | 4 ++ lib/parse/cmdline.py | 7 +- lib/request/connect.py | 33 ++++++++++ lib/request/inject.py | 45 +++++++++++++ lib/techniques/inband/union/test.py | 3 + lib/techniques/outband/stacked.py | 3 + plugins/dbms/access/connector.py | 35 ++++++++++ plugins/dbms/firebird/connector.py | 35 ++++++++++ plugins/dbms/mssqlserver/connector.py | 47 ++++++++++++++ plugins/dbms/mysql/connector.py | 89 ++++++++++++++++++++++++++ plugins/dbms/mysql/fingerprint.py | 28 ++++---- plugins/dbms/oracle/connector.py | 35 ++++++++++ plugins/dbms/postgresql/connector.py | 88 +++++++++++++++++++++++++ plugins/dbms/postgresql/filesystem.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 39 +++++------ plugins/dbms/sqlite/connector.py | 35 ++++++++++ plugins/generic/connector.py | 81 +++++++++++++++++++++++ plugins/generic/enumeration.py | 22 +++---- plugins/generic/filesystem.py | 34 +++++----- plugins/generic/misc.py | 5 +- plugins/generic/takeover.py | 12 ++-- sqlmap.conf | 4 ++ xml/queries.xml | 22 +++---- 32 files changed, 791 insertions(+), 122 deletions(-) create mode 100644 plugins/dbms/access/connector.py create mode 100644 plugins/dbms/firebird/connector.py create mode 100644 plugins/dbms/mssqlserver/connector.py create mode 100644 plugins/dbms/mysql/connector.py create mode 100644 plugins/dbms/oracle/connector.py create mode 100644 plugins/dbms/postgresql/connector.py create mode 100644 plugins/dbms/sqlite/connector.py create mode 100644 plugins/generic/connector.py diff --git a/lib/controller/action.py b/lib/controller/action.py index 686399136..d1f7e737f 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -43,7 +43,7 @@ def action(): # First of all we have to identify the back-end database management # system to be able to go ahead with the injection - conf.dbmsHandler = setHandler() + setHandler() if not conf.dbmsHandler: htmlParsed = getHtmlErrorFp() @@ -166,3 +166,6 @@ def action(): # Miscellaneous options if conf.cleanup: conf.dbmsHandler.cleanup() + + if conf.direct: + conf.dbmsConnector.close() diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 17a6be693..436fd773d 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -76,7 +76,7 @@ def __selectInjection(injData): return "Quit" else: - warnMsg = "Invalid choice, retry" + warnMsg = "invalid choice, retry" logger.warn(warnMsg) __selectInjection(injData) @@ -92,6 +92,13 @@ def start(): if not conf.start: return + if conf.direct: + initTargetEnv() + setupTargetEnv() + action() + + return + if conf.url: kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie )) @@ -104,9 +111,9 @@ def start(): infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls) logger.info(infoMsg) - hostCount = 0 - cookieStr = "" - setCookieAsInjectable = True + hostCount = 0 + cookieStr = "" + setCookieAsInjectable = True for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls: try: diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 213a7a8c2..fc4d99657 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -34,12 +34,19 @@ from lib.core.settings import ACCESS_ALIASES from lib.core.settings import FIREBIRD_ALIASES from plugins.dbms.mssqlserver import MSSQLServerMap +from plugins.dbms.mssqlserver.connector import Connector as MSSQLServerConn from plugins.dbms.mysql import MySQLMap +from plugins.dbms.mysql.connector import Connector as MySQLConn from plugins.dbms.oracle import OracleMap +from plugins.dbms.oracle.connector import Connector as OracleConn from plugins.dbms.postgresql import PostgreSQLMap +from plugins.dbms.postgresql.connector import Connector as PostgreSQLConn from plugins.dbms.sqlite import SQLiteMap +from plugins.dbms.sqlite.connector import Connector as SQLiteConn from plugins.dbms.access import AccessMap +from plugins.dbms.access.connector import Connector as AccessConn from plugins.dbms.firebird import FirebirdMap +from plugins.dbms.firebird.connector import Connector as FirebirdConn def setHandler(): """ @@ -50,16 +57,16 @@ def setHandler(): count = 0 dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server", "SQLite", "Microsoft Access", "Firebird" ) dbmsMap = ( - ( MYSQL_ALIASES, MySQLMap ), - ( ORACLE_ALIASES, OracleMap ), - ( PGSQL_ALIASES, PostgreSQLMap ), - ( MSSQL_ALIASES, MSSQLServerMap ), - ( SQLITE_ALIASES, SQLiteMap ), - ( ACCESS_ALIASES, AccessMap ), - ( FIREBIRD_ALIASES, FirebirdMap ), + ( MYSQL_ALIASES, MySQLMap, MySQLConn ), + ( ORACLE_ALIASES, OracleMap, OracleConn ), + ( PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn ), + ( MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn ), + ( SQLITE_ALIASES, SQLiteMap, SQLiteConn ), + ( ACCESS_ALIASES, AccessMap, AccessConn ), + ( FIREBIRD_ALIASES, FirebirdMap, FirebirdConn ), ) - for dbmsAliases, dbmsEntry in dbmsMap: + for dbmsAliases, dbmsMap, dbmsConn in dbmsMap: if conf.dbms and conf.dbms not in dbmsAliases: debugMsg = "skipping test for %s" % dbmsNames[count] logger.debug(debugMsg) @@ -68,12 +75,15 @@ def setHandler(): continue - handler = dbmsEntry() + handler = dbmsMap() + conf.dbmsConnector = dbmsConn() if handler.checkDbms(): if not conf.dbms or conf.dbms in dbmsAliases: kb.dbmsDetected = True - return handler + conf.dbmsHandler = handler - return None + return + else: + conf.dbmsConnector = None diff --git a/lib/core/agent.py b/lib/core/agent.py index 435c58eb6..c1719e099 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -44,12 +44,25 @@ class Agent: temp.start = randomStr(6) temp.stop = randomStr(6) + def payloadDirect(self, query): + if query.startswith(" AND "): + query = query.replace(" AND ", "SELECT ") + elif query.startswith(" UNION ALL "): + query = query.replace(" UNION ALL ", "") + elif query.startswith("; "): + query = query.replace("; ", "") + + return query + def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False, falseCond=False): """ This method replaces the affected parameter with the SQL injection statement to request """ + if conf.direct: + return self.payloadDirect(newValue) + falseValue = "" negValue = "" retValue = "" @@ -83,6 +96,9 @@ class Agent: return retValue def fullPayload(self, query): + if conf.direct: + return self.payloadDirect(query) + query = self.prefixQuery(query) query = self.postfixQuery(query) payload = self.payload(newValue=query) @@ -96,6 +112,9 @@ class Agent: identified as valid """ + if conf.direct: + return self.payloadDirect(string) + query = "" if conf.prefix: @@ -123,6 +142,9 @@ class Agent: SQL injection request """ + if conf.direct: + return self.payloadDirect(string) + randInt = randomInt() randStr = randomStr() diff --git a/lib/core/common.py b/lib/core/common.py index 965172d49..139b9f2b7 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -47,12 +47,21 @@ from lib.core.data import temp from lib.core.convert import urlencode from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapNoneDataException +from lib.core.exception import sqlmapMissingDependence from lib.core.exception import sqlmapSyntaxException from lib.core.settings import DESCRIPTION from lib.core.settings import IS_WIN from lib.core.settings import SITE from lib.core.settings import SQL_STATEMENTS +from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import VERSION_STRING +from lib.core.settings import MSSQL_ALIASES +from lib.core.settings import MYSQL_ALIASES +from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import ORACLE_ALIASES +from lib.core.settings import SQLITE_ALIASES +from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import FIREBIRD_ALIASES def paramToDict(place, parameters=None): """ @@ -319,7 +328,7 @@ def getDirs(webApi=None): [directories.add(directory) for directory in defaultDirs] return directories - + def filePathToString(filePath): strRepl = filePath.replace("/", "_").replace("\\", "_") strRepl = strRepl.replace(" ", "_").replace(":", "_") @@ -329,18 +338,18 @@ def filePathToString(filePath): def dataToStdout(data): sys.stdout.write(data) sys.stdout.flush() - + def dataToSessionFile(data): if not conf.sessionFile: return conf.sessionFP.write(data) conf.sessionFP.flush() - + def dataToDumpFile(dumpFile, data): dumpFile.write(data) dumpFile.flush() - + def dataToOutFile(data): if not data: return "No data retrieved" @@ -586,10 +595,62 @@ def weAreFrozen(): return hasattr(sys, "frozen") +def parseTargetDirect(): + """ + Parse target dbms and set some attributes into the configuration singleton. + """ + + if not conf.direct: + return + + details = None + + for dbms in SUPPORTED_DBMS: + details = re.search("^(%s)://(.+?)\:(.+?)\@(.+?)\:([\d]+)\/(.+?)$" % dbms, conf.direct, re.I) + + if details: + conf.dbms = details.group(1) + conf.dbmsUser = details.group(2) + conf.dbmsPass = details.group(3) + conf.hostname = details.group(4) + conf.port = int(details.group(5)) + conf.dbmsDb = details.group(6) + + 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" + 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, "", ""], + "SQLite": [SQLITE_ALIASES, "", ""], + "Access": [ACCESS_ALIASES, "", ""], + "Firebird": [FIREBIRD_ALIASES, "", ""] } + + for dbmsName, data in dbmsDict.items(): + if conf.dbms in data[0]: + try: + if dbmsName == "Microsoft SQL Server": + import pymssql + elif dbmsName == "MySQL": + import MySQLdb + elif dbmsName == "PostgreSQL": + import psycopg2 + except ImportError, _: + errMsg = "sqlmap requires %s third-party library " % data[1] + errMsg += "in order to directly connect to the database " + errMsg += "%s. Download from %s" % (dbmsName, data[2]) + raise sqlmapMissingDependence, errMsg + def parseTargetUrl(): """ - Parse target url and set some attributes into the configuration - singleton. + Parse target url and set some attributes into the configuration singleton. """ if not conf.url: diff --git a/lib/core/convert.py b/lib/core/convert.py index 8767d084d..1a0c6de28 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -32,6 +32,8 @@ import sys import struct import urllib +from lib.core.data import conf + def base64decode(string): return string.decode("base64") @@ -77,6 +79,9 @@ def urldecode(string): return result def urlencode(string, safe=":/?%&=", convall=False): + if conf.direct: + return string + result = None if string is None: diff --git a/lib/core/dump.py b/lib/core/dump.py index 737550c38..fa7c7d0e4 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -92,7 +92,7 @@ class Dump: if isinstance(element, str): self.__write("[*] %s" % element) elif isinstance(element, (list, tuple, set)): - self.__write("[*] " + ", ".join(e for e in element)) + self.__write("[*] " + ", ".join(str(e) for e in element)) if elements: self.__write("") @@ -321,11 +321,11 @@ class Dump: info = tableValues[column] value = info["values"][i] - if re.search("^[\ *]*$", value): + if re.search("^[\ *]*$", str(value)): value = "NULL" maxlength = int(info["length"]) - blank = " " * (maxlength - len(value)) + blank = " " * (maxlength - len(str(value))) self.__write("| %s%s" % (value, blank), n=False) if not conf.multipleTargets and field == fields: diff --git a/lib/core/option.py b/lib/core/option.py index 2a6ea84cb..ff68f0669 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -37,6 +37,7 @@ from ConfigParser import ConfigParser from lib.core.common import getFileType from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes +from lib.core.common import parseTargetDirect from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomRange @@ -58,6 +59,9 @@ from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import PGSQL_ALIASES from lib.core.settings import ORACLE_ALIASES +from lib.core.settings import SQLITE_ALIASES +from lib.core.settings import ACCESS_ALIASES +from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import IS_WIN from lib.core.settings import PLATFORM from lib.core.settings import SITE @@ -493,7 +497,10 @@ def __setDBMS(): firstRegExp = "(%s|%s|%s|%s)" % ("|".join([alias for alias in MSSQL_ALIASES]), "|".join([alias for alias in MYSQL_ALIASES]), "|".join([alias for alias in PGSQL_ALIASES]), - "|".join([alias for alias in ORACLE_ALIASES])) + "|".join([alias for alias in ORACLE_ALIASES]), + "|".join([alias for alias in SQLITE_ALIASES]), + "|".join([alias for alias in ACCESS_ALIASES]), + "|".join([alias for alias in FIREBIRD_ALIASES])) dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, conf.dbms) if dbmsRegExp: @@ -606,7 +613,7 @@ def __setHTTPAuthentication(): elif aTypeLower == "digest": authHandler = urllib2.HTTPDigestAuthHandler(passwordMgr) - + elif aTypeLower == "ntlm": try: from ntlm import HTTPNtlmAuthHandler @@ -861,6 +868,7 @@ def __setConfAttributes(): logger.debug(debugMsg) conf.cj = None + conf.dbmsConnector = None conf.dbmsHandler = None conf.dumpPath = None conf.httpHeaders = [] @@ -1045,28 +1053,30 @@ def init(inputOptions=advancedDict()): __setConfAttributes() __setKnowledgeBaseAttributes() __cleanupOptions() - __setRequestFromFile() - - parseTargetUrl() - __setHTTPTimeout() - __setHTTPCookies() - __setHTTPReferer() - __setHTTPUserAgent() - __setHTTPExtraHeaders() - __setHTTPMethod() - __setHTTPAuthentication() - __setHTTPProxy() + parseTargetUrl() + parseTargetDirect() + + if conf.url or conf.list or conf.requestFile or conf.googleDork: + __setHTTPTimeout() + __setHTTPCookies() + __setHTTPReferer() + __setHTTPUserAgent() + __setHTTPExtraHeaders() + __setHTTPMethod() + __setHTTPAuthentication() + __setHTTPProxy() + __setUnionTech() + __setGoogleDorking() + __setMultipleTargets() + __urllib2Opener() + __setDBMS() + __setThreads() - __setDBMS() __setOS() - __setUnionTech() __setWriteFile() __setMetasploit() - __setGoogleDorking() - __setMultipleTargets() - __urllib2Opener() update() queriesParser() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 412b5e740..1a8ea462b 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -25,6 +25,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA optDict = { # Family: { "parameter_name": "parameter_datatype" }, "Target": { + "direct": "string", "url": "string", "list": "string", "requestFile": "string", diff --git a/lib/core/target.py b/lib/core/target.py index e52c8e75d..dd49389b1 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -43,6 +43,10 @@ def __setRequestParams(): HTTP method POST. """ + if conf.direct: + conf.parameters[None] = "direct connection" + return + __testableParameters = False # Perform checks on GET parameters diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index af0643603..f26fddf8b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -48,6 +48,9 @@ def cmdLineParser(): "options has to be specified to set the source " "to get target urls from.") + target.add_option("-d", dest="direct", help="Direct " + "connection to the database") + target.add_option("-u", "--url", dest="url", help="Target url") target.add_option("-l", dest="list", help="Parse targets from Burp " @@ -437,8 +440,8 @@ def cmdLineParser(): (args, _) = parser.parse_args() - if not args.url and not args.list and not args.googleDork and not args.configFile and not args.requestFile and not args.updateAll: - errMsg = "missing a mandatory parameter ('-u', '-l', '-r', '-g', '-c' or '--update'), " + if not args.direct and not args.url and not args.list and not args.googleDork and not args.configFile and not args.requestFile and not args.updateAll: + errMsg = "missing a mandatory parameter ('-d', '-u', '-l', '-r', '-g', '-c' or '--update'), " errMsg += "-h for help" parser.error(errMsg) diff --git a/lib/request/connect.py b/lib/request/connect.py index 558e68c6f..54eae7bf3 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -38,6 +38,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.common import sanitizeAsciiString from lib.core.exception import sqlmapConnectionException +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 @@ -263,6 +264,38 @@ class Connect: string match check ('--string' command line parameter) """ + if conf.direct: + values = None + select = False + + 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] == 1: + return True + else: + return False + get = None post = None cookie = None diff --git a/lib/request/inject.py b/lib/request/inject.py index 81c0962e0..497e975b0 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.core.settings import SQL_STATEMENTS from lib.techniques.inband.union.use import unionUse from lib.techniques.blind.inference import bisection from lib.utils.resume import queryOutputLength @@ -350,6 +351,32 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, (if selected). """ + if conf.direct: + expression = agent.payloadDirect(expression) + values = None + select = False + + 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 + expression = cleanQuery(expression) expression = expandAsteriskForColumns(expression) value = None @@ -387,6 +414,24 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, def goStacked(expression, silent=False): expression = cleanQuery(expression) + if conf.direct: + expression = agent.payloadDirect(expression) + values = None + select = False + + 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 + debugMsg = "query: %s" % expression logger.debug(debugMsg) diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index b45b09114..e36b10204 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -200,6 +200,9 @@ def unionTest(): SQL injection vulnerability. The test is done up to 3*50 times """ + if conf.direct: + return + if conf.uTech == "orderby": technique = "ORDER BY clause bruteforcing" else: diff --git a/lib/techniques/outband/stacked.py b/lib/techniques/outband/stacked.py index 7b8c0330a..5f8afbd2f 100644 --- a/lib/techniques/outband/stacked.py +++ b/lib/techniques/outband/stacked.py @@ -32,6 +32,9 @@ from lib.core.session import setStacked from lib.request import inject def stackedTest(): + if conf.direct: + return + if kb.stackedTest is not None: return kb.stackedTest diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py new file mode 100644 index 000000000..e33d9226d --- /dev/null +++ b/plugins/dbms/access/connector.py @@ -0,0 +1,35 @@ +#!/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 plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: + User guide: + API: + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py new file mode 100644 index 000000000..e33d9226d --- /dev/null +++ b/plugins/dbms/firebird/connector.py @@ -0,0 +1,35 @@ +#!/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 plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: + User guide: + API: + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py new file mode 100644 index 000000000..1e92f53b5 --- /dev/null +++ b/plugins/dbms/mssqlserver/connector.py @@ -0,0 +1,47 @@ +#!/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 +""" + +try: + import pymssql +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: http://pymssql.sourceforge.net/ + User guide: http://pymssql.sourceforge.net/examples_pymssql.php + API: http://pymssql.sourceforge.net/ref_pymssql.php + Debian package: python-pymssql + License: LGPL + + Possible connectors: http://wiki.python.org/moin/SQL%20Server + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py new file mode 100644 index 000000000..2e023b163 --- /dev/null +++ b/plugins/dbms/mysql/connector.py @@ -0,0 +1,89 @@ +#!/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 +""" + +try: + import MySQLdb +except ImportError, _: + pass + +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import sqlmapConnectionException + +from plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: http://mysql-python.sourceforge.net/ + User guide: http://mysql-python.sourceforge.net/MySQLdb.html + API: http://mysql-python.sourceforge.net/MySQLdb-1.2.2/ + Debian package: python-mysqldb + License: GPL + + Possible connectors: http://wiki.python.org/moin/MySQL + """ + + def __init__(self): + GenericConnector.__init__(self) + + def connect(self): + self.initConnection() + + try: + self.connector = MySQLdb.connect(host=self.hostname, user=self.user, passwd=self.password, db=self.db, port=self.port, connect_timeout=conf.timeout) + except MySQLdb.OperationalError, msg: + raise sqlmapConnectionException, msg[1] + + self.setCursor() + self.connected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except MySQLdb.ProgrammingError, msg: + logger.log(8, msg[1]) + return None + + def execute(self, query): + logger.debug(query) + + try: + self.cursor.execute(query) + except (MySQLdb.OperationalError, MySQLdb.ProgrammingError), msg: + logger.log(8, msg[1]) + except MySQLdb.InternalError, msg: + raise sqlmapConnectionException, msg[1] + + 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/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 43a9fd169..7a4cc57da 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -154,6 +154,12 @@ 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.direct: + conf.dbmsConnector.connect() + if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): setDbms("MySQL %s" % kb.dbmsVersion[0]) @@ -165,11 +171,7 @@ 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) @@ -203,7 +205,7 @@ class Fingerprint(GenericFingerprint): kb.dbmsVersion = [">= 5.5.0"] # Check if it is MySQL >= 5.1.2 and < 5.5.0 - elif inject.getValue("MID(@@table_open_cache, 1, 1)", unpack=False): + elif inject.getValue("SELECT MID(@@table_open_cache, 1, 1)", unpack=False): if inject.getValue("SELECT %s FROM information_schema.GLOBAL_STATUS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: kb.dbmsVersion = [">= 5.1.12", "< 5.5.0"] elif inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: @@ -216,11 +218,11 @@ class Fingerprint(GenericFingerprint): kb.dbmsVersion = [">= 5.1.2", "< 5.1.5"] # Check if it is MySQL >= 5.0.0 and < 5.1.2 - elif inject.getValue("MID(@@hostname, 1, 1)", unpack=False): + elif inject.getValue("SELECT MID(@@hostname, 1, 1)", unpack=False): kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"] elif inject.getValue("SELECT 1 FROM DUAL", charsetType=1) == "1": kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"] - elif inject.getValue("DATABASE() LIKE SCHEMA()"): + elif inject.getValue("SELECT DATABASE() LIKE SCHEMA()"): kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"] else: kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"] @@ -237,24 +239,24 @@ class Fingerprint(GenericFingerprint): return True # Check which version of MySQL < 5.0.0 it is - coercibility = inject.getValue("COERCIBILITY(USER())") + coercibility = inject.getValue("SELECT COERCIBILITY(USER())") if coercibility == "3": kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"] elif coercibility == "2": kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"] - elif inject.getValue("CURRENT_USER()"): + elif inject.getValue("SELECT CURRENT_USER()"): kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"] - if inject.getValue("CHARSET(CURRENT_USER())") == "utf8": + if inject.getValue("SELECT CHARSET(CURRENT_USER())") == "utf8": kb.dbmsVersion = ["= 4.1.0"] else: kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"] - elif inject.getValue("FOUND_ROWS()", charsetType=1) == "0": + elif inject.getValue("SELECT FOUND_ROWS()", charsetType=1) == "0": kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] - elif inject.getValue("CONNECTION_ID()"): + elif inject.getValue("SELECT CONNECTION_ID()"): kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"] - elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")): + elif re.search("@[\w\.\-\_]+", inject.getValue("SELECT USER()")): kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"] else: kb.dbmsVersion = ["< 3.22.11"] diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py new file mode 100644 index 000000000..e33d9226d --- /dev/null +++ b/plugins/dbms/oracle/connector.py @@ -0,0 +1,35 @@ +#!/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 plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: + User guide: + API: + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py new file mode 100644 index 000000000..68555525f --- /dev/null +++ b/plugins/dbms/postgresql/connector.py @@ -0,0 +1,88 @@ +#!/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 +""" + +try: + import psycopg2 +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: http://initd.org/psycopg/ + User guide: http://initd.org/psycopg/docs/ + API: http://initd.org/psycopg/docs/genindex.html + Debian package: python-psycopg2 + License: GPL + + Possible connectors: http://wiki.python.org/moin/PostgreSQL + """ + + def __init__(self): + GenericConnector.__init__(self) + + def connect(self): + self.initConnection() + + try: + self.connector = psycopg2.connect(host=self.hostname, user=self.user, password=self.password, database=self.db, port=self.port) + except psycopg2.OperationalError, msg: + raise sqlmapConnectionException, msg + + self.setCursor() + self.connected() + + def fetchall(self): + try: + return self.cursor.fetchall() + except psycopg2.ProgrammingError, msg: + logger.log(8, msg) + return None + + def execute(self, query): + logger.debug(query) + + try: + self.cursor.execute(query) + except (psycopg2.OperationalError, psycopg2.ProgrammingError), msg: + logger.log(8, msg) + except psycopg2.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/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 5abb43c3a..ea102d68e 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -125,4 +125,4 @@ class Filesystem(GenericFilesystem): if confirm: self.askCheckWrittenFile(wFile, dFile, fileType) - inject.goStacked("SELECT lo_unlink(%d)" % self.oid) \ No newline at end of file + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 079afe64f..a8ebe2303 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -86,6 +86,12 @@ 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.direct: + conf.dbmsConnector.connect() + if conf.dbms in PGSQL_ALIASES: setDbms("PostgreSQL") @@ -94,9 +100,6 @@ 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)) @@ -122,33 +125,33 @@ class Fingerprint(GenericFingerprint): if not conf.extensiveFp: return True - if inject.getValue("DIV(6, 3)", unpack=False, charsetType=2) == "2": + if inject.getValue("SELECT DIV(6, 3)", unpack=False, charsetType=2) == "2": kb.dbmsVersion = [">= 8.4.0"] - elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ) and not inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ): + elif inject.getValue("SELECT SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ) and not inject.getValue("SELECT SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ): kb.dbmsVersion = [">= 8.3.0", "< 8.4"] - elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2): + elif inject.getValue("SELECT SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2): kb.dbmsVersion = [">= 8.2.0", "< 8.3.0"] - elif inject.getValue("GREATEST(5, 9, 1)", unpack=False, charsetType=2) == "9": + elif inject.getValue("SELECT GREATEST(5, 9, 1)", unpack=False, charsetType=2) == "9": kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"] - elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)", unpack=False, charsetType=2) == "3": + elif inject.getValue("SELECT WIDTH_BUCKET(5.35, 0.024, 10.06, 5)", unpack=False, charsetType=2) == "3": kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"] - elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)", unpack=False): + elif inject.getValue("SELECT SUBSTR(MD5('sqlmap'), 1, 1)", unpack=False): kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"] - elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)", unpack=False) == "p": + elif inject.getValue("SELECT SUBSTR(CURRENT_SCHEMA(), 1, 1)", unpack=False) == "p": kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"] - elif inject.getValue("BIT_LENGTH(1)") == "8": + elif inject.getValue("SELECT BIT_LENGTH(1)") == "8": kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"] - elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)", unpack=False) == "a": + elif inject.getValue("SELECT SUBSTR(QUOTE_LITERAL('a'), 2, 1)", unpack=False) == "a": kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"] - elif inject.getValue("POW(2, 3)", unpack=False, charsetType=2) == "8": + elif inject.getValue("SELECT POW(2, 3)", unpack=False, charsetType=2) == "8": kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"] - elif inject.getValue("MAX('a')") == "a": + elif inject.getValue("SELECT MAX('a')") == "a": kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"] - elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)", unpack=False)): + elif re.search("([\d\.]+)", inject.getValue("SELECT SUBSTR(VERSION(), 12, 5)", unpack=False)): kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"] - elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)", unpack=False, charsetType=2) == "2": + elif inject.getValue("SELECT SUBSTR(CURRENT_DATE, 1, 1)", unpack=False, charsetType=2) == "2": kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"] - elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s": + elif inject.getValue("SELECT SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s": kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"] else: kb.dbmsVersion = ["< 6.2.0"] @@ -167,7 +170,7 @@ class Fingerprint(GenericFingerprint): infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)") + self.createSupportTbl(self.fileTblName, self.tblField, "character(10000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) # Windows executables should always have ' Visual C++' or ' mingw' diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py new file mode 100644 index 000000000..e33d9226d --- /dev/null +++ b/plugins/dbms/sqlite/connector.py @@ -0,0 +1,35 @@ +#!/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 plugins.generic.connector import Connector as GenericConnector + +class Connector(GenericConnector): + """ + Homepage: + User guide: + API: + """ + + def __init__(self): + GenericConnector.__init__(self) diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py new file mode 100644 index 000000000..a905d7ead --- /dev/null +++ b/plugins/generic/connector.py @@ -0,0 +1,81 @@ +#!/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 021101301 USA +""" + +from lib.core.data import conf +from lib.core.data import logger +from lib.core.exception import sqlmapUndefinedMethod + +class Connector: + """ + This class defines generic dbms protocol functionalities for plugins. + """ + + def __init__(self): + self.connector = None + self.cursor = None + + def initConnection(self): + self.user = conf.dbmsUser + self.password = conf.dbmsPass + self.hostname = conf.hostname + self.port = conf.port + self.db = conf.dbmsDb + + def connected(self): + infoMsg = "connection to %s server %s" % (conf.dbms, self.hostname) + infoMsg += ":%d established" % self.port + logger.info(infoMsg) + + def connect(self): + errMsg = "'connect' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def fetchall(self): + errMsg = "'fetchall' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def execute(self, query): + errMsg = "'execute' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def select(self, query): + 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 547516be1..f563672f2 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -139,7 +139,7 @@ class Enumeration: condition = ( kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ) ) condition |= ( kb.dbms == "MySQL" and not kb.data.has_information_schema ) - if kb.unionPosition: + if kb.unionPosition or conf.direct: if condition: query = rootQuery["inband"]["query2"] else: @@ -196,7 +196,7 @@ class Enumeration: logger.info(infoMsg) - if kb.unionPosition: + if kb.unionPosition or conf.direct: if kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): query = rootQuery["inband"]["query2"] else: @@ -393,7 +393,7 @@ class Enumeration: "E": "EXECUTE" } - if kb.unionPosition: + if kb.unionPosition or conf.direct: if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["inband"]["query2"] condition = rootQuery["inband"]["condition2"] @@ -439,7 +439,7 @@ class Enumeration: # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise - if kb.dbms == "PostgreSQL" and privilege.isdigit(): + if kb.dbms == "PostgreSQL" and str(privilege).isdigit(): for position, pgsqlPriv in pgsqlPrivs: if count == position and int(privilege) == 1: privileges.add(pgsqlPriv) @@ -639,7 +639,7 @@ class Enumeration: rootQuery = queries[kb.dbms].dbs - if kb.unionPosition: + if kb.unionPosition or conf.direct: if kb.dbms == "MySQL" and not kb.data.has_information_schema: query = rootQuery["inband"]["query2"] else: @@ -696,7 +696,7 @@ class Enumeration: rootQuery = queries[kb.dbms].tables - if kb.unionPosition: + if kb.unionPosition or conf.direct: query = rootQuery["inband"]["query"] condition = rootQuery["inband"]["condition"] @@ -855,7 +855,7 @@ class Enumeration: infoMsg += "on database '%s'" % conf.db logger.info(infoMsg) - if kb.unionPosition: + if kb.unionPosition or conf.direct: if kb.dbms in ( "MySQL", "PostgreSQL" ): query = rootQuery["inband"]["query"] % (conf.tbl, conf.db) query += condQuery @@ -1039,7 +1039,7 @@ class Enumeration: colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % column - if kb.unionPosition: + if kb.unionPosition or conf.direct: query = rootQuery["inband"]["query"] query += colQuery query += dbsQuery @@ -1095,7 +1095,7 @@ class Enumeration: infoMsg += " '%s' in database '%s'" % (column, db) logger.info(infoMsg) - if kb.unionPosition: + if kb.unionPosition or conf.direct: query = rootQuery["inband"]["query2"] if kb.dbms in ( "MySQL", "PostgreSQL" ): @@ -1321,7 +1321,7 @@ class Enumeration: entriesCount = 0 - if kb.unionPosition: + if kb.unionPosition or conf.direct: if kb.dbms == "Oracle": query = rootQuery["inband"]["query"] % (colString, conf.tbl.upper()) elif kb.dbms == "SQLite": @@ -1349,7 +1349,7 @@ class Enumeration: else: colEntry = entry[index] - colEntryLen = len(colEntry) + colEntryLen = len(str(colEntry)) maxLen = max(colLen, colEntryLen) if maxLen > kb.data.dumpedTable[column]["length"]: diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 5e020b388..f4f831bde 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -29,13 +29,13 @@ from lib.core.agent import agent from lib.core.common import dataToOutFile from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import sqlmapUndefinedMethod from lib.request import inject from lib.techniques.outband.stacked import stackedTest - class Filesystem: """ This class defines generic OS file system functionalities for plugins. @@ -278,18 +278,19 @@ class Filesystem: self.checkDbmsOs() - if not kb.stackedTest: + if conf.direct or kb.stackedTest: + if kb.stackedTest: + debugMsg = "going to read the file with stacked query SQL " + debugMsg += "injection technique" + logger.debug(debugMsg) + + fileContent = self.stackedReadFile(rFile) + else: debugMsg = "going to read the file with UNION query SQL " debugMsg += "injection technique" logger.debug(debugMsg) fileContent = self.unionReadFile(rFile) - else: - debugMsg = "going to read the file with stacked query SQL " - debugMsg += "injection technique" - logger.debug(debugMsg) - - fileContent = self.stackedReadFile(rFile) if fileContent in ( None, "" ) and kb.dbms != "PostgreSQL": self.cleanup(onlyFileTbl=True) @@ -319,16 +320,17 @@ class Filesystem: self.checkDbmsOs() - if not kb.stackedTest: + if conf.direct or kb.stackedTest: + if kb.stackedTest: + debugMsg = "going to upload the %s file with " % fileType + debugMsg += "stacked query SQL injection technique" + logger.debug(debugMsg) + + self.stackedWriteFile(wFile, dFile, fileType, confirm) + self.cleanup(onlyFileTbl=True) + else: debugMsg = "going to upload the %s file with " % fileType debugMsg += "UNION query SQL injection technique" logger.debug(debugMsg) self.unionWriteFile(wFile, dFile, fileType, confirm) - else: - debugMsg = "going to upload the %s file with " % fileType - debugMsg += "stacked query SQL injection technique" - logger.debug(debugMsg) - - self.stackedWriteFile(wFile, dFile, fileType, confirm) - self.cleanup(onlyFileTbl=True) diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 8d265e5f5..5ef82e0be 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -98,6 +98,9 @@ class Miscellaneous: query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last) + if conf.direct: + query = "SELECT %s" % query + kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "") @@ -127,7 +130,7 @@ class Miscellaneous: stackedTest() - if not kb.stackedTest: + if not kb.stackedTest and not conf.direct: return if kb.os == "Windows": diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 36d662e72..8f2541aed 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -51,7 +51,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): def osCmd(self): stackedTest() - if kb.stackedTest: + if kb.stackedTest or conf.direct: web = False elif not kb.stackedTest and kb.dbms == "MySQL": infoMsg = "going to use a web backdoor for command execution" @@ -74,7 +74,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): def osShell(self): stackedTest() - if kb.stackedTest: + if kb.stackedTest or conf.direct: web = False elif not kb.stackedTest and kb.dbms == "MySQL": infoMsg = "going to use a web backdoor for command prompt" @@ -99,7 +99,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): stackedTest() - if kb.stackedTest: + if kb.stackedTest or conf.direct: web = False self.initEnv(web=web) @@ -195,7 +195,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): errMsg += "relay attack" raise sqlmapUnsupportedDBMSException(errMsg) - if not kb.stackedTest: + if not kb.stackedTest and not conf.direct: if kb.dbms in ( "PostgreSQL", "Microsoft SQL Server" ): errMsg = "on this back-end DBMS it is only possible to " errMsg += "perform the SMB relay attack if stacked " @@ -239,7 +239,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): def osBof(self): stackedTest() - if not kb.stackedTest: + if not kb.stackedTest and not conf.direct: return if not kb.dbms == "Microsoft SQL Server" or kb.dbmsVersion[0] not in ( "2000", "2005" ): @@ -267,7 +267,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): def __regInit(self): stackedTest() - if not kb.stackedTest: + if not kb.stackedTest and not conf.direct: return self.checkDbmsOs() diff --git a/sqlmap.conf b/sqlmap.conf index 09c38a99d..0ccc6aa19 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -2,6 +2,10 @@ # get target urls from. [Target] +# Direct connection to the database. +# Example: mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME +direct = + # Target URL. # Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 url = diff --git a/xml/queries.xml b/xml/queries.xml index 9a7b25352..68825a450 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -25,9 +25,9 @@ - - - + + + @@ -160,9 +160,9 @@ - - - + + + @@ -218,9 +218,9 @@ - - - + + + @@ -276,7 +276,7 @@ - + @@ -316,7 +316,7 @@ - +