55See the file 'doc/COPYING' for copying permission
66"""
77
8+ from BaseHTTPServer import BaseHTTPRequestHandler
9+ from StringIO import StringIO
10+
811from lib .core .data import logger
12+ from lib .core .settings import VERSION
913
1014
1115class RequestCollectorFactory :
@@ -18,6 +22,8 @@ def create(self):
1822
1923 if not self .collect :
2024 collector .collectRequest = self ._noop
25+ else :
26+ logger .info ("Request collection is enabled." )
2127
2228 return collector
2329
@@ -28,5 +34,174 @@ def _noop(*args, **kwargs):
2834
2935class RequestCollector :
3036
37+ def __init__ (self ):
38+ self .reset ()
39+
3140 def collectRequest (self , requestMessage , responseMessage ):
32- logger .info ("Received request/response: %s/%s" , len (requestMessage ), len (responseMessage ))
41+ self .messages .append (RawPair (requestMessage , responseMessage ))
42+
43+ def reset (self ):
44+ self .messages = []
45+
46+ def obtain (self ):
47+ if self .messages :
48+ return {"log" : {
49+ "version" : "1.2" ,
50+ "creator" : {"name" : "SQLMap" , "version" : VERSION },
51+ "entries" : [pair .toEntry ().toDict () for pair in self .messages ],
52+ }}
53+
54+
55+ class RawPair :
56+
57+ def __init__ (self , request , response ):
58+ self .request = request
59+ self .response = response
60+
61+ def toEntry (self ):
62+ return Entry (request = Request .parse (self .request ),
63+ response = Response .parse (self .response ))
64+
65+
66+ class Entry :
67+
68+ def __init__ (self , request , response ):
69+ self .request = request
70+ self .response = response
71+
72+ def toDict (self ):
73+ return {
74+ "request" : self .request .toDict (),
75+ "response" : self .response .toDict (),
76+ }
77+
78+
79+ class Request :
80+
81+ def __init__ (self , method , path , httpVersion , headers , postBody = None , raw = None , comment = None ):
82+ self .method = method
83+ self .path = path
84+ self .httpVersion = httpVersion
85+ self .headers = headers or {}
86+ self .postBody = postBody
87+ self .comment = comment
88+ self .raw = raw
89+
90+ @classmethod
91+ def parse (cls , raw ):
92+ request = HTTPRequest (raw )
93+ return cls (method = request .command ,
94+ path = request .path ,
95+ httpVersion = request .request_version ,
96+ headers = request .headers ,
97+ postBody = request .rfile .read (),
98+ comment = request .comment ,
99+ raw = raw )
100+
101+ @property
102+ def url (self ):
103+ host = self .headers .get ('Host' , 'unknown' )
104+ return "http://%s%s" % (host , self .path )
105+
106+ def toDict (self ):
107+ out = {
108+ "method" : self .method ,
109+ "url" : self .url ,
110+ "headers" : [dict (name = key , value = value ) for key , value in self .headers .items ()],
111+ "comment" : self .comment ,
112+ "_raw" : self .raw ,
113+ }
114+ if self .postBody :
115+ contentType = self .headers .get ('Content-Type' )
116+ out ["postData" ] = {
117+ "mimeType" : contentType ,
118+ "text" : self .postBody ,
119+ }
120+ return out
121+
122+
123+ class Response :
124+
125+ def __init__ (self ):
126+ pass
127+
128+ @classmethod
129+ def parse (cls , raw ):
130+ return cls ()
131+
132+ def toDict (self ):
133+ return {
134+ }
135+
136+
137+ class HTTPRequest (BaseHTTPRequestHandler ):
138+ # Original source:
139+ # https://stackoverflow.com/questions/4685217/parse-raw-http-headers
140+
141+ def __init__ (self , request_text ):
142+ self .comment = None
143+ self .rfile = StringIO (request_text )
144+ self .raw_requestline = self .rfile .readline ()
145+
146+ if self .raw_requestline .startswith ("HTTP request [" ):
147+ self .comment = self .raw_requestline
148+ self .raw_requestline = self .rfile .readline ()
149+
150+ self .error_code = self .error_message = None
151+ self .parse_request ()
152+
153+ def send_error (self , code , message ):
154+ self .error_code = code
155+ self .error_message = message
156+
157+
158+ if __name__ == '__main__' :
159+ import unittest
160+
161+ class RequestParseTest (unittest .TestCase ):
162+
163+ def test_basic_request (self ):
164+ req = Request .parse ("GET /test HTTP/1.0\r \n "
165+ "Host: test\r \n "
166+ "Connection: close" )
167+ self .assertEqual ("GET" , req .method )
168+ self .assertEqual ("/test" , req .path )
169+ self .assertEqual ("close" , req .headers ['Connection' ])
170+ self .assertEqual ("test" , req .headers ['Host' ])
171+ self .assertEqual ("HTTP/1.0" , req .httpVersion )
172+
173+ def test_with_request_as_logged_by_sqlmap (self ):
174+ raw = "HTTP request [#75]:\n POST /create.php HTTP/1.1\n Host: 240.0.0.2\n Accept-encoding: gzip,deflate\n Cache-control: no-cache\n Content-type: application/x-www-form-urlencoded; charset=utf-8\n Accept: */*\n User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10\n Cookie: PHPSESSID=65c4a9cfbbe91f2d975d50ce5e8d1026\n Content-length: 138\n Connection: close\n \n name=test%27%29%3BSELECT%20LIKE%28%27ABCDEFG%27%2CUPPER%28HEX%28RANDOMBLOB%28000000000%2F2%29%29%29%29--&csrfmiddlewaretoken=594d26cfa3fad\n " # noqa
175+ req = Request .parse (raw )
176+ self .assertEqual ("POST" , req .method )
177+ self .assertEqual ("138" , req .headers ["Content-Length" ])
178+ self .assertIn ("csrfmiddlewaretoken" , req .postBody )
179+ self .assertEqual ("HTTP request [#75]:\n " , req .comment )
180+
181+ class RequestRenderTest (unittest .TestCase ):
182+ def test_render_get_request (self ):
183+ req = Request (method = "GET" ,
184+ path = "/test.php" ,
185+ headers = {"Host" : "example.com" , "Content-Length" : "0" },
186+ httpVersion = "HTTP/1.1" ,
187+ comment = "Hello World" )
188+ out = req .toDict ()
189+ self .assertEqual ("GET" , out ["method" ])
190+ self .assertEqual ("http://example.com/test.php" , out ["url" ])
191+ self .assertIn ({"name" : "Host" , "value" : "example.com" }, out ["headers" ])
192+ self .assertEqual ("Hello World" , out ["comment" ])
193+
194+ def test_render_with_post_body (self ):
195+ req = Request (method = "POST" ,
196+ path = "/test.php" ,
197+ headers = {"Host" : "example.com" ,
198+ "Content-Type" : "application/x-www-form-urlencoded; charset=utf-8" },
199+ httpVersion = "HTTP/1.1" ,
200+ postBody = "name=test&csrfmiddlewaretoken=594d26cfa3fad\n " )
201+ out = req .toDict ()
202+ self .assertEqual (out ["postData" ], {
203+ "mimeType" : "application/x-www-form-urlencoded; charset=utf-8" ,
204+ "text" : "name=test&csrfmiddlewaretoken=594d26cfa3fad\n " ,
205+ })
206+
207+ unittest .main (buffer = False )
0 commit comments