diff --git a/lib/core/common.py b/lib/core/common.py index d6258e22f..52c4541eb 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -82,6 +82,9 @@ from lib.core.exception import SqlmapUserQuitException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.settings import BOLD_PATTERNS +from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES +from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES +from lib.core.settings import BRUTE_DOC_ROOT_TARGET_MARK from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DBMS_DIRECTORY_DICT from lib.core.settings import DEFAULT_COOKIE_DELIMITER @@ -99,6 +102,7 @@ from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INVALID_UNICODE_CHAR_FORMAT +from lib.core.settings import IP_ADDRESS_REGEX from lib.core.settings import ISSUES_PAGE from lib.core.settings import IS_WIN from lib.core.settings import LARGE_OUTPUT_THRESHOLD @@ -644,38 +648,48 @@ def getDocRoot(): docRoot = [] - message = "do you want to provide a text file with a list of " - message += "directories to try? [y/N] " - answer = readInput(message, default="N") + message = "what do you want to use for web server document root?\n" + message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot) + message += "[2] custom location\n" + message += "[3] custom directory list file\n" + message += "[4] brute force search\n" + choice = readInput(message, default="1").strip() - if answer and answer.lower() == "y": - message = "please provide the directories list file to try: " - dirFilePath = readInput(message) + if choice == "2": + message = "please provide the web server document root: " + docRoot = readInput(message).split(',') + elif choice == "3": + message = "what's the list file location?\n" + listPath = readInput(message) + checkFile(listPath) + docRoot = getFileItems(listPath) + elif choice == "4": + targets = set([conf.hostname]) + _ = conf.hostname.split('.') - if dirFilePath: - if os.path.isfile(dirFilePath): - fd = codecs.open(dirFilePath, "rb", UNICODE_ENCODING) - - for filepath in fd.readlines(): - docRoot.append(normalizePath(filepath)) - - else: - errMsg = "provided directory list file %s " % dirFilePath - errMsg += "is not a valid file" - logger.error(errMsg) - - if len(docRoot) == 0: - message = "please provide the web server document root " - message += "[%s]: " % ", ".join(root for root in defaultDocRoot) - inputDocRoot = readInput(message, default=defaultDocRoot) - - if inputDocRoot: - if isinstance(inputDocRoot, basestring): - docRoot = inputDocRoot.split(',') - else: - docRoot = inputDocRoot + if _[0] == "www": + targets.add('.'.join(_[1:])) + targets.add('.'.join(_[1:-1])) else: - docRoot = defaultDocRoot + targets.add('.'.join(_[:-1])) + + targets = filter(None, targets) + + for prefix in BRUTE_DOC_ROOT_PREFIXES.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]): + if BRUTE_DOC_ROOT_TARGET_MARK in prefix and re.match(IP_ADDRESS_REGEX, conf.hostname): + continue + + for suffix in BRUTE_DOC_ROOT_SUFFIXES: + for target in targets: + item = "%s/%s" % (prefix, suffix) + item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", "/") + docRoot.append(item) + + if BRUTE_DOC_ROOT_TARGET_MARK not in prefix: + break + + else: + docRoot = defaultDocRoot return docRoot @@ -700,19 +714,6 @@ def getDirs(): if webDir: directories.add(webDir) - message = "please provide additional comma separated file paths to " - message += "try to upload the agent inside the possible document: " - message += "root%s [Enter for None]: " % "s" if len(kb.docRoot) > 1 else "" - inputDirs = readInput(message) - - if inputDirs: - inputDirs = inputDirs.replace(", ", ",") - inputDirs = inputDirs.split(",") - - for inputDir in inputDirs: - if inputDir: - directories.add(inputDir) - return list(directories) def filePathToSafeString(filePath): diff --git a/lib/core/settings.py b/lib/core/settings.py index 8dd2cbafb..949ce3b47 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -13,6 +13,7 @@ import sys from lib.core.enums import DBMS from lib.core.enums import DBMS_DIRECTORY_NAME +from lib.core.enums import OS from lib.core.revision import getRevisionNumber # sqlmap version and site @@ -58,6 +59,9 @@ GOOGLE_REGEX = r"url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&( # Regular expression used for extracting content from "textual" tags TEXT_TAG_REGEX = r"(?si)<(abbr|acronym|b|blockquote|br|center|cite|code|dt|em|font|h\d|i|li|p|pre|q|strong|sub|sup|td|th|title|tt|u)(?!\w).*?>(?P[^<]+)" +# Regular expression used for recognition of IP addresses +IP_ADDRESS_REGEX = r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" + # Dumping characters used in GROUP_CONCAT MySQL technique CONCAT_ROW_DELIMITER = ',' CONCAT_VALUE_DELIMITER = '|' @@ -547,6 +551,18 @@ METASPLOIT_SESSION_TIMEOUT = 180 # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." +# Prefixes used in brute force search for web server document root +BRUTE_DOC_ROOT_PREFIXES = { + OS.LINUX: ("/var/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), + OS.WINDOWS: ("/xampp", "/Program Files/xampp/", "/wamp", "/Program Files/wampp/", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") +} + +# Suffixes used in brute force search for web server document root +BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "sites/all", "www/build") + +# String used for marking target name inside used brute force web server document root +BRUTE_DOC_ROOT_TARGET_MARK = "%TARGET%" + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """