2019-05-08 13:47:52 +03:00
|
|
|
#!/usr/bin/env python
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
"""
|
2020-12-31 13:46:27 +03:00
|
|
|
Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)
|
2017-10-11 15:50:46 +03:00
|
|
|
See the file 'LICENSE' for copying permission
|
2017-07-03 17:55:24 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
import base64
|
2017-07-04 13:14:17 +03:00
|
|
|
import datetime
|
2019-03-26 16:37:01 +03:00
|
|
|
import io
|
2017-07-03 17:55:24 +03:00
|
|
|
import re
|
2017-07-04 13:14:17 +03:00
|
|
|
import time
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2017-07-05 15:07:21 +03:00
|
|
|
from lib.core.bigarray import BigArray
|
2019-05-08 14:15:42 +03:00
|
|
|
from lib.core.convert import getBytes
|
|
|
|
from lib.core.convert import getText
|
2017-07-03 17:55:24 +03:00
|
|
|
from lib.core.settings import VERSION
|
2019-03-27 18:36:32 +03:00
|
|
|
from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer
|
2019-03-27 19:39:21 +03:00
|
|
|
from thirdparty.six.moves import http_client as _http_client
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2017-07-04 13:14:17 +03:00
|
|
|
# Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
|
|
|
|
# http://www.softwareishard.com/har/viewer/
|
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class HTTPCollectorFactory(object):
|
2017-07-03 17:55:24 +03:00
|
|
|
def __init__(self, harFile=False):
|
|
|
|
self.harFile = harFile
|
|
|
|
|
|
|
|
def create(self):
|
2017-07-04 13:14:17 +03:00
|
|
|
return HTTPCollector()
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class HTTPCollector(object):
|
2017-07-03 17:55:24 +03:00
|
|
|
def __init__(self):
|
2017-07-05 15:07:21 +03:00
|
|
|
self.messages = BigArray()
|
2017-07-18 22:46:52 +03:00
|
|
|
self.extendedArguments = {}
|
|
|
|
|
|
|
|
def setExtendedArguments(self, arguments):
|
|
|
|
self.extendedArguments = arguments
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2017-07-04 13:14:17 +03:00
|
|
|
def collectRequest(self, requestMessage, responseMessage, startTime=None, endTime=None):
|
2017-07-18 22:46:52 +03:00
|
|
|
self.messages.append(RawPair(requestMessage, responseMessage,
|
|
|
|
startTime=startTime, endTime=endTime,
|
|
|
|
extendedArguments=self.extendedArguments))
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
def obtain(self):
|
|
|
|
return {"log": {
|
|
|
|
"version": "1.2",
|
|
|
|
"creator": {"name": "sqlmap", "version": VERSION},
|
|
|
|
"entries": [pair.toEntry().toDict() for pair in self.messages],
|
|
|
|
}}
|
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class RawPair(object):
|
2017-07-18 22:46:52 +03:00
|
|
|
def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None):
|
2019-05-08 14:15:42 +03:00
|
|
|
self.request = getBytes(request)
|
|
|
|
self.response = getBytes(response)
|
2017-07-04 13:14:17 +03:00
|
|
|
self.startTime = startTime
|
|
|
|
self.endTime = endTime
|
2017-07-18 22:46:52 +03:00
|
|
|
self.extendedArguments = extendedArguments or {}
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
def toEntry(self):
|
2017-07-18 22:46:52 +03:00
|
|
|
return Entry(request=Request.parse(self.request), response=Response.parse(self.response),
|
|
|
|
startTime=self.startTime, endTime=self.endTime,
|
|
|
|
extendedArguments=self.extendedArguments)
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class Entry(object):
|
2017-07-18 22:46:52 +03:00
|
|
|
def __init__(self, request, response, startTime, endTime, extendedArguments):
|
2017-07-03 17:55:24 +03:00
|
|
|
self.request = request
|
|
|
|
self.response = response
|
2017-07-04 13:14:17 +03:00
|
|
|
self.startTime = startTime or 0
|
|
|
|
self.endTime = endTime or 0
|
2017-07-18 22:46:52 +03:00
|
|
|
self.extendedArguments = extendedArguments
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
def toDict(self):
|
2017-07-18 22:46:52 +03:00
|
|
|
out = {
|
2017-07-03 17:55:24 +03:00
|
|
|
"request": self.request.toDict(),
|
|
|
|
"response": self.response.toDict(),
|
2017-07-04 13:14:17 +03:00
|
|
|
"cache": {},
|
2017-07-20 15:50:03 +03:00
|
|
|
"timings": {
|
|
|
|
"send": -1,
|
|
|
|
"wait": -1,
|
|
|
|
"receive": -1,
|
|
|
|
},
|
2017-07-04 13:14:17 +03:00
|
|
|
"time": int(1000 * (self.endTime - self.startTime)),
|
|
|
|
"startedDateTime": "%s%s" % (datetime.datetime.fromtimestamp(self.startTime).isoformat(), time.strftime("%z")) if self.startTime else None
|
2017-07-03 17:55:24 +03:00
|
|
|
}
|
2017-07-18 22:46:52 +03:00
|
|
|
out.update(self.extendedArguments)
|
|
|
|
return out
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class Request(object):
|
2017-07-03 17:55:24 +03:00
|
|
|
def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None):
|
|
|
|
self.method = method
|
|
|
|
self.path = path
|
|
|
|
self.httpVersion = httpVersion
|
|
|
|
self.headers = headers or {}
|
|
|
|
self.postBody = postBody
|
2017-07-04 13:14:17 +03:00
|
|
|
self.comment = comment.strip() if comment else comment
|
2017-07-03 17:55:24 +03:00
|
|
|
self.raw = raw
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def parse(cls, raw):
|
|
|
|
request = HTTPRequest(raw)
|
|
|
|
return cls(method=request.command,
|
|
|
|
path=request.path,
|
|
|
|
httpVersion=request.request_version,
|
|
|
|
headers=request.headers,
|
|
|
|
postBody=request.rfile.read(),
|
|
|
|
comment=request.comment,
|
|
|
|
raw=raw)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def url(self):
|
|
|
|
host = self.headers.get("Host", "unknown")
|
|
|
|
return "http://%s%s" % (host, self.path)
|
|
|
|
|
|
|
|
def toDict(self):
|
|
|
|
out = {
|
|
|
|
"httpVersion": self.httpVersion,
|
|
|
|
"method": self.method,
|
|
|
|
"url": self.url,
|
|
|
|
"headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items()],
|
2017-07-04 13:14:17 +03:00
|
|
|
"cookies": [],
|
|
|
|
"queryString": [],
|
|
|
|
"headersSize": -1,
|
|
|
|
"bodySize": -1,
|
2019-05-08 14:15:42 +03:00
|
|
|
"comment": getText(self.comment),
|
2017-07-03 17:55:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.postBody:
|
|
|
|
contentType = self.headers.get("Content-Type")
|
|
|
|
out["postData"] = {
|
|
|
|
"mimeType": contentType,
|
2019-05-08 14:15:42 +03:00
|
|
|
"text": getText(self.postBody).rstrip("\r\n"),
|
2017-07-03 17:55:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class Response(object):
|
2019-05-08 14:15:42 +03:00
|
|
|
extract_status = re.compile(b'\\((\\d{3}) (.*)\\)')
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
def __init__(self, httpVersion, status, statusText, headers, content, raw=None, comment=None):
|
|
|
|
self.raw = raw
|
|
|
|
self.httpVersion = httpVersion
|
|
|
|
self.status = status
|
|
|
|
self.statusText = statusText
|
|
|
|
self.headers = headers
|
|
|
|
self.content = content
|
2017-07-04 13:14:17 +03:00
|
|
|
self.comment = comment.strip() if comment else comment
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def parse(cls, raw):
|
|
|
|
altered = raw
|
2019-05-08 14:15:42 +03:00
|
|
|
comment = b""
|
2017-07-03 17:55:24 +03:00
|
|
|
|
2019-05-08 14:15:42 +03:00
|
|
|
if altered.startswith(b"HTTP response [") or altered.startswith(b"HTTP redirect ["):
|
|
|
|
stream = io.BytesIO(raw)
|
2019-03-26 16:37:01 +03:00
|
|
|
first_line = stream.readline()
|
2017-07-03 17:55:24 +03:00
|
|
|
parts = cls.extract_status.search(first_line)
|
2019-05-16 02:41:26 +03:00
|
|
|
status_line = "HTTP/1.0 %s %s" % (getText(parts.group(1)), getText(parts.group(2)))
|
2019-03-26 16:37:01 +03:00
|
|
|
remain = stream.read()
|
2019-05-16 02:41:26 +03:00
|
|
|
altered = getBytes(status_line) + b"\r\n" + remain
|
2017-07-03 17:55:24 +03:00
|
|
|
comment = first_line
|
|
|
|
|
2019-03-27 15:33:46 +03:00
|
|
|
response = _http_client.HTTPResponse(FakeSocket(altered))
|
2017-07-03 17:55:24 +03:00
|
|
|
response.begin()
|
|
|
|
|
|
|
|
try:
|
2019-05-08 14:15:42 +03:00
|
|
|
content = response.read()
|
2019-03-27 15:33:46 +03:00
|
|
|
except _http_client.IncompleteRead:
|
2021-06-11 10:48:00 +03:00
|
|
|
content = raw[raw.find(b"\r\n\r\n") + 4:].rstrip(b"\r\n")
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0",
|
|
|
|
status=response.status,
|
|
|
|
statusText=response.reason,
|
|
|
|
headers=response.msg,
|
|
|
|
content=content,
|
|
|
|
comment=comment,
|
|
|
|
raw=raw)
|
|
|
|
|
|
|
|
def toDict(self):
|
|
|
|
content = {
|
|
|
|
"mimeType": self.headers.get("Content-Type"),
|
|
|
|
"text": self.content,
|
2017-07-04 13:14:17 +03:00
|
|
|
"size": len(self.content or "")
|
2017-07-03 17:55:24 +03:00
|
|
|
}
|
|
|
|
|
2019-05-08 14:15:42 +03:00
|
|
|
binary = set([b'\0', b'\1'])
|
2017-07-03 17:55:24 +03:00
|
|
|
if any(c in binary for c in self.content):
|
|
|
|
content["encoding"] = "base64"
|
2019-05-08 14:15:42 +03:00
|
|
|
content["text"] = getText(base64.b64encode(self.content))
|
|
|
|
else:
|
|
|
|
content["text"] = getText(content["text"])
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
return {
|
|
|
|
"httpVersion": self.httpVersion,
|
|
|
|
"status": self.status,
|
|
|
|
"statusText": self.statusText,
|
|
|
|
"headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items() if key.lower() != "uri"],
|
2017-07-04 13:14:17 +03:00
|
|
|
"cookies": [],
|
2017-07-03 17:55:24 +03:00
|
|
|
"content": content,
|
2017-07-04 13:14:17 +03:00
|
|
|
"headersSize": -1,
|
|
|
|
"bodySize": -1,
|
|
|
|
"redirectURL": "",
|
2019-05-08 14:15:42 +03:00
|
|
|
"comment": getText(self.comment),
|
2017-07-03 17:55:24 +03:00
|
|
|
}
|
|
|
|
|
2019-05-29 17:42:04 +03:00
|
|
|
class FakeSocket(object):
|
2017-07-03 17:55:24 +03:00
|
|
|
# Original source:
|
|
|
|
# https://stackoverflow.com/questions/24728088/python-parse-http-response-string
|
|
|
|
|
|
|
|
def __init__(self, response_text):
|
2019-05-08 14:15:42 +03:00
|
|
|
self._file = io.BytesIO(response_text)
|
2017-07-03 17:55:24 +03:00
|
|
|
|
|
|
|
def makefile(self, *args, **kwargs):
|
|
|
|
return self._file
|
|
|
|
|
2019-03-27 18:36:32 +03:00
|
|
|
class HTTPRequest(_BaseHTTPServer.BaseHTTPRequestHandler):
|
2017-07-03 17:55:24 +03:00
|
|
|
# Original source:
|
|
|
|
# https://stackoverflow.com/questions/4685217/parse-raw-http-headers
|
|
|
|
|
|
|
|
def __init__(self, request_text):
|
|
|
|
self.comment = None
|
2019-05-08 14:15:42 +03:00
|
|
|
self.rfile = io.BytesIO(request_text)
|
2017-07-03 17:55:24 +03:00
|
|
|
self.raw_requestline = self.rfile.readline()
|
|
|
|
|
2019-05-08 14:15:42 +03:00
|
|
|
if self.raw_requestline.startswith(b"HTTP request ["):
|
2017-07-03 17:55:24 +03:00
|
|
|
self.comment = self.raw_requestline
|
|
|
|
self.raw_requestline = self.rfile.readline()
|
|
|
|
|
|
|
|
self.error_code = self.error_message = None
|
|
|
|
self.parse_request()
|
|
|
|
|
|
|
|
def send_error(self, code, message):
|
|
|
|
self.error_code = code
|
|
|
|
self.error_message = message
|