Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """ | |
| http_pollution_scanner.py — HTTP Parameter Pollution Scanner | |
| """ | |
| import urllib.request, urllib.error, urllib.parse | |
| from scanners.base_scanner import BaseScanner | |
| class HttpPollutionScanner(BaseScanner): | |
| SCANNER_NAME = "HTTP Parameter Pollution Scanner" | |
| _SCANNER_KEY = "http_pollution" | |
| def __init__(self, scan_id, target, domain, **kwargs): | |
| super().__init__(scan_id, target, domain, **kwargs) | |
| def run(self) -> list: | |
| self.log("INFO", f"[HPP] Testing HTTP Parameter Pollution on {self.target}...") | |
| parsed = urllib.parse.urlparse(self.target) | |
| qs = urllib.parse.parse_qsl(parsed.query) | |
| if not qs: | |
| self.log("INFO", "[HPP] No query parameters to test.") | |
| return self.vulns | |
| baseline = self._fetch(self.target) or "" | |
| for k, v in qs: | |
| # Duplicate the parameter with a different value | |
| dup_qs = urllib.parse.urlencode(qs) + f"&{k}=HPP_PROBE" | |
| test_url = parsed._replace(query=dup_qs).geturl() | |
| resp = self._fetch(test_url) | |
| if resp and "HPP_PROBE" in resp and "HPP_PROBE" not in baseline: | |
| self.add_vuln( | |
| title=f"HTTP Parameter Pollution in `{k}`", | |
| severity="Medium", category="HTTP Parameter Pollution", cvss_score=5.3, | |
| description=f"Duplicating parameter `{k}` with value `HPP_PROBE` caused " | |
| f"the injected value to appear in the response. The server uses the " | |
| f"last/first occurrence, which can bypass WAFs and input validation.", | |
| remediation="Explicitly handle duplicate parameters. Use the first occurrence " | |
| "only and reject requests with duplicate security-sensitive params.") | |
| self.log("WARNING", f"[HPP] Pollution confirmed in `{k}`!") | |
| return self.vulns | |
| self.log("SUCCESS", "[HPP] No HTTP Parameter Pollution detected.") | |
| return self.vulns | |
| def _fetch(self, url): | |
| try: | |
| req = urllib.request.Request(url, headers=self._make_headers()) | |
| with urllib.request.urlopen(req, timeout=5, context=self.get_ssl_context()) as r: | |
| return r.read().decode("utf-8", errors="ignore") | |
| except urllib.error.HTTPError as e: return e.read().decode("utf-8", errors="ignore") | |
| except Exception as e: | |
| self.log("ERROR", f"[HPP] _fetch error: {e}") | |
| return self.vulns |