mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-11-09 04:59:34 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
397a3bde2e | ||
|
|
097f4d2c37 | ||
|
|
202d8cc1c4 | ||
|
|
f65de0e890 | ||
|
|
b333a14d8b | ||
|
|
74f50122fb | ||
|
|
bf8b9956e0 | ||
|
|
8476cf4d09 | ||
|
|
3130623afc | ||
|
|
c15f4bf055 | ||
|
|
9cd0619e2b |
No files matched your search
@@ -1818,6 +1818,8 @@
|
|||||||
/run/secrets/kubernetes.io/serviceaccount/namespace
|
/run/secrets/kubernetes.io/serviceaccount/namespace
|
||||||
/var/run/secrets/kubernetes.io/serviceaccount/namespace
|
/var/run/secrets/kubernetes.io/serviceaccount/namespace
|
||||||
/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||||
|
/etc/kubernetes/admin.conf
|
||||||
|
/etc/kubernetes/kubelet.conf
|
||||||
|
|
||||||
# Cloud provider credentials
|
# Cloud provider credentials
|
||||||
|
|
||||||
@@ -1825,7 +1827,9 @@
|
|||||||
/root/.aws/config
|
/root/.aws/config
|
||||||
/root/.config/gcloud/application_default_credentials.json
|
/root/.config/gcloud/application_default_credentials.json
|
||||||
/root/.config/gcloud/credentials.db
|
/root/.config/gcloud/credentials.db
|
||||||
|
/root/.config/gcloud/access_tokens.db
|
||||||
/root/.azure/accessTokens.json
|
/root/.azure/accessTokens.json
|
||||||
|
/root/.azure/credentials
|
||||||
/root/.kube/config
|
/root/.kube/config
|
||||||
|
|
||||||
# SSH keys and DB/tool history
|
# SSH keys and DB/tool history
|
||||||
@@ -1834,6 +1838,10 @@
|
|||||||
/root/.ssh/id_ed25519
|
/root/.ssh/id_ed25519
|
||||||
/root/.ssh/id_ecdsa
|
/root/.ssh/id_ecdsa
|
||||||
/root/.ssh/id_dsa
|
/root/.ssh/id_dsa
|
||||||
|
/root/.ssh/config
|
||||||
|
/etc/ssh/ssh_host_rsa_key
|
||||||
|
/etc/ssh/ssh_host_ed25519_key
|
||||||
|
/etc/ssh/ssh_host_ecdsa_key
|
||||||
/root/.mysql_history
|
/root/.mysql_history
|
||||||
/root/.psql_history
|
/root/.psql_history
|
||||||
/root/.rediscli_history
|
/root/.rediscli_history
|
||||||
@@ -1852,3 +1860,60 @@
|
|||||||
/app/application.properties
|
/app/application.properties
|
||||||
/app/application.yml
|
/app/application.yml
|
||||||
/app/docker-compose.yml
|
/app/docker-compose.yml
|
||||||
|
|
||||||
|
# Developer, package manager and DBMS credential files
|
||||||
|
|
||||||
|
/root/.env
|
||||||
|
/root/.netrc
|
||||||
|
/root/.git-credentials
|
||||||
|
/root/.npmrc
|
||||||
|
/root/.pypirc
|
||||||
|
/root/.docker/config.json
|
||||||
|
/root/.config/gh/hosts.yml
|
||||||
|
/root/.config/op/config
|
||||||
|
/root/.my.cnf
|
||||||
|
/etc/mysql/debian.cnf
|
||||||
|
|
||||||
|
# Hosting control panels
|
||||||
|
|
||||||
|
/etc/psa/.psa.shadow
|
||||||
|
/etc/psa/private/secret_key
|
||||||
|
|
||||||
|
# AI agent credentials
|
||||||
|
|
||||||
|
/root/.claude/.credentials.json
|
||||||
|
/root/.claude.json
|
||||||
|
/root/.codex/auth.json
|
||||||
|
/root/.config/anthropic/config.json
|
||||||
|
|
||||||
|
# Secrets in the default user's home on common cloud images
|
||||||
|
|
||||||
|
/home/ubuntu/.ssh/id_rsa
|
||||||
|
/home/ubuntu/.aws/credentials
|
||||||
|
/home/ubuntu/.kube/config
|
||||||
|
/home/ubuntu/.git-credentials
|
||||||
|
/home/ubuntu/.claude/.credentials.json
|
||||||
|
/home/debian/.ssh/id_rsa
|
||||||
|
/home/debian/.aws/credentials
|
||||||
|
/home/debian/.kube/config
|
||||||
|
/home/debian/.git-credentials
|
||||||
|
/home/admin/.ssh/id_rsa
|
||||||
|
/home/admin/.aws/credentials
|
||||||
|
/home/admin/.kube/config
|
||||||
|
/home/admin/.git-credentials
|
||||||
|
/home/ec2-user/.ssh/id_rsa
|
||||||
|
/home/ec2-user/.aws/credentials
|
||||||
|
/home/ec2-user/.kube/config
|
||||||
|
/home/ec2-user/.git-credentials
|
||||||
|
/home/centos/.ssh/id_rsa
|
||||||
|
/home/centos/.aws/credentials
|
||||||
|
/home/centos/.kube/config
|
||||||
|
/home/opc/.ssh/id_rsa
|
||||||
|
/home/opc/.aws/credentials
|
||||||
|
/home/opc/.kube/config
|
||||||
|
|
||||||
|
# Appliance and LFI-to-RCE gadget targets
|
||||||
|
|
||||||
|
/dev/cmdb/sslvpn_websession
|
||||||
|
/usr/share/php/pearcmd.php
|
||||||
|
/usr/local/lib/php/pearcmd.php
|
||||||
+25
-9
@@ -3236,7 +3236,7 @@ def logHTTPTraffic(requestLogMsg, responseLogMsg, startTime=None, endTime=None):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if conf.harFile:
|
if conf.harFile:
|
||||||
conf.httpCollector.collectRequest(requestLogMsg, responseLogMsg, startTime, endTime)
|
conf.httpCollector.collectRequest(requestLogMsg, responseLogMsg, startTime, endTime, scheme=conf.scheme)
|
||||||
|
|
||||||
if conf.trafficFile:
|
if conf.trafficFile:
|
||||||
with kb.locks.log:
|
with kb.locks.log:
|
||||||
@@ -4066,7 +4066,7 @@ def decodeIntToUnicode(value):
|
|||||||
"""
|
"""
|
||||||
retVal = value
|
retVal = value
|
||||||
|
|
||||||
if isinstance(value, int):
|
if isinstance(value, six.integer_types):
|
||||||
try:
|
try:
|
||||||
if value > 255:
|
if value > 255:
|
||||||
_ = "%x" % value
|
_ = "%x" % value
|
||||||
@@ -5138,12 +5138,12 @@ def getHostHeader(url):
|
|||||||
retVal = url
|
retVal = url
|
||||||
|
|
||||||
if url:
|
if url:
|
||||||
retVal = _urllib.parse.urlparse(url).netloc
|
parsed = _urllib.parse.urlparse(url)
|
||||||
|
retVal = parsed.netloc
|
||||||
|
defaultPort = {"http": 80, "https": 443, "ws": 80, "wss": 443}.get(parsed.scheme.lower())
|
||||||
|
|
||||||
# Note: netloc keeps the IPv6 brackets (and any port), so only the default ports are
|
# Keep non-default ports, including 80 on HTTPS and 443 on HTTP.
|
||||||
# stripped here - mirroring the hostname/IPv4 branch and preserving non-default ports
|
if defaultPort is not None and retVal.endswith(':%d' % defaultPort):
|
||||||
# (e.g. '[::1]:8080') as required by RFC 7230
|
|
||||||
if any(retVal.endswith(':%d' % _) for _ in (80, 443)):
|
|
||||||
retVal = retVal[:retVal.rfind(':')]
|
retVal = retVal[:retVal.rfind(':')]
|
||||||
|
|
||||||
if retVal and retVal.count(':') > 1 and not any(_ in retVal for _ in ('[', ']')):
|
if retVal and retVal.count(':') > 1 and not any(_ in retVal for _ in ('[', ']')):
|
||||||
@@ -5645,7 +5645,7 @@ def pollProcess(process, suppress_errors=False):
|
|||||||
|
|
||||||
def parseRequestFile(reqFile, checkParams=True):
|
def parseRequestFile(reqFile, checkParams=True):
|
||||||
"""
|
"""
|
||||||
Parses WebScarab and Burp logs and adds results to the target URL list
|
Parses WebScarab, Burp and HAR logs and adds results to the target URL list
|
||||||
|
|
||||||
>>> handle, reqFile = tempfile.mkstemp(suffix=".req")
|
>>> handle, reqFile = tempfile.mkstemp(suffix=".req")
|
||||||
>>> content = b"POST / HTTP/1.0\\nUser-agent: foobar\\nHost: www.example.com\\n\\nid=1\\n"
|
>>> content = b"POST / HTTP/1.0\\nUser-agent: foobar\\nHost: www.example.com\\n\\nid=1\\n"
|
||||||
@@ -5689,7 +5689,23 @@ def parseRequestFile(reqFile, checkParams=True):
|
|||||||
Parses Burp logs
|
Parses Burp logs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S):
|
if re.search(r"\A\s*\{.*\"entries\"", content[:1000], re.S):
|
||||||
|
reqResList = []
|
||||||
|
try:
|
||||||
|
entries = json.loads(content)["log"]["entries"]
|
||||||
|
except (ValueError, KeyError, TypeError):
|
||||||
|
entries = []
|
||||||
|
for entry in entries: # Note: HAR (HTTP Archive) logs (e.g. ZAP, browsers) - re-serialized into raw requests
|
||||||
|
try:
|
||||||
|
request = entry["request"]
|
||||||
|
url = request["url"]
|
||||||
|
headers = [(_["name"], _["value"]) for _ in request.get("headers", []) if not _["name"].startswith(':')]
|
||||||
|
if not any(_[0].upper() == HTTP_HEADER.HOST.upper() for _ in headers):
|
||||||
|
headers.insert(0, (HTTP_HEADER.HOST, _urllib.parse.urlsplit(url).netloc))
|
||||||
|
reqResList.append("%s %s HTTP/1.1\n%s\n\n%s" % (request["method"], url, "\n".join("%s: %s" % _ for _ in headers), (request.get("postData") or {}).get("text") or ""))
|
||||||
|
except (KeyError, TypeError, AttributeError):
|
||||||
|
continue
|
||||||
|
elif not re.search(BURP_REQUEST_REGEX, content, re.I | re.S):
|
||||||
if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
|
if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
|
||||||
reqResList = []
|
reqResList = []
|
||||||
for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
|
for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S):
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ def _serializeEncode(value):
|
|||||||
if isinstance(value, decimal.Decimal):
|
if isinstance(value, decimal.Decimal):
|
||||||
return {_SERIALIZE_TAG: "dec", "v": getUnicode(value)}
|
return {_SERIALIZE_TAG: "dec", "v": getUnicode(value)}
|
||||||
|
|
||||||
|
if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is not None:
|
||||||
|
return getUnicode(value) # Note: time zone aware value (e.g. from '-d' connector) kept as displayed, as an arbitrary tzinfo can't be rebuilt
|
||||||
|
|
||||||
if isinstance(value, datetime.datetime):
|
if isinstance(value, datetime.datetime):
|
||||||
return {_SERIALIZE_TAG: "dt", "v": [value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond]}
|
return {_SERIALIZE_TAG: "dt", "v": [value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond]}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from lib.core.enums import OS
|
|||||||
from thirdparty import six
|
from thirdparty import six
|
||||||
|
|
||||||
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
|
||||||
VERSION = "1.10.11.0"
|
VERSION = "1.10.11.8"
|
||||||
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
|
||||||
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
|
||||||
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
|
||||||
@@ -114,7 +114,7 @@ BOOLEAN_MODEL_MATCH_RATIO = 0.9
|
|||||||
PARAMETER_MINING_BUCKET_SIZE = 25
|
PARAMETER_MINING_BUCKET_SIZE = 25
|
||||||
|
|
||||||
# For filling in case of dumb push updates
|
# For filling in case of dumb push updates
|
||||||
DUMMY_JUNK = "Phah5jue"
|
DUMMY_JUNK = "rie8kooZ"
|
||||||
|
|
||||||
# Markers for special cases when parameter values contain html encoded characters
|
# Markers for special cases when parameter values contain html encoded characters
|
||||||
PARAMETER_AMP_MARKER = "__PARAMETER_AMP__"
|
PARAMETER_AMP_MARKER = "__PARAMETER_AMP__"
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ def cmdLineParser(argv=None):
|
|||||||
help="Connection string for direct database connection")
|
help="Connection string for direct database connection")
|
||||||
|
|
||||||
target.add_argument("-l", dest="logFile",
|
target.add_argument("-l", dest="logFile",
|
||||||
help="Parse target(s) from Burp or WebScarab proxy log file")
|
help="Parse target(s) from Burp, WebScarab or HAR log file")
|
||||||
|
|
||||||
target.add_argument("-m", dest="bulkFile",
|
target.add_argument("-m", dest="bulkFile",
|
||||||
help="Scan multiple targets given in a textual file")
|
help="Scan multiple targets given in a textual file")
|
||||||
|
|||||||
+11
-5
@@ -521,6 +521,8 @@ class Connect(object):
|
|||||||
|
|
||||||
# Prepare HTTP headers
|
# Prepare HTTP headers
|
||||||
headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host or getHeader(dict(conf.httpHeaders), HTTP_HEADER.HOST) or getHostHeader(url)}, base=None if target else {})
|
headers = forgeHeaders({HTTP_HEADER.COOKIE: cookie, HTTP_HEADER.USER_AGENT: ua, HTTP_HEADER.REFERER: referer, HTTP_HEADER.HOST: host or getHeader(dict(conf.httpHeaders), HTTP_HEADER.HOST) or getHostHeader(url)}, base=None if target else {})
|
||||||
|
# WebSocket builds its own default Host, but must preserve explicit overrides.
|
||||||
|
customWsHost = webSocket and bool(host or getHeader(dict(conf.httpHeaders), HTTP_HEADER.HOST) or getHeader(auxHeaders, HTTP_HEADER.HOST) is not None)
|
||||||
|
|
||||||
if HTTP_HEADER.COOKIE in headers:
|
if HTTP_HEADER.COOKIE in headers:
|
||||||
cookie = headers[HTTP_HEADER.COOKIE]
|
cookie = headers[HTTP_HEADER.COOKIE]
|
||||||
@@ -573,9 +575,13 @@ class Connect(object):
|
|||||||
if ':' in line:
|
if ':' in line:
|
||||||
header, value = line.split(':', 1)
|
header, value = line.split(':', 1)
|
||||||
headers[header] = value
|
headers[header] = value
|
||||||
|
if webSocket and header.upper() == HTTP_HEADER.HOST.upper():
|
||||||
|
headers[HTTP_HEADER.HOST] = value
|
||||||
|
customWsHost = True
|
||||||
|
|
||||||
if conf.localhost:
|
if conf.localhost:
|
||||||
headers[HTTP_HEADER.HOST] = "localhost"
|
headers[HTTP_HEADER.HOST] = "localhost"
|
||||||
|
customWsHost = True
|
||||||
|
|
||||||
for key, value in list(headers.items()):
|
for key, value in list(headers.items()):
|
||||||
if key.upper() == HTTP_HEADER.ACCEPT_ENCODING.upper():
|
if key.upper() == HTTP_HEADER.ACCEPT_ENCODING.upper():
|
||||||
@@ -600,7 +606,7 @@ class Connect(object):
|
|||||||
try:
|
try:
|
||||||
ws.settimeout(WEBSOCKET_INITIAL_TIMEOUT if kb.webSocketRecvCount is None else timeout)
|
ws.settimeout(WEBSOCKET_INITIAL_TIMEOUT if kb.webSocketRecvCount is None else timeout)
|
||||||
wsHeaders = tuple("%s: %s" % (getUnicode(key), getUnicode(value)) for key, value in headers.items() if getUnicode(key).upper() != HTTP_HEADER.HOST.upper())
|
wsHeaders = tuple("%s: %s" % (getUnicode(key), getUnicode(value)) for key, value in headers.items() if getUnicode(key).upper() != HTTP_HEADER.HOST.upper())
|
||||||
ws.connect(url, header=wsHeaders, cookie=cookie) # WebSocket will add Host field of headers automatically
|
ws.connect(url, header=wsHeaders, cookie=cookie, hostHeader=getHeader(headers, HTTP_HEADER.HOST) if customWsHost else None)
|
||||||
ws.send(urldecode(post or ""))
|
ws.send(urldecode(post or ""))
|
||||||
|
|
||||||
_page = []
|
_page = []
|
||||||
@@ -613,17 +619,17 @@ class Connect(object):
|
|||||||
warnMsg = "too large websocket response detected. Automatically trimming it"
|
warnMsg = "too large websocket response detected. Automatically trimming it"
|
||||||
singleTimeWarnMessage(warnMsg)
|
singleTimeWarnMessage(warnMsg)
|
||||||
break
|
break
|
||||||
except websocket.WebSocketTimeoutException:
|
except (websocket.WebSocketTimeoutException, websocket.WebSocketNormalClosureException):
|
||||||
kb.webSocketRecvCount = len(_page)
|
kb.webSocketRecvCount = len(_page)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
for i in xrange(max(1, kb.webSocketRecvCount)):
|
for i in xrange(max(1, kb.webSocketRecvCount)):
|
||||||
# Note: a later response may carry fewer frames than the calibration one
|
# Note: a later response may carry fewer frames than the calibration one
|
||||||
# (e.g. a FALSE boolean-blind reply); stop on timeout instead of letting it
|
# (e.g. a FALSE boolean-blind reply); stop on timeout or normal closure,
|
||||||
# bubble up as a failed request, mirroring the initial recv loop above
|
# mirroring the initial recv loop above
|
||||||
try:
|
try:
|
||||||
_page.append(ws.recv())
|
_page.append(ws.recv())
|
||||||
except websocket.WebSocketTimeoutException:
|
except (websocket.WebSocketTimeoutException, websocket.WebSocketNormalClosureException):
|
||||||
break
|
break
|
||||||
|
|
||||||
page = "\n".join(_page)
|
page = "\n".join(_page)
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ class WebSocketTimeoutException(WebSocketException):
|
|||||||
class WebSocketConnectionClosedException(WebSocketException):
|
class WebSocketConnectionClosedException(WebSocketException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class WebSocketNormalClosureException(WebSocketConnectionClosedException):
|
||||||
|
pass
|
||||||
|
|
||||||
class WebSocket(object):
|
class WebSocket(object):
|
||||||
"""
|
"""
|
||||||
Minimal RFC 6455 client exposing the websocket-client subset used by sqlmap: settimeout(), connect(),
|
Minimal RFC 6455 client exposing the websocket-client subset used by sqlmap: settimeout(), connect(),
|
||||||
@@ -58,7 +61,7 @@ class WebSocket(object):
|
|||||||
if self.sock is not None:
|
if self.sock is not None:
|
||||||
self.sock.settimeout(timeout)
|
self.sock.settimeout(timeout)
|
||||||
|
|
||||||
def connect(self, url, header=None, cookie=None):
|
def connect(self, url, header=None, cookie=None, hostHeader=None):
|
||||||
parts = _urllib.parse.urlsplit(url)
|
parts = _urllib.parse.urlsplit(url)
|
||||||
secure = parts.scheme == "wss"
|
secure = parts.scheme == "wss"
|
||||||
host = parts.hostname
|
host = parts.hostname
|
||||||
@@ -73,8 +76,10 @@ class WebSocket(object):
|
|||||||
self.sock.settimeout(self._timeout)
|
self.sock.settimeout(self._timeout)
|
||||||
|
|
||||||
hostport = "[%s]" % host if ":" in host else host # bracket IPv6 literals
|
hostport = "[%s]" % host if ":" in host else host # bracket IPv6 literals
|
||||||
if port not in (80, 443):
|
if port != (443 if secure else 80): # Note: only the scheme's own default port gets omitted
|
||||||
hostport = "%s:%d" % (hostport, port)
|
hostport = "%s:%d" % (hostport, port)
|
||||||
|
if hostHeader is not None:
|
||||||
|
hostport = getText(hostHeader)
|
||||||
|
|
||||||
key = getText(base64.b64encode(os.urandom(16)))
|
key = getText(base64.b64encode(os.urandom(16)))
|
||||||
lines = ["GET %s HTTP/1.1" % resource,
|
lines = ["GET %s HTTP/1.1" % resource,
|
||||||
@@ -157,10 +162,14 @@ class WebSocket(object):
|
|||||||
|
|
||||||
def recv(self):
|
def recv(self):
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
|
receivedFragment = False
|
||||||
while True:
|
while True:
|
||||||
fin, opcode, payload = self._recvFrame()
|
fin, opcode, payload = self._recvFrame()
|
||||||
if opcode == OPCODE_CLOSE:
|
if opcode == OPCODE_CLOSE:
|
||||||
self._closed = True
|
self._closed = True
|
||||||
|
normal = not payload or (len(payload) >= 2 and struct.unpack("!H", bytes(payload[:2]))[0] in (1000, 1001))
|
||||||
|
if normal and not receivedFragment:
|
||||||
|
raise WebSocketNormalClosureException("WebSocket connection closed")
|
||||||
raise WebSocketConnectionClosedException("WebSocket connection closed")
|
raise WebSocketConnectionClosedException("WebSocket connection closed")
|
||||||
elif opcode == OPCODE_PING:
|
elif opcode == OPCODE_PING:
|
||||||
self._sendFrame(bytes(payload), OPCODE_PONG)
|
self._sendFrame(bytes(payload), OPCODE_PONG)
|
||||||
@@ -169,6 +178,7 @@ class WebSocket(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
data += payload
|
data += payload
|
||||||
|
receivedFragment = True
|
||||||
if fin:
|
if fin:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -36,10 +36,10 @@ class HTTPCollector(object):
|
|||||||
def setExtendedArguments(self, arguments):
|
def setExtendedArguments(self, arguments):
|
||||||
self.extendedArguments = arguments
|
self.extendedArguments = arguments
|
||||||
|
|
||||||
def collectRequest(self, requestMessage, responseMessage, startTime=None, endTime=None):
|
def collectRequest(self, requestMessage, responseMessage, startTime=None, endTime=None, scheme=None):
|
||||||
self.messages.append(RawPair(requestMessage, responseMessage,
|
self.messages.append(RawPair(requestMessage, responseMessage,
|
||||||
startTime=startTime, endTime=endTime,
|
startTime=startTime, endTime=endTime,
|
||||||
extendedArguments=self.extendedArguments))
|
extendedArguments=self.extendedArguments, scheme=scheme))
|
||||||
|
|
||||||
def obtain(self):
|
def obtain(self):
|
||||||
return {"log": {
|
return {"log": {
|
||||||
@@ -49,15 +49,16 @@ class HTTPCollector(object):
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
class RawPair(object):
|
class RawPair(object):
|
||||||
def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None):
|
def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None, scheme=None):
|
||||||
self.request = getBytes(request)
|
self.request = getBytes(request)
|
||||||
self.response = getBytes(response)
|
self.response = getBytes(response)
|
||||||
self.startTime = startTime
|
self.startTime = startTime
|
||||||
self.endTime = endTime
|
self.endTime = endTime
|
||||||
self.extendedArguments = extendedArguments or {}
|
self.extendedArguments = extendedArguments or {}
|
||||||
|
self.scheme = scheme # Note: of the target at the time of the request (as origin-form request lines don't carry one)
|
||||||
|
|
||||||
def toEntry(self):
|
def toEntry(self):
|
||||||
return Entry(request=Request.parse(self.request), response=Response.parse(self.response),
|
return Entry(request=Request.parse(self.request, self.scheme), response=Response.parse(self.response),
|
||||||
startTime=self.startTime, endTime=self.endTime,
|
startTime=self.startTime, endTime=self.endTime,
|
||||||
extendedArguments=self.extendedArguments)
|
extendedArguments=self.extendedArguments)
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ class Entry(object):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
class Request(object):
|
class Request(object):
|
||||||
def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None):
|
def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None, scheme=None):
|
||||||
self.method = method
|
self.method = method
|
||||||
self.path = path
|
self.path = path
|
||||||
self.httpVersion = httpVersion
|
self.httpVersion = httpVersion
|
||||||
@@ -94,9 +95,10 @@ class Request(object):
|
|||||||
self.postBody = postBody
|
self.postBody = postBody
|
||||||
self.comment = comment.strip() if comment else comment
|
self.comment = comment.strip() if comment else comment
|
||||||
self.raw = raw
|
self.raw = raw
|
||||||
|
self.scheme = scheme
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, raw):
|
def parse(cls, raw, scheme=None):
|
||||||
request = HTTPRequest(raw)
|
request = HTTPRequest(raw)
|
||||||
return cls(method=request.command,
|
return cls(method=request.command,
|
||||||
path=request.path,
|
path=request.path,
|
||||||
@@ -104,12 +106,16 @@ class Request(object):
|
|||||||
headers=request.headers,
|
headers=request.headers,
|
||||||
postBody=request.rfile.read(),
|
postBody=request.rfile.read(),
|
||||||
comment=request.comment,
|
comment=request.comment,
|
||||||
raw=raw)
|
raw=raw,
|
||||||
|
scheme=scheme)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
if re.search(r"(?i)\Ahttps?://", self.path or ""): # Note: absolute-form request target (e.g. refreshing/crawling/checking requests)
|
||||||
|
return self.path
|
||||||
|
|
||||||
host = self.headers.get("Host", "unknown")
|
host = self.headers.get("Host", "unknown")
|
||||||
return "http://%s%s" % (host, self.path)
|
return "%s://%s%s" % (self.scheme or "http", host, self.path)
|
||||||
|
|
||||||
def toDict(self):
|
def toDict(self):
|
||||||
out = {
|
out = {
|
||||||
|
|||||||
+2
-2
@@ -12,10 +12,10 @@ url =
|
|||||||
# oracle://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_SID
|
# oracle://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_SID
|
||||||
direct =
|
direct =
|
||||||
|
|
||||||
# Parse targets from Burp or WebScarab logs
|
# Parse targets from Burp, WebScarab or HAR logs
|
||||||
# Valid: Burp proxy (http://portswigger.net/suite/) requests log file path
|
# Valid: Burp proxy (http://portswigger.net/suite/) requests log file path
|
||||||
# or WebScarab proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project)
|
# or WebScarab proxy (http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project)
|
||||||
# 'conversations/' folder path
|
# 'conversations/' folder path or HAR (HTTP Archive) file path
|
||||||
logFile =
|
logFile =
|
||||||
|
|
||||||
# Scan multiple targets enlisted in a given textual file
|
# Scan multiple targets enlisted in a given textual file
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ stdlib unittest only (no pytest / no pip); works on Python 2.7 and 3.x.
|
|||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
@@ -537,6 +538,22 @@ class TestParseRequestFileBurp(unittest.TestCase):
|
|||||||
self.assertEqual([_[0] for _ in targets], ["https://www.target.com:8443/vuln.php?id=1", "http://www.target.com:443/vuln.php?id=1", "https://www.target.com:443/vuln.php?id=1", "http://www.target.com:8080/vuln.php?id=1"])
|
self.assertEqual([_[0] for _ in targets], ["https://www.target.com:8443/vuln.php?id=1", "http://www.target.com:443/vuln.php?id=1", "https://www.target.com:443/vuln.php?id=1", "http://www.target.com:8080/vuln.php?id=1"])
|
||||||
self.assertTrue(all("://" not in value for _ in targets for key, value in _[4] if key == "Host"))
|
self.assertTrue(all("://" not in value for _ in targets for key, value in _[4] if key == "Host"))
|
||||||
|
|
||||||
|
def test_har_log(self):
|
||||||
|
# Note: HAR (HTTP Archive) logs (e.g. ZAP, browsers), including HTTP/2 entries (pseudo-headers, no Host)
|
||||||
|
entries = [
|
||||||
|
{"request": {"method": "GET", "url": "https://www.target.com:8443/vuln.php?id=1", "headers": [{"name": "Host", "value": "www.target.com:8443"}, {"name": "Cookie", "value": "SID=xyz"}]}},
|
||||||
|
{"request": {"method": "POST", "url": "https://www.target.com/api", "headers": [{"name": ":authority", "value": "www.target.com"}, {"name": "content-type", "value": "application/json"}], "postData": {"text": '{"id": 1}'}}},
|
||||||
|
{"request": {"method": "GET", "url": "https://www.target.com/logo.png", "headers": []}},
|
||||||
|
]
|
||||||
|
path = _write_temp(json.dumps({"log": {"version": "1.2", "entries": entries}}), ".har")
|
||||||
|
try:
|
||||||
|
targets = list(parseRequestFile(path))
|
||||||
|
finally:
|
||||||
|
os.unlink(path)
|
||||||
|
|
||||||
|
self.assertEqual([_[:4] for _ in targets], [("https://www.target.com:8443/vuln.php?id=1", HTTPMETHOD.GET, None, "SID=xyz"), ("https://www.target.com/api", HTTPMETHOD.POST, '{"id": 1}', None)])
|
||||||
|
self.assertEqual(targets[1][4], (("Host", "www.target.com"), ("content-type", "application/json")))
|
||||||
|
|
||||||
def test_post_body_captured(self):
|
def test_post_body_captured(self):
|
||||||
content = (
|
content = (
|
||||||
"======================================================\n"
|
"======================================================\n"
|
||||||
|
|||||||
@@ -70,6 +70,21 @@ class TestGetHostHeader(unittest.TestCase):
|
|||||||
def test_without_port(self):
|
def test_without_port(self):
|
||||||
self.assertEqual(getHostHeader("http://example.com/path"), "example.com")
|
self.assertEqual(getHostHeader("http://example.com/path"), "example.com")
|
||||||
|
|
||||||
|
def test_default_ports_depend_on_scheme(self):
|
||||||
|
cases = (
|
||||||
|
("http://example.com:80/path", "example.com"),
|
||||||
|
("https://example.com:443/path", "example.com"),
|
||||||
|
("http://example.com:443/path", "example.com:443"),
|
||||||
|
("https://example.com:80/path", "example.com:80"),
|
||||||
|
("HTTP://example.com:443/path", "example.com:443"),
|
||||||
|
("http://[::1]:80/path", "[::1]"),
|
||||||
|
("https://[::1]:443/path", "[::1]"),
|
||||||
|
("http://[::1]:443/path", "[::1]:443"),
|
||||||
|
("https://[::1]:80/path", "[::1]:80"),
|
||||||
|
)
|
||||||
|
for url, expected in cases:
|
||||||
|
self.assertEqual(getHostHeader(url), expected, msg=url)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
@@ -52,6 +52,14 @@ class TestHTTPCollector(unittest.TestCase):
|
|||||||
self.assertEqual(entries[0]["request"]["method"], "GET")
|
self.assertEqual(entries[0]["request"]["method"], "GET")
|
||||||
self.assertEqual(entries[0]["response"]["status"], 200)
|
self.assertEqual(entries[0]["response"]["status"], 200)
|
||||||
|
|
||||||
|
def test_request_url_scheme(self):
|
||||||
|
# Note: origin-form request lines carry no scheme (HTTPS targets used to be logged as 'http://'), while
|
||||||
|
# absolute-form ones (refreshing/crawling/checking requests) used to get the Host prepended to them
|
||||||
|
c = H.HTTPCollector()
|
||||||
|
for line, scheme in (("GET /?id=1", "https"), ("GET /?id=1", None), ("GET https://other.com:8443/x?id=1", "http")):
|
||||||
|
c.collectRequest(("%s HTTP/1.1\r\nHost: example.com\r\n\r\n" % line).encode(), b"HTTP/1.1 200 OK\r\n\r\n", startTime=1000, endTime=2000, scheme=scheme)
|
||||||
|
self.assertEqual([_["request"]["url"] for _ in c.obtain()["log"]["entries"]], ["https://example.com/?id=1", "http://example.com/?id=1", "https://other.com:8443/x?id=1"])
|
||||||
|
|
||||||
|
|
||||||
class TestHTTPCollectorFactory(unittest.TestCase):
|
class TestHTTPCollectorFactory(unittest.TestCase):
|
||||||
def test_create_returns_collector(self):
|
def test_create_returns_collector(self):
|
||||||
|
|||||||
@@ -259,6 +259,19 @@ class TestDbmsScalars(unittest.TestCase):
|
|||||||
self.assertEqual(restored, value)
|
self.assertEqual(restored, value)
|
||||||
self.assertIs(type(restored), type(value))
|
self.assertIs(type(restored), type(value))
|
||||||
|
|
||||||
|
def test_timezone_aware_kept_as_displayed(self):
|
||||||
|
# Note: a resumed '-d' session used to drop the offset (e.g. '+05:30'), as only naive fields got stored
|
||||||
|
class _Offset(datetime.tzinfo):
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
return datetime.timedelta(hours=5, minutes=30)
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
return datetime.timedelta(0)
|
||||||
|
|
||||||
|
for value in (datetime.datetime(2024, 1, 2, 3, 4, 0, tzinfo=_Offset()), datetime.time(3, 4, 0, tzinfo=_Offset())):
|
||||||
|
self.assertEqual(rt(value), getUnicode(value))
|
||||||
|
self.assertTrue(rt(value).endswith("+05:30"))
|
||||||
|
|
||||||
|
|
||||||
class TestRealSessionObjects(unittest.TestCase):
|
class TestRealSessionObjects(unittest.TestCase):
|
||||||
# the exact shapes written with serialize=True across the codebase
|
# the exact shapes written with serialize=True across the codebase
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ from lib.request.websocket import (
|
|||||||
OPCODE_PING,
|
OPCODE_PING,
|
||||||
OPCODE_CLOSE,
|
OPCODE_CLOSE,
|
||||||
)
|
)
|
||||||
|
from lib.request.connect import Connect
|
||||||
|
|
||||||
|
_GET_PAGE = Connect.__dict__["getPage"]
|
||||||
|
|
||||||
|
|
||||||
class _FakeSock(object):
|
class _FakeSock(object):
|
||||||
@@ -133,6 +136,117 @@ class TestWebSocket(unittest.TestCase):
|
|||||||
ws = _client(); ws.sock = _RaisingSock(_ssl.SSLError("decryption failed"))
|
ws = _client(); ws.sock = _RaisingSock(_ssl.SSLError("decryption failed"))
|
||||||
self.assertRaises(_ssl.SSLError, ws.recv)
|
self.assertRaises(_ssl.SSLError, ws.recv)
|
||||||
|
|
||||||
|
def test_handshake_host_port(self):
|
||||||
|
# Note: only the scheme's own default port is omitted (port 443 used to be dropped for 'ws://' too)
|
||||||
|
import lib.request.websocket as W
|
||||||
|
|
||||||
|
class _Sent(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class _Capture(_FakeSock):
|
||||||
|
def settimeout(self, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def sendall(self, data):
|
||||||
|
raise _Sent(data)
|
||||||
|
|
||||||
|
original = W.socket.create_connection
|
||||||
|
try:
|
||||||
|
W.socket.create_connection = lambda *args, **kwargs: _Capture()
|
||||||
|
for url, host in (("ws://example.com:443/x", "example.com:443"), ("ws://example.com:80/x", "example.com"), ("ws://example.com:8080/x", "example.com:8080")):
|
||||||
|
ws = WebSocket.__new__(WebSocket)
|
||||||
|
ws._timeout = None
|
||||||
|
try:
|
||||||
|
ws.connect(url)
|
||||||
|
except _Sent as ex:
|
||||||
|
self.assertIn(("Host: %s\r\n" % host).encode(), ex.args[0])
|
||||||
|
else:
|
||||||
|
self.fail("handshake was not sent")
|
||||||
|
finally:
|
||||||
|
W.socket.create_connection = original
|
||||||
|
|
||||||
|
def test_getpage_preserves_host_header(self):
|
||||||
|
import socket
|
||||||
|
import tempfile
|
||||||
|
import lib.request.websocket as W
|
||||||
|
from lib.core.data import conf, kb
|
||||||
|
from lib.core.option import _setKnowledgeBaseAttributes
|
||||||
|
from lib.core.threads import getCurrentThreadData
|
||||||
|
|
||||||
|
class _HandshakeSock(_FakeSock):
|
||||||
|
def __init__(self):
|
||||||
|
_FakeSock.__init__(self)
|
||||||
|
self.handshake = None
|
||||||
|
self.response = b""
|
||||||
|
|
||||||
|
def settimeout(self, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def sendall(self, data):
|
||||||
|
if self.handshake is None:
|
||||||
|
self.handshake = data
|
||||||
|
key = next(line.split(b":", 1)[1].strip() for line in data.split(b"\r\n") if line.lower().startswith(b"sec-websocket-key:"))
|
||||||
|
accept = base64.b64encode(hashlib.sha1(key + _GUID.encode("ascii")).digest())
|
||||||
|
self.response = b"HTTP/1.1 101 Switching Protocols\r\nSec-WebSocket-Accept: " + accept + b"\r\n\r\n" + _serverFrame("ok")
|
||||||
|
|
||||||
|
def recv(self, size):
|
||||||
|
result, self.response = self.response[:size], self.response[size:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
sockets = []
|
||||||
|
def create_socket(*args, **kwargs):
|
||||||
|
sock = _HandshakeSock()
|
||||||
|
sockets.append(sock)
|
||||||
|
return sock
|
||||||
|
|
||||||
|
oldConf, oldKb = dict(conf), dict(kb)
|
||||||
|
threadData = getCurrentThreadData()
|
||||||
|
oldThreadData = dict(threadData.__dict__)
|
||||||
|
oldTimeout = socket.getdefaulttimeout()
|
||||||
|
original = W.socket.create_connection
|
||||||
|
oldGetPage = Connect.__dict__["getPage"]
|
||||||
|
fd, headersFile = tempfile.mkstemp(prefix="sqlmap-ws-headers-")
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, "w") as f:
|
||||||
|
f.write("hOsT: file.example\n")
|
||||||
|
threadData.__dict__.clear()
|
||||||
|
threadData.reset()
|
||||||
|
_setKnowledgeBaseAttributes(False)
|
||||||
|
kb.wizardMode, kb.webSocketRecvCount = True, 1
|
||||||
|
conf.url = "ws://example.test:8080/test"
|
||||||
|
conf.hostname, conf.scheme, conf.port, conf.path = "example.test", "ws", 8080, "/test"
|
||||||
|
conf.parameters, conf.data = {}, None
|
||||||
|
conf.abortCode, conf.ignoreCode = [], []
|
||||||
|
conf.timeout, conf.batch, conf.keepAlive = 3, True, False
|
||||||
|
conf.retryOn, conf.ignoreRedirects, conf.scope = None, False, None
|
||||||
|
W.socket.create_connection = create_socket
|
||||||
|
Connect.getPage = _GET_PAGE
|
||||||
|
cases = (
|
||||||
|
("ws://example.test:8080/test", [], None, None, None, False, "example.test:8080"),
|
||||||
|
("ws://user:[email protected]:8080/test", [], None, None, None, False, "example.test:8080"),
|
||||||
|
("ws://example.test:8080/test", [('hOsT', 'configured.example')], None, None, None, False, "configured.example"),
|
||||||
|
("ws://example.test:8080/test", [('Host', 'configured.example')], "override.example:8443", None, None, False, "override.example:8443"),
|
||||||
|
("ws://example.test:8080/test", [], None, {'Host': 'aux.example'}, None, False, "aux.example"),
|
||||||
|
("ws://example.test:8080/test", [], None, None, headersFile, False, "file.example"),
|
||||||
|
("ws://example.test:8080/test", [], None, None, None, True, "localhost"),
|
||||||
|
)
|
||||||
|
for url, headers, host, aux, path, localhost, expected in cases:
|
||||||
|
conf.url = url
|
||||||
|
conf.httpHeaders = headers
|
||||||
|
conf.localhost, kb.headersFile = localhost, path
|
||||||
|
self.assertEqual(Connect.getPage(url=url, host=host, auxHeaders=aux)[0], "ok")
|
||||||
|
hostLines = [line for line in sockets[-1].handshake.split(b"\r\n") if line.lower().startswith(b"host:")]
|
||||||
|
self.assertEqual(len(hostLines), 1)
|
||||||
|
self.assertEqual(hostLines[0].split(b":", 1)[1].strip(), expected.encode("ascii"))
|
||||||
|
finally:
|
||||||
|
Connect.getPage = oldGetPage
|
||||||
|
W.socket.create_connection = original
|
||||||
|
socket.setdefaulttimeout(oldTimeout)
|
||||||
|
conf.clear(); conf.update(oldConf)
|
||||||
|
kb.clear(); kb.update(oldKb)
|
||||||
|
threadData.__dict__.clear(); threadData.__dict__.update(oldThreadData)
|
||||||
|
os.unlink(headersFile)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
||||||
|
See the file 'LICENSE' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
from _testutils import bootstrap
|
||||||
|
bootstrap()
|
||||||
|
|
||||||
|
from lib.core.data import conf, kb
|
||||||
|
from lib.core.exception import SqlmapConnectionException
|
||||||
|
from lib.core.option import _setKnowledgeBaseAttributes
|
||||||
|
from lib.core.threads import getCurrentThreadData
|
||||||
|
from lib.request.connect import Connect
|
||||||
|
import lib.request.websocket as websocket
|
||||||
|
|
||||||
|
_GET_PAGE = Connect.__dict__["getPage"]
|
||||||
|
|
||||||
|
|
||||||
|
def _frame(payload, opcode=1, fin=1):
|
||||||
|
return bytes(bytearray([(fin << 7) | opcode, len(payload)])) + payload
|
||||||
|
|
||||||
|
|
||||||
|
class _Socket(object):
|
||||||
|
def __init__(self, frames):
|
||||||
|
self.frames = frames
|
||||||
|
self.response = b""
|
||||||
|
self.handshake = None
|
||||||
|
|
||||||
|
def settimeout(self, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def sendall(self, data):
|
||||||
|
if self.handshake is None:
|
||||||
|
self.handshake = data
|
||||||
|
key = next(line.split(b":", 1)[1].strip() for line in data.split(b"\r\n") if line.lower().startswith(b"sec-websocket-key:"))
|
||||||
|
accept = base64.b64encode(hashlib.sha1(key + websocket._GUID.encode("ascii")).digest())
|
||||||
|
self.response = b"HTTP/1.1 101 Switching Protocols\r\nSec-WebSocket-Accept: " + accept + b"\r\n\r\n" + self.frames
|
||||||
|
|
||||||
|
def recv(self, size):
|
||||||
|
result, self.response = self.response[:size], self.response[size:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebSocketResponseClose(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.oldConf, self.oldKb = dict(conf), dict(kb)
|
||||||
|
self.threadData = getCurrentThreadData()
|
||||||
|
self.oldThreadData = dict(self.threadData.__dict__)
|
||||||
|
self.oldTimeout = socket.getdefaulttimeout()
|
||||||
|
self.oldSocket = websocket.socket.create_connection
|
||||||
|
self.oldGetPage = Connect.__dict__["getPage"]
|
||||||
|
self.threadData.__dict__.clear()
|
||||||
|
self.threadData.reset()
|
||||||
|
_setKnowledgeBaseAttributes(False)
|
||||||
|
kb.wizardMode = True
|
||||||
|
conf.url = "ws://example.test:8080/test"
|
||||||
|
conf.hostname, conf.scheme, conf.port, conf.path = "example.test", "ws", 8080, "/test"
|
||||||
|
conf.httpHeaders, conf.parameters, conf.data = [], {}, None
|
||||||
|
conf.abortCode, conf.ignoreCode = [], []
|
||||||
|
conf.timeout, conf.batch, conf.keepAlive, conf.retries = 3, True, False, 0
|
||||||
|
conf.retryOn, conf.ignoreRedirects, conf.scope = None, False, None
|
||||||
|
Connect.getPage = _GET_PAGE
|
||||||
|
websocket.socket.create_connection = lambda *args, **kwargs: _Socket(self.frames)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
Connect.getPage = self.oldGetPage
|
||||||
|
websocket.socket.create_connection = self.oldSocket
|
||||||
|
socket.setdefaulttimeout(self.oldTimeout)
|
||||||
|
conf.clear(); conf.update(self.oldConf)
|
||||||
|
kb.clear(); kb.update(self.oldKb)
|
||||||
|
self.threadData.__dict__.clear(); self.threadData.__dict__.update(self.oldThreadData)
|
||||||
|
|
||||||
|
def test_normal_close_after_response(self):
|
||||||
|
for status in (b"", struct.pack("!H", 1000), struct.pack("!H", 1001)):
|
||||||
|
for count in (None, 3):
|
||||||
|
self.frames = _frame(b"one") + _frame(b"two") + _frame(status, opcode=8)
|
||||||
|
kb.webSocketRecvCount = count
|
||||||
|
self.assertEqual(Connect.getPage(url=conf.url)[0], "one\ntwo")
|
||||||
|
if count is None:
|
||||||
|
self.assertEqual(kb.webSocketRecvCount, 2)
|
||||||
|
|
||||||
|
def test_normal_close_without_response(self):
|
||||||
|
self.frames = _frame(struct.pack("!H", 1000), opcode=8)
|
||||||
|
kb.webSocketRecvCount = None
|
||||||
|
self.assertEqual(Connect.getPage(url=conf.url)[0], "")
|
||||||
|
self.assertEqual(kb.webSocketRecvCount, 0)
|
||||||
|
|
||||||
|
def test_abnormal_close_remains_an_error(self):
|
||||||
|
for status in (b"\x03", struct.pack("!H", 1002), struct.pack("!H", 1011)):
|
||||||
|
self.frames = _frame(b"one") + _frame(status, opcode=8)
|
||||||
|
for count in (None, 3):
|
||||||
|
kb.webSocketRecvCount = count
|
||||||
|
self.assertRaises(SqlmapConnectionException, Connect.getPage, url=conf.url)
|
||||||
|
|
||||||
|
def test_truncated_frame_remains_an_error(self):
|
||||||
|
for frames in (_frame(b"one"), _frame(b"one") + b"\x81\x05ab"):
|
||||||
|
self.frames = frames
|
||||||
|
for count in (None, 3):
|
||||||
|
kb.webSocketRecvCount = count
|
||||||
|
self.assertRaises(SqlmapConnectionException, Connect.getPage, url=conf.url)
|
||||||
|
|
||||||
|
def test_close_during_fragmented_message_remains_an_error(self):
|
||||||
|
for fragment in (b"", b"partial"):
|
||||||
|
self.frames = _frame(fragment, fin=0) + _frame(struct.pack("!H", 1000), opcode=8)
|
||||||
|
kb.webSocketRecvCount = None
|
||||||
|
self.assertRaises(SqlmapConnectionException, Connect.getPage, url=conf.url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user