mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-17 21:19:40 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
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
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
# API Reference: Implementing API Abuse Detection with Rate Limiting
|
||||
|
||||
## Redis Token Bucket (Python)
|
||||
|
||||
```python
|
||||
import redis, time
|
||||
r = redis.Redis()
|
||||
|
||||
# Lua-based atomic token bucket
|
||||
lua = """
|
||||
local tokens = tonumber(redis.call('HGET', KEYS[1], 'tokens') or ARGV[1])
|
||||
local last = tonumber(redis.call('HGET', KEYS[1], 'last') or ARGV[3])
|
||||
local elapsed = ARGV[3] - last
|
||||
tokens = math.min(tonumber(ARGV[1]), tokens + elapsed * tonumber(ARGV[2]))
|
||||
if tokens >= 1 then
|
||||
tokens = tokens - 1
|
||||
redis.call('HMSET', KEYS[1], 'tokens', tokens, 'last', ARGV[3])
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
"""
|
||||
allowed = r.eval(lua, 1, f"rl:{client_ip}", max_tokens, refill_rate, time.time())
|
||||
```
|
||||
|
||||
## Rate Limit Response Headers
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| `X-RateLimit-Limit` | Maximum requests allowed |
|
||||
| `X-RateLimit-Remaining` | Requests remaining |
|
||||
| `X-RateLimit-Reset` | Unix timestamp when limit resets |
|
||||
| `Retry-After` | Seconds until client can retry |
|
||||
|
||||
## NGINX Rate Limiting
|
||||
|
||||
```nginx
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||||
location /api/ {
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
limit_req_status 429;
|
||||
}
|
||||
```
|
||||
|
||||
## Abuse Detection Thresholds
|
||||
|
||||
| Attack Type | Indicator | Threshold |
|
||||
|-------------|-----------|-----------|
|
||||
| Brute Force | Auth failures/IP | > 10 in 5 min |
|
||||
| Credential Stuffing | Unique users/IP | > 20 |
|
||||
| API Scraping | Requests/IP | > 500/hr |
|
||||
| Rate Bypass | User-Agent rotation | > 10 unique UAs |
|
||||
|
||||
## Flask-Limiter
|
||||
|
||||
```python
|
||||
from flask import Flask
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
|
||||
app = Flask(__name__)
|
||||
limiter = Limiter(get_remote_address, app=app, default_limits=["100/minute"])
|
||||
|
||||
@app.route("/api/login")
|
||||
@limiter.limit("5/minute")
|
||||
def login():
|
||||
pass
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- Redis Rate Limiting: https://redis.io/glossary/rate-limiting/
|
||||
- Flask-Limiter: https://flask-limiter.readthedocs.io/
|
||||
- IETF RateLimit Headers: https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/
|
||||
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for implementing API abuse detection with rate limiting analysis."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import re
|
||||
from datetime import datetime
|
||||
from collections import defaultdict, Counter
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_access_logs(log_path):
|
||||
"""Load API access logs from JSON lines."""
|
||||
entries = []
|
||||
with open(log_path) as f:
|
||||
for line in f:
|
||||
try:
|
||||
entries.append(json.loads(line))
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
return entries
|
||||
|
||||
|
||||
def detect_brute_force(logs, failure_threshold=10, window_minutes=5):
|
||||
"""Detect brute force attacks by counting auth failures per IP."""
|
||||
ip_failures = defaultdict(list)
|
||||
for entry in logs:
|
||||
status = entry.get("status_code", entry.get("status", 0))
|
||||
if int(status) in (401, 403):
|
||||
ip = entry.get("client_ip", entry.get("ip", ""))
|
||||
ts = entry.get("timestamp", "")
|
||||
ip_failures[ip].append(ts)
|
||||
findings = []
|
||||
for ip, timestamps in ip_failures.items():
|
||||
if len(timestamps) >= failure_threshold:
|
||||
findings.append({
|
||||
"client_ip": ip,
|
||||
"auth_failures": len(timestamps),
|
||||
"severity": "CRITICAL" if len(timestamps) > 50 else "HIGH",
|
||||
"category": "brute_force",
|
||||
"first_seen": timestamps[0],
|
||||
"last_seen": timestamps[-1],
|
||||
})
|
||||
return sorted(findings, key=lambda x: x["auth_failures"], reverse=True)
|
||||
|
||||
|
||||
def detect_api_scraping(logs, threshold=500):
|
||||
"""Detect API scraping by high request volume per IP."""
|
||||
ip_counts = Counter()
|
||||
ip_endpoints = defaultdict(set)
|
||||
for entry in logs:
|
||||
ip = entry.get("client_ip", entry.get("ip", ""))
|
||||
endpoint = entry.get("path", entry.get("endpoint", ""))
|
||||
ip_counts[ip] += 1
|
||||
ip_endpoints[ip].add(endpoint)
|
||||
findings = []
|
||||
for ip, count in ip_counts.items():
|
||||
if count >= threshold:
|
||||
findings.append({
|
||||
"client_ip": ip,
|
||||
"total_requests": count,
|
||||
"unique_endpoints": len(ip_endpoints[ip]),
|
||||
"severity": "HIGH",
|
||||
"category": "api_scraping",
|
||||
})
|
||||
return sorted(findings, key=lambda x: x["total_requests"], reverse=True)
|
||||
|
||||
|
||||
def detect_credential_stuffing(logs, threshold=20):
|
||||
"""Detect credential stuffing: many unique usernames from single IP."""
|
||||
ip_users = defaultdict(set)
|
||||
for entry in logs:
|
||||
if entry.get("path", "").endswith(("/login", "/auth", "/signin")):
|
||||
ip = entry.get("client_ip", entry.get("ip", ""))
|
||||
user = entry.get("username", entry.get("user", ""))
|
||||
if user:
|
||||
ip_users[ip].add(user)
|
||||
findings = []
|
||||
for ip, users in ip_users.items():
|
||||
if len(users) >= threshold:
|
||||
findings.append({
|
||||
"client_ip": ip,
|
||||
"unique_usernames": len(users),
|
||||
"severity": "CRITICAL",
|
||||
"category": "credential_stuffing",
|
||||
})
|
||||
return sorted(findings, key=lambda x: x["unique_usernames"], reverse=True)
|
||||
|
||||
|
||||
def detect_rate_limit_bypass(logs):
|
||||
"""Detect attempts to bypass rate limiting."""
|
||||
findings = []
|
||||
ip_ua_combos = defaultdict(set)
|
||||
for entry in logs:
|
||||
ip = entry.get("client_ip", entry.get("ip", ""))
|
||||
ua = entry.get("user_agent", "")
|
||||
ip_ua_combos[ip].add(ua)
|
||||
for ip, agents in ip_ua_combos.items():
|
||||
if len(agents) >= 10:
|
||||
findings.append({
|
||||
"client_ip": ip,
|
||||
"unique_user_agents": len(agents),
|
||||
"severity": "HIGH",
|
||||
"category": "ua_rotation",
|
||||
"reason": "Rotating User-Agent to bypass rate limits",
|
||||
})
|
||||
ip_429_count = Counter()
|
||||
for entry in logs:
|
||||
if int(entry.get("status_code", entry.get("status", 0))) == 429:
|
||||
ip = entry.get("client_ip", entry.get("ip", ""))
|
||||
ip_429_count[ip] += 1
|
||||
for ip, count in ip_429_count.items():
|
||||
if count >= 50:
|
||||
findings.append({
|
||||
"client_ip": ip,
|
||||
"rate_limit_hits": count,
|
||||
"severity": "MEDIUM",
|
||||
"category": "rate_limit_persistence",
|
||||
"reason": "Continuing requests after rate limiting",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def generate_rate_limit_config(logs):
|
||||
"""Generate recommended rate limit configuration based on traffic patterns."""
|
||||
endpoint_counts = Counter()
|
||||
for entry in logs:
|
||||
path = entry.get("path", entry.get("endpoint", ""))
|
||||
endpoint_counts[path] += 1
|
||||
auth_endpoints = [p for p in endpoint_counts if any(
|
||||
k in p for k in ["login", "auth", "signin", "register", "password"])]
|
||||
config = {
|
||||
"global": {"requests_per_minute": 100, "burst": 20},
|
||||
"auth_endpoints": {
|
||||
"endpoints": auth_endpoints,
|
||||
"requests_per_minute": 10,
|
||||
"burst": 3,
|
||||
"block_duration_seconds": 300,
|
||||
},
|
||||
"sensitive_endpoints": {
|
||||
"endpoints": ["/api/admin", "/api/users", "/api/export"],
|
||||
"requests_per_minute": 30,
|
||||
"burst": 5,
|
||||
},
|
||||
}
|
||||
return config
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="API Abuse Detection Agent")
|
||||
parser.add_argument("--log", required=True, help="API access log (JSON lines)")
|
||||
parser.add_argument("--output", default="api_abuse_report.json")
|
||||
parser.add_argument("--action", choices=[
|
||||
"brute_force", "scraping", "stuffing", "bypass", "config", "full_analysis"
|
||||
], default="full_analysis")
|
||||
args = parser.parse_args()
|
||||
|
||||
logs = load_access_logs(args.log)
|
||||
report = {"generated_at": datetime.utcnow().isoformat(), "total_requests": len(logs),
|
||||
"findings": {}}
|
||||
print(f"[+] Loaded {len(logs)} API requests")
|
||||
|
||||
if args.action in ("brute_force", "full_analysis"):
|
||||
f = detect_brute_force(logs)
|
||||
report["findings"]["brute_force"] = f
|
||||
print(f"[+] Brute force sources: {len(f)}")
|
||||
|
||||
if args.action in ("scraping", "full_analysis"):
|
||||
f = detect_api_scraping(logs)
|
||||
report["findings"]["api_scraping"] = f
|
||||
print(f"[+] Scraping sources: {len(f)}")
|
||||
|
||||
if args.action in ("stuffing", "full_analysis"):
|
||||
f = detect_credential_stuffing(logs)
|
||||
report["findings"]["credential_stuffing"] = f
|
||||
print(f"[+] Credential stuffing sources: {len(f)}")
|
||||
|
||||
if args.action in ("bypass", "full_analysis"):
|
||||
f = detect_rate_limit_bypass(logs)
|
||||
report["findings"]["bypass_attempts"] = f
|
||||
print(f"[+] Rate limit bypass attempts: {len(f)}")
|
||||
|
||||
if args.action in ("config", "full_analysis"):
|
||||
config = generate_rate_limit_config(logs)
|
||||
report["findings"]["recommended_config"] = config
|
||||
print("[+] Rate limit config generated")
|
||||
|
||||
with open(args.output, "w") as fout:
|
||||
json.dump(report, fout, indent=2, default=str)
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user