SentinelScan-WSS / backend /scanners /http_method_tampering_scanner.py
larxius's picture
Deploy SentinelScan WSS to HF Spaces
d543fc1 verified
Raw
History Blame Contribute Delete
11.5 kB
"""
http_method_tampering_scanner.py — HTTP Method Tampering Scanner
=================================================================
Tests all HTTP methods (TRACE, PUT, DELETE, PATCH, OPTIONS, CONNECT) on
discovered endpoints. TRACE enables XST, PUT can write files.
Also tests HTTP method override headers.
"""
from scanners.base_scanner import BaseScanner
DANGEROUS_METHODS = {
"TRACE": ("High", "Enables Cross-Site Tracing (XST) — reflects HTTP headers including cookies/auth tokens back to the client. Can be used to bypass HttpOnly cookie protection via JavaScript."),
"PUT": ("Critical","HTTP PUT on a web server may allow direct file upload, enabling Remote Code Execution (e.g., uploading a webshell)."),
"DELETE": ("High", "HTTP DELETE allowed. An attacker could delete files or resources if the web server maps it to the filesystem."),
"CONNECT": ("Medium", "CONNECT method may allow the server to be used as an HTTP proxy for SSRF or network pivoting."),
"PATCH": ("Medium", "HTTP PATCH allowed. May allow partial modification of server-side resources."),
"PROPFIND":("Medium", "WebDAV PROPFIND allowed. May leak file listings and metadata."),
"MOVE": ("High", "WebDAV MOVE allowed. An attacker can rename/move files on the server."),
"COPY": ("Medium", "WebDAV COPY allowed. An attacker can copy files on the server."),
"MKCOL": ("Medium", "WebDAV MKCOL allowed. An attacker can create directories on the server."),
}
# Curated list of HTTP method override header name variants used in real WAF bypass scenarios.
# Only valid HTTP header name chars (printable ASCII, no control chars) are used.
# Do NOT use waf_evade() here — it generates URL-encoded / control-char variants
# that urllib rejects as invalid header names.
OVERRIDE_HEADERS = [
# Standard override headers
"X-HTTP-Method-Override",
"X-HTTP-Method",
"X-Method-Override",
# Case variations (common WAF bypass)
"x-http-method-override",
"x-http-method",
"x-method-override",
"X-Http-Method-Override",
"X-Http-Method",
# Additional known override header aliases
"X-Forwarded-Method",
"X-Original-Method",
"X-Rewrite-Method",
"XHTTP-Method",
]
class HttpMethodTamperingScanner(BaseScanner):
SCANNER_NAME = "HTTP Method Tampering Scanner"
_SCANNER_KEY = "http_method_tampering"
def __init__(self, scan_id, target, domain, **kwargs):
super().__init__(scan_id, target, domain, **kwargs)
def run(self) -> list:
self.log("INFO", f"[HTTPMethod] Testing dangerous HTTP methods on {self.target}...")
advertised = self._get_options_methods()
if advertised:
self.log("INFO", f"[HTTPMethod] OPTIONS Allow header: {advertised}")
for method, (severity, description) in DANGEROUS_METHODS.items():
status, headers, body = self._send_method(method, self.target)
if status and status not in (405, 501, 400, 0):
cwe = ["CWE-749"]
owasp = "A05:2021 – Security Misconfiguration"
if method == "TRACE" and body and "TRACE" in body.upper():
self.add_vuln(
title="TRACE Method Enabled — Cross-Site Tracing (XST) Risk",
severity=severity,
category="HTTP Method Tampering",
cvss_score=7.4,
description=f"The server responded to a TRACE request with HTTP {status} "
f"and reflected the request body back. {description}",
remediation="Disable TRACE globally: Apache: `TraceEnable Off` | "
"Nginx: `map $request_method $block {{ TRACE 1; }} if ($block) {{ return 405; }}`",
evidence=f"TRACE request reflected body, status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
elif method == "PUT":
self.add_vuln(
title="HTTP PUT Method Accepted",
severity=severity,
category="HTTP Method Tampering",
cvss_score=9.1,
description=f"Server responded HTTP {status} to a PUT request. {description}",
remediation="Disable PUT in web server config unless explicitly required by the API. "
"If required, restrict to authenticated endpoints only.",
evidence=f"PUT request accepted with status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
elif method == "DELETE":
self.add_vuln(
title="HTTP DELETE Method Accepted",
severity=severity,
category="HTTP Method Tampering",
cvss_score=7.5,
description=f"Server responded HTTP {status} to a DELETE request. {description}",
remediation="Restrict DELETE to authenticated, authorized API routes. "
"Disable WebDAV if not needed.",
evidence=f"DELETE request accepted with status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
elif method == "CONNECT":
self.add_vuln(
title="HTTP CONNECT Method Accepted",
severity=severity,
category="HTTP Method Tampering",
cvss_score=5.3,
description=f"Server responded HTTP {status} to CONNECT. {description}",
remediation="Block CONNECT at the web server level unless operating a proxy.",
evidence=f"CONNECT request accepted with status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
elif method == "PATCH":
self.add_vuln(
title="HTTP PATCH Method Accepted",
severity=severity,
category="HTTP Method Tampering",
cvss_score=5.3,
description=f"Server responded HTTP {status} to a PATCH request. {description}",
remediation="Restrict PATCH to authenticated API routes with proper input validation.",
evidence=f"PATCH request accepted with status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
elif method in ("PROPFIND", "MOVE", "COPY", "MKCOL"):
self.add_vuln(
title=f"HTTP {method} Method Accepted (WebDAV)",
severity=severity,
category="HTTP Method Tampering",
cvss_score=6.5 if method == "MOVE" else 5.3,
description=f"Server responded HTTP {status} to {method}. {description}",
remediation="Disable WebDAV methods unless explicitly required. "
"Restrict to authenticated API routes with proper input validation.",
evidence=f"{method} request accepted with status {status}",
confidence="Confirmed",
cwe_ids=cwe,
owasp_category=owasp,
)
self.log("WARNING", f"[HTTPMethod] {method} → HTTP {status}")
if advertised and any(m in advertised for m in ["TRACE", "PUT", "DELETE"]):
self.add_vuln(
title="Dangerous Methods Advertised in OPTIONS Response",
severity="Medium",
category="HTTP Method Tampering",
cvss_score=5.3,
description=f"OPTIONS Allow header reveals: `{advertised}`. Dangerous methods are publicly advertised.",
remediation="Filter the Allow header to only expose methods actually required.",
evidence=f"Allow header: {advertised}",
confidence="Confirmed",
cwe_ids=["CWE-749"],
owasp_category="A05:2021 – Security Misconfiguration",
)
self._test_method_override_headers()
if not self.vulns:
self.log("SUCCESS", "[HTTPMethod] No dangerous HTTP methods accepted.")
return self.vulns
def _get_options_methods(self):
body, status, headers = self._make_request(
self.target, method="OPTIONS", return_response_obj=True
)
if status == 0:
return ""
if isinstance(headers, dict):
return headers.get("Allow", headers.get("allow", ""))
return getattr(headers, "get", lambda x, y="": y)("Allow", "")
def _send_method(self, method, url):
data = b"WSS-probe" if method in ("PUT", "PATCH") else None
body, status, headers = self._make_request(
url, method=method, data=data, return_response_obj=True
)
return status, headers if isinstance(headers, dict) else {}, body or ""
def _test_method_override_headers(self):
for header_name in OVERRIDE_HEADERS:
for override_method in ["PUT", "DELETE", "PATCH", "TRACE"]:
body, status = self._make_request(
self.target,
method="POST",
data=b"WSS-override-test",
headers={"Content-Type": "application/x-www-form-urlencoded", header_name: override_method}
)
if status and status not in (405, 501, 400, 404, 0):
self.add_vuln(
title=f"HTTP Method Override via `{header_name}` — {override_method}",
severity="Medium",
category="HTTP Method Tampering",
cvss_score=6.1,
description=f"Server accepted `{override_method}` via `{header_name}: {override_method}` header. "
f"Response status: {status}. HTTP method override headers can bypass access controls "
f"that only check the HTTP method.",
remediation="Disable HTTP method override headers at the reverse proxy "
"unless explicitly required. Validate the actual HTTP method, not the override header.",
evidence=f"{header_name}: {override_method} → HTTP {status}",
confidence="High",
cwe_ids=["CWE-749"],
owasp_category="A05:2021 – Security Misconfiguration",
)