From 0e9dd9b0bed0eaa6ef81c7ba5eb83ed232e0a8a7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Jan 2020 23:49:45 +0100 Subject: [PATCH] Some testing stuff --- extra/vulnserver/vulnserver.py | 2 +- lib/core/optiondict.py | 2 - lib/core/settings.py | 2 +- lib/core/testing.py | 185 +++++++++++---------------------- lib/parse/cmdline.py | 7 +- sqlmap.py | 3 + 6 files changed, 66 insertions(+), 135 deletions(-) diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py index d14dbc94a..4c6d2f4f5 100644 --- a/extra/vulnserver/vulnserver.py +++ b/extra/vulnserver/vulnserver.py @@ -191,7 +191,7 @@ class ReqHandler(BaseHTTPRequestHandler): length = int(self.headers.get("Content-length", 0)) if length: data = self.rfile.read(length) - data = unquote_plus(data.decode(UNICODE_ENCODING)) + data = unquote_plus(data.decode(UNICODE_ENCODING, "ignore")) self.data = data self.do_REQUEST() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 7273718e6..472690076 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -252,8 +252,6 @@ optDict = { "forceDns": "boolean", "murphyRate": "integer", "smokeTest": "boolean", - "stopFail": "boolean", - "runCase": "string", }, "API": { diff --git a/lib/core/settings.py b/lib/core/settings.py index 4dc09a3e5..b27db5e65 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -18,7 +18,7 @@ from lib.core.enums import OS from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.4.1.11" +VERSION = "1.4.1.12" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/core/testing.py b/lib/core/testing.py index 85fd6af79..730de1fa6 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -7,51 +7,33 @@ See the file 'LICENSE' for copying permission from __future__ import division -import codecs import doctest import logging import os import random import re -import shutil import socket import sqlite3 import sys import tempfile import threading import time -import traceback from extra.vulnserver import vulnserver -from lib.controller.controller import start from lib.core.common import clearColors from lib.core.common import clearConsoleLine from lib.core.common import dataToStdout +from lib.core.common import randomInt +from lib.core.common import randomStr from lib.core.common import shellExec from lib.core.compat import round from lib.core.compat import xrange from lib.core.convert import encodeBase64 -from lib.core.convert import getUnicode -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.data import queries -from lib.core.enums import MKSTEMP_PREFIX -from lib.core.exception import SqlmapBaseException -from lib.core.log import LOGGER_HANDLER -from lib.core.option import init -from lib.core.option import initOptions -from lib.core.optiondict import optDict -from lib.core.settings import UNICODE_ENCODING -from lib.parse.cmdline import cmdLineParser -class Failures(object): - failedItems = None - failedParseOn = None - failedTraceBack = None - -_failures = Failures() _rand = 0 def vulnTest(): @@ -154,6 +136,63 @@ def vulnTest(): return retVal +def fuzzTest(): + count = 0 + address, port = "127.0.0.10", random.randint(1025, 65535) + + def _thread(): + vulnserver.init(quiet=True) + vulnserver.run(address=address, port=port) + + thread = threading.Thread(target=_thread) + thread.daemon = True + thread.start() + + while True: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect((address, port)) + break + except: + time.sleep(1) + + handle, config = tempfile.mkstemp(suffix=".conf") + os.close(handle) + + url = "http://%s:%d/?id=1" % (address, port) + + content = open(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.conf"))).read().replace("url =", "url = %s" % url) + open(config, "w+").write(content) + + while True: + lines = content.split("\n") + + for i in xrange(20): + j = random.randint(0, len(lines) - 1) + if lines[j].strip().endswith('='): + lines[j] += random.sample(("True", "False", randomStr(), str(randomInt())), 1)[0] + + k = random.randint(0, len(lines) - 1) + if '=' in lines[k]: + lines[k] += chr(random.randint(0, 255)) + + open(config, "w+").write("\n".join(lines)) + + cmd = "%s %s -c %s --batch --flush-session --technique=%s --banner" % (sys.executable, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "sqlmap.py")), config, random.sample("BEUQ", 1)[0]) + output = shellExec(cmd) + + if "Traceback" in output: + dataToStdout("---\n\n$ %s\n" % cmd) + dataToStdout("%s---\n" % clearColors(output)) + else: + handle, config = tempfile.mkstemp(prefix="sqlmapcrash", suffix=".conf") + os.close(handle) + open(config, "w+").write("\n".join(lines)) + + dataToStdout("\r%d\r" % count) + + count += 1 + def dirtyPatchRandom(): """ Unifying random generated data across different Python versions @@ -274,109 +313,3 @@ def smokeTest(): logger.error("smoke test final result: FAILED") return retVal - -def adjustValueType(tagName, value): - for family in optDict: - for name, type_ in optDict[family].items(): - if type(type_) == tuple: - type_ = type_[0] - if tagName == name: - if type_ == "boolean": - value = (value == "True") - elif type_ == "integer": - value = int(value) - elif type_ == "float": - value = float(value) - break - return value - -def initCase(switches, count): - _failures.failedItems = [] - _failures.failedParseOn = None - _failures.failedTraceBack = None - - paths.SQLMAP_OUTPUT_PATH = tempfile.mkdtemp(prefix="%s%d-" % (MKSTEMP_PREFIX.TESTING, count)) - paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") - paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") - - logger.debug("using output directory '%s' for this test case" % paths.SQLMAP_OUTPUT_PATH) - - LOGGER_HANDLER.stream = sys.stdout = tempfile.SpooledTemporaryFile(max_size=0, mode="w+b", prefix="sqlmapstdout-") - - cmdLineOptions = cmdLineParser() - - if switches: - for key, value in switches.items(): - if key in cmdLineOptions.__dict__: - cmdLineOptions.__dict__[key] = value - - initOptions(cmdLineOptions, True) - init() - -def cleanCase(): - shutil.rmtree(paths.SQLMAP_OUTPUT_PATH, True) - -def runCase(parse): - retVal = True - handled_exception = None - unhandled_exception = None - result = False - console = "" - - try: - result = start() - except KeyboardInterrupt: - pass - except SqlmapBaseException as ex: - handled_exception = ex - except Exception as ex: - unhandled_exception = ex - finally: - sys.stdout.seek(0) - console = sys.stdout.read() - LOGGER_HANDLER.stream = sys.stdout = sys.__stdout__ - - if unhandled_exception: - _failures.failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) - retVal = None - elif handled_exception: - _failures.failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) - retVal = None - elif result is False: # this means no SQL injection has been detected - if None, ignore - retVal = False - - console = getUnicode(console, encoding=sys.stdin.encoding) - - if parse and retVal: - with codecs.open(conf.dumper.getOutputFile(), "rb", UNICODE_ENCODING) as f: - content = f.read() - - for item, parse_from_console_output in parse: - parse_on = console if parse_from_console_output else content - - if item.startswith("r'") and item.endswith("'"): - if not re.search(item[2:-1], parse_on, re.DOTALL): - retVal = None - _failures.failedItems.append(item) - - elif item not in parse_on: - retVal = None - _failures.failedItems.append(item) - - if _failures.failedItems: - _failures.failedParseOn = console - - elif retVal is False: - _failures.failedParseOn = console - - return retVal - -def replaceVars(item, vars_): - retVal = item - - if item and vars_: - for var in re.findall(r"\$\{([^}]+)\}", item): - if var in vars_: - retVal = retVal.replace("${%s}" % var, vars_[var]) - - return retVal diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 8bf6a8188..09c963d8f 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -790,10 +790,7 @@ def cmdLineParser(argv=None): parser.add_argument("--vuln-test", dest="vulnTest", action="store_true", help=SUPPRESS) - parser.add_argument("--stop-fail", dest="stopFail", action="store_true", - help=SUPPRESS) - - parser.add_argument("--run-case", dest="runCase", + parser.add_argument("--fuzz-test", dest="fuzzTest", action="store_true", help=SUPPRESS) # API options @@ -1002,7 +999,7 @@ def cmdLineParser(argv=None): if args.dummy: args.url = args.url or DUMMY_URL - if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile)): + if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, args.requestFile, args.updateAll, args.smokeTest, args.vulnTest, args.fuzzTest, args.wizard, args.dependencies, args.purge, args.listTampers, args.hashFile)): errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --list-tampers, --wizard, --update, --purge or --dependencies). " errMsg += "Use -h for basic and -hh for advanced help\n" parser.error(errMsg) diff --git a/sqlmap.py b/sqlmap.py index 347460cc5..8269e665e 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -173,6 +173,9 @@ def main(): elif conf.vulnTest: from lib.core.testing import vulnTest os._exitcode = 1 - (vulnTest() or 0) + elif conf.fuzzTest: + from lib.core.testing import fuzzTest + fuzzTest() else: from lib.controller.controller import start if conf.profile and six.PY2: