From 23ff1cadab46ae764b824280f8770a20637886b4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 29 Mar 2019 11:04:58 +0100 Subject: [PATCH] Adding SQLi vulnserver (for testing purposes) --- extra/vulnserver/__init__.py | 8 ++ extra/vulnserver/vulnserver.py | 148 +++++++++++++++++++++++++++++++++ lib/core/settings.py | 4 +- 3 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 extra/vulnserver/__init__.py create mode 100644 extra/vulnserver/vulnserver.py diff --git a/extra/vulnserver/__init__.py b/extra/vulnserver/__init__.py new file mode 100644 index 000000000..c654cbef7 --- /dev/null +++ b/extra/vulnserver/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +pass diff --git a/extra/vulnserver/vulnserver.py b/extra/vulnserver/vulnserver.py new file mode 100644 index 000000000..85f064814 --- /dev/null +++ b/extra/vulnserver/vulnserver.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +""" +vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes) + +Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +from __future__ import print_function + +import re +import sqlite3 +import sys +import traceback + +if sys.version_info >= (3, 0): + from http.client import FOUND + from http.client import NOT_FOUND + from http.client import OK + from http.server import BaseHTTPRequestHandler + from http.server import HTTPServer + from socketserver import ThreadingMixIn + from urllib.parse import parse_qs + from urllib.parse import unquote_plus +else: + from BaseHTTPServer import BaseHTTPRequestHandler + from BaseHTTPServer import HTTPServer + from httplib import FOUND + from httplib import NOT_FOUND + from httplib import OK + from SocketServer import ThreadingMixIn + from urlparse import parse_qs + from urllib import unquote_plus + +SCHEMA = """ + CREATE TABLE users ( + id INTEGER, + name TEXT, + surname TEXT + ); + INSERT INTO users (id, name, surname) VALUES (1, 'luther', 'blisset'); + INSERT INTO users (id, name, surname) VALUES (2, 'fluffy', 'bunny'); + INSERT INTO users (id, name, surname) VALUES (3, 'wu', 'ming'); + INSERT INTO users (id, name, surname) VALUES (4, 'sqlmap/1.0-dev (http://sqlmap.org)', 'user agent header'); + INSERT INTO users (id, name, surname) VALUES (5, NULL, 'nameisnull'); +""" + +LISTEN_ADDRESS = "localhost" +LISTEN_PORT = 8440 + +_conn = None +_cursor = None +_server = None + +def init(): + global _conn + global _cursor + + _conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False) + _cursor = _conn.cursor() + + _cursor.executescript(SCHEMA) + +class ThreadingServer(ThreadingMixIn, HTTPServer): + def finish_request(self, *args, **kwargs): + try: + HTTPServer.finish_request(self, *args, **kwargs) + except Exception: + traceback.print_exc() + +class ReqHandler(BaseHTTPRequestHandler): + def do_REQUEST(self): + path, query = self.path.split('?', 1) if '?' in self.path else (self.path, "") + params = {} + + if query: + params.update(parse_qs(query)) + + if hasattr(self, "data"): + params.update(parse_qs(self.data)) + + for key in params: + if params[key]: + params[key] = params[key][-1] + + self.url, self.params = path, params + + if self.url == '/': + if "id" not in params: + self.send_response(FOUND) + self.send_header("Connection", "close") + self.send_header("Location", "/?id=1") + self.end_headers() + else: + self.send_response(OK) + self.send_header("Content-type", "text/html") + self.send_header("Connection", "close") + self.end_headers() + + try: + _cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params.get("id", "")) + + output = "SQL results:\n" + output += "\n" + for row in _cursor.fetchall(): + output += "" + for value in row: + output += "" % value + output += "\n" + output += "
%s
\n" + output += ""; + except Exception as ex: + output = "%s: %s" % (re.search(r"'([^']+)'", str(type(ex))).group(1), ex) + + self.wfile.write(output.encode("utf8")) + else: + self.send_response(NOT_FOUND) + self.send_header("Connection", "close") + self.end_headers() + + def do_GET(self): + self.do_REQUEST() + + def do_POST(self): + length = int(self.headers.get("Content-length", 0)) + if length: + data = self.rfile.read(length) + data = unquote_plus(data.decode("utf8")) + self.data = data + self.do_REQUEST() + +def run(address=LISTEN_ADDRESS, port=LISTEN_PORT): + global _server + try: + _server = ThreadingServer((address, port), ReqHandler) + print("[i] running HTTP server at '%s:%d'" % (address, port)) + _server.serve_forever() + except KeyboardInterrupt: + _server.socket.close() + raise + +if __name__ == "__main__": + try: + init() + run(sys.argv[1] if len(sys.argv) > 1 else LISTEN_ADDRESS, int(sys.argv[2] if len(sys.argv) > 2 else LISTEN_PORT)) + except KeyboardInterrupt: + print("\r[x] Ctrl-C received") diff --git a/lib/core/settings.py b/lib/core/settings.py index 1b8865782..a3ea56719 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -17,7 +17,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME from lib.core.enums import OS # sqlmap version (...) -VERSION = "1.3.3.78" +VERSION = "1.3.3.79" 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) @@ -98,7 +98,7 @@ MAX_CONSECUTIVE_CONNECTION_ERRORS = 15 PRECONNECT_CANDIDATE_TIMEOUT = 10 # Servers known to cause issue with pre-connection mechanism (because of lack of multi-threaded support) -PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP",) +PRECONNECT_INCOMPATIBLE_SERVERS = ("SimpleHTTP", "BaseHTTP") # Maximum sleep time in "Murphy" (testing) mode MAX_MURPHY_SLEEP_TIME = 3