Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0d756a8

Browse files
author
Louis-Philippe Huberdeau
committed
Parse request data and convert to HAR, include in injection data
1 parent 8df4cc3 commit 0d756a8

2 files changed

Lines changed: 178 additions & 1 deletion

File tree

lib/controller/checks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def checkSqlInjection(place, parameter, value):
117117

118118
while tests:
119119
test = tests.pop(0)
120+
threadData.requestCollector.reset()
120121

121122
try:
122123
if kb.endDetection:
@@ -700,6 +701,7 @@ def genCmpPayload():
700701
injection.data[stype].matchRatio = kb.matchRatio
701702
injection.data[stype].trueCode = trueCode
702703
injection.data[stype].falseCode = falseCode
704+
injection.data[stype].collectedRequests = threadData.requestCollector.obtain()
703705

704706
injection.conf.textOnly = conf.textOnly
705707
injection.conf.titles = conf.titles

lib/utils/collect.py

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
See the file 'doc/COPYING' for copying permission
66
"""
77

8+
from BaseHTTPServer import BaseHTTPRequestHandler
9+
from StringIO import StringIO
10+
811
from lib.core.data import logger
12+
from lib.core.settings import VERSION
913

1014

1115
class 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

2935
class 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]:\nPOST /create.php HTTP/1.1\nHost: 240.0.0.2\nAccept-encoding: gzip,deflate\nCache-control: no-cache\nContent-type: application/x-www-form-urlencoded; charset=utf-8\nAccept: */*\nUser-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\nCookie: PHPSESSID=65c4a9cfbbe91f2d975d50ce5e8d1026\nContent-length: 138\nConnection: close\n\nname=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

Comments
 (0)