mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-27 06:31:00 +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.
|
||||
@@ -0,0 +1,63 @@
|
||||
# API Reference: Forced Browsing Authentication Bypass Agent
|
||||
|
||||
## Overview
|
||||
|
||||
Tests web applications for unprotected endpoints, authentication bypass via HTTP methods and path normalization, and exposed sensitive files. For authorized penetration testing only.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| requests | >=2.28 | HTTP requests to target endpoints |
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# Test common admin paths
|
||||
python agent.py --target https://target.example.com --admin-paths --session-cookie <token>
|
||||
|
||||
# Test with custom wordlist
|
||||
python agent.py --target https://target.example.com --wordlist /path/to/wordlist.txt
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `--target` | Yes | Target base URL |
|
||||
| `--wordlist` | No | Path to directory/file wordlist |
|
||||
| `--session-cookie` | No | Valid session cookie for authenticated comparison |
|
||||
| `--admin-paths` | No | Use built-in common admin path list |
|
||||
| `--output` | No | Output file (default: `forced_browsing_report.json`) |
|
||||
|
||||
## Key Functions
|
||||
|
||||
### `test_endpoint(base_url, path, session_cookie)`
|
||||
Tests an endpoint with and without authentication, comparing response status and size to detect auth bypass.
|
||||
|
||||
### `enumerate_directories(base_url, wordlist, session_cookie)`
|
||||
Iterates through wordlist paths, recording responses with status 200, 301, 302, or 403.
|
||||
|
||||
### `test_http_method_bypass(base_url, path)`
|
||||
Tests GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD on protected endpoints to find method-based bypasses.
|
||||
|
||||
### `test_path_traversal_bypass(base_url, path)`
|
||||
Tests URL normalization variants (case changes, path traversal, encoding, semicolons) against protected paths.
|
||||
|
||||
### `check_sensitive_files(base_url)`
|
||||
Checks for exposed `.env`, `.git`, backup files, and configuration files.
|
||||
|
||||
### `generate_report(findings, method_results, sensitive_files)`
|
||||
Compiles all findings into a structured JSON pentest report.
|
||||
|
||||
## Output Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"total_endpoints_found": 15,
|
||||
"auth_bypass_candidates": [{"path": "/admin", "unauth_status": 200}],
|
||||
"accessible_without_auth": [...],
|
||||
"http_method_bypass": {"/admin": {"GET": 403, "PUT": 200}},
|
||||
"sensitive_files_exposed": [{"path": ".env", "size": 1024}]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
# For authorized penetration testing and lab environments only
|
||||
"""Forced Browsing Authentication Bypass Agent - Tests for unprotected endpoints."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import requests
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_ADMIN_PATHS = [
|
||||
"/admin", "/administrator", "/admin-panel", "/wp-admin", "/cpanel",
|
||||
"/phpmyadmin", "/adminer", "/manager", "/console", "/debug",
|
||||
"/actuator", "/actuator/env", "/actuator/health", "/actuator/beans",
|
||||
"/swagger-ui", "/swagger-ui.html", "/api-docs", "/graphql", "/graphiql",
|
||||
"/.env", "/server-status", "/server-info", "/.git/HEAD", "/.git/config",
|
||||
"/web.config", "/phpinfo.php", "/robots.txt", "/sitemap.xml",
|
||||
]
|
||||
|
||||
SENSITIVE_EXTENSIONS = [
|
||||
".bak", ".old", ".orig", ".save", ".swp", ".tmp", ".config",
|
||||
".sql", ".gz", ".tar", ".zip", ".env",
|
||||
]
|
||||
|
||||
|
||||
def load_wordlist(wordlist_path):
|
||||
"""Load directory/file wordlist from file."""
|
||||
with open(wordlist_path, "r") as f:
|
||||
return [line.strip() for line in f if line.strip() and not line.startswith("#")]
|
||||
|
||||
|
||||
def test_endpoint(base_url, path, session_cookie=None, timeout=10):
|
||||
"""Test a single endpoint with and without authentication."""
|
||||
url = urljoin(base_url, path)
|
||||
unauth_resp = requests.get(url, timeout=timeout, allow_redirects=False, verify=False)
|
||||
|
||||
auth_resp = None
|
||||
if session_cookie:
|
||||
auth_resp = requests.get(
|
||||
url, cookies={"session": session_cookie},
|
||||
timeout=timeout, allow_redirects=False, verify=False,
|
||||
)
|
||||
|
||||
result = {
|
||||
"path": path,
|
||||
"url": url,
|
||||
"unauth_status": unauth_resp.status_code,
|
||||
"unauth_size": len(unauth_resp.content),
|
||||
}
|
||||
if auth_resp:
|
||||
result["auth_status"] = auth_resp.status_code
|
||||
result["auth_size"] = len(auth_resp.content)
|
||||
result["auth_bypass"] = (
|
||||
unauth_resp.status_code == 200 and auth_resp.status_code == 200
|
||||
and abs(result["unauth_size"] - result["auth_size"]) < 100
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def enumerate_directories(base_url, wordlist, session_cookie=None):
|
||||
"""Enumerate directories and test authentication enforcement."""
|
||||
findings = []
|
||||
for word in wordlist:
|
||||
path = f"/{word}" if not word.startswith("/") else word
|
||||
try:
|
||||
result = test_endpoint(base_url, path, session_cookie)
|
||||
if result["unauth_status"] in (200, 301, 302, 403):
|
||||
findings.append(result)
|
||||
logger.info(
|
||||
"Found: %s (status: %d, size: %d)",
|
||||
path, result["unauth_status"], result["unauth_size"],
|
||||
)
|
||||
except requests.RequestException:
|
||||
continue
|
||||
return findings
|
||||
|
||||
|
||||
def test_http_method_bypass(base_url, path):
|
||||
"""Test HTTP method-based authentication bypass."""
|
||||
methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
|
||||
results = {}
|
||||
for method in methods:
|
||||
try:
|
||||
resp = requests.request(method, urljoin(base_url, path), timeout=10, verify=False)
|
||||
results[method] = resp.status_code
|
||||
except requests.RequestException:
|
||||
results[method] = None
|
||||
logger.info("Method bypass test for %s: %s", path, results)
|
||||
return results
|
||||
|
||||
|
||||
def test_path_traversal_bypass(base_url, path):
|
||||
"""Test path normalization bypass techniques."""
|
||||
variants = [
|
||||
path,
|
||||
path.upper(),
|
||||
path.replace("/", "/./"),
|
||||
f"/public/..{path}",
|
||||
path.replace("/", "%2f"),
|
||||
f"/;{path}",
|
||||
f"/.;{path}",
|
||||
f"{path}/",
|
||||
f"{path}.json",
|
||||
]
|
||||
results = []
|
||||
for variant in variants:
|
||||
try:
|
||||
resp = requests.get(urljoin(base_url, variant), timeout=10, verify=False)
|
||||
results.append({"path": variant, "status": resp.status_code, "size": len(resp.content)})
|
||||
except requests.RequestException:
|
||||
continue
|
||||
return results
|
||||
|
||||
|
||||
def check_sensitive_files(base_url):
|
||||
"""Check for exposed backup and configuration files."""
|
||||
sensitive_paths = [
|
||||
".env", ".git/HEAD", ".git/config", "web.config", "wp-config.php.bak",
|
||||
"config.php.old", ".htpasswd", "database.yml", "phpinfo.php",
|
||||
]
|
||||
exposed = []
|
||||
for path in sensitive_paths:
|
||||
try:
|
||||
resp = requests.get(urljoin(base_url, path), timeout=10, verify=False)
|
||||
if resp.status_code == 200 and len(resp.content) > 0:
|
||||
exposed.append({"path": path, "status": resp.status_code, "size": len(resp.content)})
|
||||
logger.warning("EXPOSED: %s (size: %d bytes)", path, len(resp.content))
|
||||
except requests.RequestException:
|
||||
continue
|
||||
return exposed
|
||||
|
||||
|
||||
def generate_report(findings, method_results, sensitive_files):
|
||||
"""Generate pentest finding report for forced browsing results."""
|
||||
report = {
|
||||
"timestamp": __import__("datetime").datetime.utcnow().isoformat(),
|
||||
"total_endpoints_found": len(findings),
|
||||
"auth_bypass_candidates": [f for f in findings if f.get("auth_bypass")],
|
||||
"accessible_without_auth": [f for f in findings if f["unauth_status"] == 200],
|
||||
"http_method_bypass": method_results,
|
||||
"sensitive_files_exposed": sensitive_files,
|
||||
}
|
||||
bypasses = len(report["auth_bypass_candidates"])
|
||||
logger.info("Report: %d endpoints, %d auth bypasses, %d sensitive files",
|
||||
len(findings), bypasses, len(sensitive_files))
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Forced Browsing Authentication Bypass Agent")
|
||||
parser.add_argument("--target", required=True, help="Target base URL")
|
||||
parser.add_argument("--wordlist", help="Path to wordlist file")
|
||||
parser.add_argument("--session-cookie", help="Valid session cookie for auth comparison")
|
||||
parser.add_argument("--admin-paths", action="store_true", help="Test common admin paths")
|
||||
parser.add_argument("--output", default="forced_browsing_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
wordlist = DEFAULT_ADMIN_PATHS if args.admin_paths else []
|
||||
if args.wordlist:
|
||||
wordlist = load_wordlist(args.wordlist)
|
||||
|
||||
findings = enumerate_directories(args.target, wordlist, args.session_cookie)
|
||||
method_results = {}
|
||||
for f in findings:
|
||||
if f["unauth_status"] in (401, 403):
|
||||
method_results[f["path"]] = test_http_method_bypass(args.target, f["path"])
|
||||
|
||||
sensitive = check_sensitive_files(args.target)
|
||||
report = generate_report(findings, method_results, sensitive)
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved to %s", args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user