sqlmap/extra/vulnserver/vulnserver.py

259 lines
8.9 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python
"""
vulnserver.py - Trivial SQLi vulnerable HTTP server (Note: for testing purposes)
2022-01-03 13:30:34 +03:00
Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from __future__ import print_function
2020-09-04 13:45:33 +03:00
import base64
2019-10-15 13:29:39 +03:00
import json
import re
import sqlite3
import sys
import threading
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"
2020-05-27 18:57:38 +03:00
DEBUG = False
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
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
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');
2021-09-08 22:01:41 +03:00
INSERT INTO users (id, name, surname) VALUES (4, 'sqlmap/1.0-dev (https://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
_lock = None
_server = None
2021-10-01 10:02:44 +03:00
_alive = False
def init(quiet=False):
global _conn
global _cursor
global _lock
_conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False)
_cursor = _conn.cursor()
_lock = threading.Lock()
_cursor.executescript(SCHEMA)
if quiet:
global print
def _(*args, **kwargs):
pass
print = _
class ThreadingServer(ThreadingMixIn, HTTPServer):
def finish_request(self, *args, **kwargs):
try:
HTTPServer.finish_request(self, *args, **kwargs)
except Exception:
2020-05-27 18:57:38 +03:00
if DEBUG:
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)
2021-01-20 18:29:52 +03:00
self.send_header("X-Powered-By", "Express")
2019-04-29 12:32:01 +03:00
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
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('>'):
params.update(dict((_[0], _[1].replace("&apos;", "'").replace("&quot;", '"').replace("&lt;", '<').replace("&gt;", '>').replace("&amp;", '&')) for _ in re.findall(r'name="([^"]+)" value="([^"]*)"', self.data)))
2019-10-15 13:29:39 +03:00
else:
2021-09-29 19:09:59 +03:00
self.data = self.data.replace(';', '&') # Note: seems that Python3 started ignoring parameter splitting with ';'
2019-10-15 13:29:39 +03:00
params.update(parse_qs(self.data))
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())
for key in params:
2019-10-15 13:29:39 +03:00
if params[key] and isinstance(params[key], (tuple, list)):
params[key] = params[key][-1]
self.url, self.params = path, params
if self.url == '/':
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)
self.send_header("Connection", "close")
self.end_headers()
2020-05-27 19:30:13 +03:00
self.wfile.write(b"<!DOCTYPE html><html><head><title>vulnserver</title></head><body><h3>GET:</h3><a href='/?id=1'>link</a><hr><h3>POST:</h3><form method='post'>ID: <input type='text' name='id'><input type='submit' value='Submit'></form></body></html>")
else:
2019-11-18 14:37:17 +03:00
code, output = OK, ""
try:
2019-11-18 13:57:06 +03:00
if self.params.get("echo", ""):
output += "%s<br>" % self.params["echo"]
2021-02-01 23:58:36 +03:00
if self.params.get("reflect", ""):
output += "%s<br>" % self.params.get("id")
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:
2020-09-04 13:45:33 +03:00
if "base64" in self.params:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % base64.b64decode("%s===" % self.params["id"], altchars=self.params.get("altchars")).decode())
else:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1" % self.params["id"])
results = _cursor.fetchall()
2020-05-05 14:31:44 +03:00
output += "<b>SQL results:</b><br>\n"
2019-11-18 13:57:06 +03:00
2021-02-03 13:52:50 +03:00
if self.params.get("code", ""):
if not results:
code = INTERNAL_SERVER_ERROR
else:
if results:
output += "<table border=\"1\">\n"
2020-05-05 14:31:44 +03:00
2021-02-03 13:52:50 +03:00
for row in results:
output += "<tr>"
for value in row:
output += "<td>%s</td>" % value
output += "</tr>\n"
2020-05-05 14:31:44 +03:00
2021-02-03 13:52:50 +03:00
output += "</table>\n"
else:
output += "no results found"
2019-11-18 13:57:06 +03:00
2019-04-19 14:54:48 +03:00
output += "</body></html>"
except Exception as ex:
2019-11-18 14:37:17 +03:00
code = INTERNAL_SERVER_ERROR
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))
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):
2021-02-03 13:52:50 +03:00
self.do_POST()
2019-11-13 13:17:32 +03:00
2019-11-14 14:51:09 +03:00
def do_HEAD(self):
self.do_REQUEST()
def do_POST(self):
length = int(self.headers.get("Content-length", 0))
if length:
data = self.rfile.read(length)
2020-01-08 01:49:45 +03:00
data = unquote_plus(data.decode(UNICODE_ENCODING, "ignore"))
self.data = data
elif self.headers.get("Transfer-encoding") == "chunked":
data, line = b"", b""
count = 0
while True:
line += self.rfile.read(1)
if line.endswith(b'\n'):
if count % 2 == 1:
current = line.rstrip(b"\r\n")
if not current:
break
else:
data += current
count += 1
line = b""
self.data = data.decode(UNICODE_ENCODING, "ignore")
self.do_REQUEST()
def log_message(self, format, *args):
return
def run(address=LISTEN_ADDRESS, port=LISTEN_PORT):
2021-10-01 10:02:44 +03:00
global _alive
global _server
try:
2021-10-01 10:02:44 +03:00
_alive = True
_server = ThreadingServer((address, port), ReqHandler)
2020-05-27 19:30:13 +03:00
print("[i] running HTTP server at 'http://%s:%d'" % (address, port))
_server.serve_forever()
except KeyboardInterrupt:
_server.socket.close()
raise
2021-10-01 10:02:44 +03:00
finally:
_alive = False
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")