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
2020-09-04 13:45:33 +03:00
import base64
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 "
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
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 :
2020-05-27 18:57:38 +03:00
if DEBUG :
traceback . print_exc ( )
2019-03-29 13:04:58 +03:00
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 ( )
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> " )
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 :
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 " ] )
2019-04-30 02:08:24 +03:00
results = _cursor . fetchall ( )
2019-03-29 13:04:58 +03:00
2020-05-05 14:31:44 +03:00
output + = " <b>SQL results:</b><br> \n "
2019-11-18 13:57:06 +03:00
2020-05-05 14:31:44 +03:00
if results :
output + = " <table border= \" 1 \" > \n "
for row in results :
output + = " <tr> "
for value in row :
output + = " <td> %s </td> " % value
output + = " </tr> \n "
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> "
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 )
2020-01-08 01:49:45 +03:00
data = unquote_plus ( data . decode ( UNICODE_ENCODING , " ignore " ) )
2019-03-29 13:04:58 +03:00
self . data = data
2020-01-27 13:32:05 +03:00
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 " )
2019-03-29 13:04:58 +03:00
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 )
2020-05-27 19:30:13 +03:00
print ( " [i] running HTTP server at ' http:// %s : %d ' " % ( address , port ) )
2019-03-29 13:04:58 +03:00
_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 " )