2019-03-29 13:04:58 +03:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""
|
|
|
|
vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes)
|
|
|
|
|
2020-01-01 15:25:15 +03:00
|
|
|
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
|
2019-03-29 13:04:58 +03:00
|
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
"""
|
|
|
|
|
|
|
|
from __future__ import print_function
|
|
|
|
|
2019-10-15 13:29:39 +03:00
|
|
|
import json
|
2019-03-29 13:04:58 +03:00
|
|
|
import re
|
|
|
|
import sqlite3
|
|
|
|
import sys
|
2019-04-30 02:08:24 +03:00
|
|
|
import threading
|
2019-03-29 13:04:58 +03:00
|
|
|
import traceback
|
|
|
|
|
2019-11-30 06:42:38 +03:00
|
|
|
PY3 = sys.version_info >= (3, 0)
|
2019-11-30 13:14:41 +03:00
|
|
|
UNICODE_ENCODING = "utf-8"
|
2019-11-30 06:42:38 +03:00
|
|
|
|
|
|
|
if PY3:
|
2019-04-29 12:32:01 +03:00
|
|
|
from http.client import INTERNAL_SERVER_ERROR
|
2019-03-29 13:04:58 +03:00
|
|
|
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
|
2019-04-29 12:32:01 +03:00
|
|
|
from httplib import INTERNAL_SERVER_ERROR
|
2019-03-29 13:04:58 +03:00
|
|
|
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');
|
2019-04-29 12:58:52 +03:00
|
|
|
INSERT INTO users (id, name, surname) VALUES (3, 'wu', '179ad45c6ce2cb97cf1029e212046e81');
|
2019-03-29 13:04:58 +03:00
|
|
|
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
|
2019-04-30 02:08:24 +03:00
|
|
|
_lock = None
|
2019-03-29 13:04:58 +03:00
|
|
|
_server = None
|
|
|
|
|
2019-04-19 14:28:11 +03:00
|
|
|
def init(quiet=False):
|
2019-03-29 13:04:58 +03:00
|
|
|
global _conn
|
|
|
|
global _cursor
|
2019-04-30 02:08:24 +03:00
|
|
|
global _lock
|
2019-03-29 13:04:58 +03:00
|
|
|
|
|
|
|
_conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False)
|
|
|
|
_cursor = _conn.cursor()
|
2019-04-30 02:08:24 +03:00
|
|
|
_lock = threading.Lock()
|
2019-03-29 13:04:58 +03:00
|
|
|
|
|
|
|
_cursor.executescript(SCHEMA)
|
|
|
|
|
2019-04-19 14:28:11 +03:00
|
|
|
if quiet:
|
|
|
|
global print
|
|
|
|
|
|
|
|
def _(*args, **kwargs):
|
|
|
|
pass
|
|
|
|
|
|
|
|
print = _
|
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
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))
|
|
|
|
|
2019-04-29 12:32:01 +03:00
|
|
|
if "<script>" in unquote_plus(query):
|
|
|
|
self.send_response(INTERNAL_SERVER_ERROR)
|
|
|
|
self.send_header("Connection", "close")
|
|
|
|
self.end_headers()
|
2019-11-30 13:14:41 +03:00
|
|
|
self.wfile.write("CLOUDFLARE_ERROR_500S_BOX".encode(UNICODE_ENCODING))
|
2019-04-29 12:32:01 +03:00
|
|
|
return
|
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
if hasattr(self, "data"):
|
2019-10-15 13:29:39 +03:00
|
|
|
if self.data.startswith('{') and self.data.endswith('}'):
|
|
|
|
params.update(json.loads(self.data))
|
2019-11-06 13:52:50 +03:00
|
|
|
elif self.data.startswith('<') and self.data.endswith('>'):
|
2019-11-14 13:49:30 +03:00
|
|
|
params.update(dict((_[0], _[1].replace("'", "'").replace(""", '"').replace("<", '<').replace(">", '>').replace("&", '&')) for _ in re.findall(r'name="([^"]+)" value="([^"]*)"', self.data)))
|
2019-10-15 13:29:39 +03:00
|
|
|
else:
|
|
|
|
params.update(parse_qs(self.data))
|
2019-03-29 13:04:58 +03:00
|
|
|
|
2019-11-06 14:27:47 +03:00
|
|
|
for name in self.headers:
|
|
|
|
params[name.lower()] = self.headers[name]
|
|
|
|
|
2019-11-06 14:54:18 +03:00
|
|
|
if "cookie" in params:
|
|
|
|
for part in params["cookie"].split(';'):
|
|
|
|
part = part.strip()
|
|
|
|
if '=' in part:
|
|
|
|
name, value = part.split('=', 1)
|
|
|
|
params[name.strip()] = unquote_plus(value.strip())
|
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
for key in params:
|
2019-10-15 13:29:39 +03:00
|
|
|
if params[key] and isinstance(params[key], (tuple, list)):
|
2019-03-29 13:04:58 +03:00
|
|
|
params[key] = params[key][-1]
|
|
|
|
|
|
|
|
self.url, self.params = path, params
|
|
|
|
|
|
|
|
if self.url == '/':
|
2019-11-14 16:21:53 +03:00
|
|
|
|
2019-11-18 14:08:26 +03:00
|
|
|
if not any(_ in self.params for _ in ("id", "query")):
|
2019-11-18 14:37:17 +03:00
|
|
|
self.send_response(OK)
|
2019-11-30 13:14:41 +03:00
|
|
|
self.send_header("Content-type", "text/html; charset=%s" % UNICODE_ENCODING)
|
2019-03-29 13:04:58 +03:00
|
|
|
self.send_header("Connection", "close")
|
|
|
|
self.end_headers()
|
2019-11-14 16:21:53 +03:00
|
|
|
self.wfile.write(b"<html><p><h3>GET:</h3><a href='/?id=1'>link</a></p><hr><p><h3>POST:</h3><form method='post'>ID: <input type='text' name='id'><input type='submit' value='Submit'></form></p></html>")
|
2019-03-29 13:04:58 +03:00
|
|
|
else:
|
2019-11-18 14:37:17 +03:00
|
|
|
code, output = OK, ""
|
2019-03-29 13:04:58 +03:00
|
|
|
|
|
|
|
try:
|
2019-11-18 13:57:06 +03:00
|
|
|
|
|
|
|
if self.params.get("echo", ""):
|
|
|
|
output += "%s<br>" % self.params["echo"]
|
|
|
|
|
2019-04-30 02:08:24 +03:00
|
|
|
with _lock:
|
2019-11-18 14:08:26 +03:00
|
|
|
if "query" in self.params:
|
|
|
|
_cursor.execute(self.params["query"])
|
|
|
|
elif "id" in self.params:
|
|
|
|
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"])
|
2019-04-30 02:08:24 +03:00
|
|
|
results = _cursor.fetchall()
|
2019-03-29 13:04:58 +03:00
|
|
|
|
2019-11-18 13:57:06 +03:00
|
|
|
output += "<b>SQL results:</b>\n"
|
2019-03-29 13:04:58 +03:00
|
|
|
output += "<table border=\"1\">\n"
|
2019-11-18 13:57:06 +03:00
|
|
|
|
2019-04-30 02:08:24 +03:00
|
|
|
for row in results:
|
2019-03-29 13:04:58 +03:00
|
|
|
output += "<tr>"
|
|
|
|
for value in row:
|
|
|
|
output += "<td>%s</td>" % value
|
|
|
|
output += "</tr>\n"
|
2019-11-18 13:57:06 +03:00
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
output += "</table>\n"
|
2019-04-19 14:54:48 +03:00
|
|
|
output += "</body></html>"
|
2019-03-29 13:04:58 +03:00
|
|
|
except Exception as ex:
|
2019-11-18 14:37:17 +03:00
|
|
|
code = INTERNAL_SERVER_ERROR
|
2019-03-29 13:04:58 +03:00
|
|
|
output = "%s: %s" % (re.search(r"'([^']+)'", str(type(ex))).group(1), ex)
|
|
|
|
|
2019-11-18 14:37:17 +03:00
|
|
|
self.send_response(code)
|
|
|
|
|
|
|
|
self.send_header("Content-type", "text/html")
|
|
|
|
self.send_header("Connection", "close")
|
|
|
|
|
2019-11-14 14:51:09 +03:00
|
|
|
if self.raw_requestline.startswith(b"HEAD"):
|
|
|
|
self.send_header("Content-Length", str(len(output)))
|
|
|
|
self.end_headers()
|
|
|
|
else:
|
2019-11-18 14:37:17 +03:00
|
|
|
self.end_headers()
|
2019-11-30 13:25:38 +03:00
|
|
|
self.wfile.write(output if isinstance(output, bytes) else output.encode(UNICODE_ENCODING))
|
2019-03-29 13:04:58 +03:00
|
|
|
else:
|
|
|
|
self.send_response(NOT_FOUND)
|
|
|
|
self.send_header("Connection", "close")
|
|
|
|
self.end_headers()
|
|
|
|
|
|
|
|
def do_GET(self):
|
|
|
|
self.do_REQUEST()
|
|
|
|
|
2019-11-13 13:17:32 +03:00
|
|
|
def do_PUT(self):
|
|
|
|
self.do_REQUEST()
|
|
|
|
|
2019-11-14 14:51:09 +03:00
|
|
|
def do_HEAD(self):
|
|
|
|
self.do_REQUEST()
|
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
def do_POST(self):
|
|
|
|
length = int(self.headers.get("Content-length", 0))
|
|
|
|
if length:
|
|
|
|
data = self.rfile.read(length)
|
2019-11-30 13:14:41 +03:00
|
|
|
data = unquote_plus(data.decode(UNICODE_ENCODING))
|
2019-03-29 13:04:58 +03:00
|
|
|
self.data = data
|
|
|
|
self.do_REQUEST()
|
|
|
|
|
2019-04-19 14:28:11 +03:00
|
|
|
def log_message(self, format, *args):
|
|
|
|
return
|
|
|
|
|
2019-03-29 13:04:58 +03:00
|
|
|
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")
|