mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-13 19:05:17 +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,92 @@
|
||||
# API Reference: Browser Forensics with Hindsight
|
||||
|
||||
## Hindsight CLI
|
||||
|
||||
### Syntax
|
||||
```bash
|
||||
hindsight.py -i <profile_path> # Analyze Chrome profile
|
||||
hindsight.py -i <path> -o <output_dir> # Save results
|
||||
hindsight.py -i <path> -f xlsx # Export as Excel
|
||||
hindsight.py -i <path> -f sqlite # Export as SQLite
|
||||
hindsight.py -i <path> -b <browser_type> # Specify browser type
|
||||
```
|
||||
|
||||
### Browser Types
|
||||
| Flag | Browser |
|
||||
|------|---------|
|
||||
| `Chrome` | Google Chrome |
|
||||
| `Edge` | Microsoft Edge (Chromium) |
|
||||
| `Brave` | Brave Browser |
|
||||
| `Opera` | Opera (Chromium) |
|
||||
|
||||
### Output Artifacts
|
||||
| Table | Description |
|
||||
|-------|-------------|
|
||||
| `urls` | Browsing history with visit counts |
|
||||
| `downloads` | File downloads with source URLs |
|
||||
| `cookies` | Cookie values, domains, expiry |
|
||||
| `autofill` | Form autofill entries |
|
||||
| `bookmarks` | Saved bookmarks |
|
||||
| `preferences` | Browser configuration |
|
||||
| `local_storage` | Site local storage data |
|
||||
| `login_data` | Saved credential metadata |
|
||||
| `extensions` | Installed extensions with permissions |
|
||||
|
||||
## Chrome SQLite Databases
|
||||
|
||||
### History Database
|
||||
```sql
|
||||
-- Browsing history
|
||||
SELECT u.url, u.title, v.visit_time, v.transition
|
||||
FROM visits v JOIN urls u ON v.url = u.id
|
||||
ORDER BY v.visit_time DESC;
|
||||
|
||||
-- Downloads
|
||||
SELECT target_path, tab_url, total_bytes, start_time, danger_type, mime_type
|
||||
FROM downloads ORDER BY start_time DESC;
|
||||
```
|
||||
|
||||
### Cookies Database
|
||||
```sql
|
||||
SELECT host_key, name, value, creation_utc, expires_utc, is_secure, is_httponly
|
||||
FROM cookies ORDER BY creation_utc DESC;
|
||||
```
|
||||
|
||||
### Web Data Database (Autofill)
|
||||
```sql
|
||||
SELECT name, value, count, date_created, date_last_used
|
||||
FROM autofill ORDER BY date_last_used DESC;
|
||||
```
|
||||
|
||||
## Chrome Timestamp Conversion
|
||||
|
||||
### Format
|
||||
Microseconds since January 1, 1601 (Windows FILETIME base)
|
||||
|
||||
### Python Conversion
|
||||
```python
|
||||
import datetime
|
||||
def chrome_to_datetime(chrome_time):
|
||||
epoch = datetime.datetime(1601, 1, 1)
|
||||
return epoch + datetime.timedelta(microseconds=chrome_time)
|
||||
```
|
||||
|
||||
## Browser Profile Paths
|
||||
|
||||
| OS | Browser | Default Path |
|
||||
|----|---------|-------------|
|
||||
| Windows | Chrome | `%LOCALAPPDATA%\Google\Chrome\User Data\Default` |
|
||||
| Windows | Edge | `%LOCALAPPDATA%\Microsoft\Edge\User Data\Default` |
|
||||
| Linux | Chrome | `~/.config/google-chrome/Default` |
|
||||
| macOS | Chrome | `~/Library/Application Support/Google/Chrome/Default` |
|
||||
|
||||
## Transition Types (visit_transition & 0xFF)
|
||||
| Value | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| 0 | LINK | Clicked a link |
|
||||
| 1 | TYPED | Typed URL in address bar |
|
||||
| 2 | AUTO_BOOKMARK | Via bookmark |
|
||||
| 3 | AUTO_SUBFRAME | Subframe navigation |
|
||||
| 5 | GENERATED | Generated (e.g., search) |
|
||||
| 7 | FORM_SUBMIT | Form submission |
|
||||
| 8 | RELOAD | Page reload |
|
||||
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Browser forensics analysis agent using Hindsight concepts.
|
||||
|
||||
Parses Chromium-based browser artifacts (Chrome, Edge, Brave) including
|
||||
history, downloads, cookies, autofill, and extensions from SQLite databases.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import sqlite3
|
||||
import datetime
|
||||
import hashlib
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def chrome_time_to_datetime(chrome_time):
|
||||
"""Convert Chrome timestamp (microseconds since 1601-01-01) to datetime."""
|
||||
if not chrome_time or chrome_time == 0:
|
||||
return None
|
||||
try:
|
||||
epoch = datetime.datetime(1601, 1, 1)
|
||||
delta = datetime.timedelta(microseconds=chrome_time)
|
||||
return (epoch + delta).isoformat() + "Z"
|
||||
except (OverflowError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def find_browser_profiles(base_path=None):
|
||||
"""Locate Chromium-based browser profile directories."""
|
||||
if base_path and os.path.isdir(base_path):
|
||||
return [base_path]
|
||||
profiles = []
|
||||
home = os.path.expanduser("~")
|
||||
candidates = [
|
||||
os.path.join(home, "AppData", "Local", "Google", "Chrome", "User Data", "Default"),
|
||||
os.path.join(home, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default"),
|
||||
os.path.join(home, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data", "Default"),
|
||||
os.path.join(home, ".config", "google-chrome", "Default"),
|
||||
os.path.join(home, ".config", "chromium", "Default"),
|
||||
os.path.join(home, ".config", "microsoft-edge", "Default"),
|
||||
]
|
||||
for path in candidates:
|
||||
if os.path.isdir(path):
|
||||
profiles.append(path)
|
||||
return profiles
|
||||
|
||||
|
||||
def parse_history(profile_path):
|
||||
"""Parse browsing history from History SQLite database."""
|
||||
db_path = os.path.join(profile_path, "History")
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
entries = []
|
||||
try:
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT u.url, u.title, v.visit_time, v.transition, u.visit_count
|
||||
FROM visits v JOIN urls u ON v.url = u.id
|
||||
ORDER BY v.visit_time DESC LIMIT 5000
|
||||
""")
|
||||
for url, title, visit_time, transition, count in cursor.fetchall():
|
||||
entries.append({
|
||||
"url": url, "title": title or "",
|
||||
"visit_time": chrome_time_to_datetime(visit_time),
|
||||
"transition": transition & 0xFF,
|
||||
"visit_count": count,
|
||||
})
|
||||
conn.close()
|
||||
except sqlite3.Error as e:
|
||||
entries.append({"error": str(e)})
|
||||
return entries
|
||||
|
||||
|
||||
def parse_downloads(profile_path):
|
||||
"""Parse download history from History database."""
|
||||
db_path = os.path.join(profile_path, "History")
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
downloads = []
|
||||
try:
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT target_path, tab_url, total_bytes, start_time, end_time,
|
||||
danger_type, interrupt_reason, mime_type
|
||||
FROM downloads ORDER BY start_time DESC LIMIT 1000
|
||||
""")
|
||||
for row in cursor.fetchall():
|
||||
downloads.append({
|
||||
"target_path": row[0], "source_url": row[1],
|
||||
"total_bytes": row[2],
|
||||
"start_time": chrome_time_to_datetime(row[3]),
|
||||
"end_time": chrome_time_to_datetime(row[4]),
|
||||
"danger_type": row[5], "interrupt_reason": row[6],
|
||||
"mime_type": row[7],
|
||||
})
|
||||
conn.close()
|
||||
except sqlite3.Error as e:
|
||||
downloads.append({"error": str(e)})
|
||||
return downloads
|
||||
|
||||
|
||||
def parse_cookies(profile_path):
|
||||
"""Parse cookies from Cookies database."""
|
||||
db_path = os.path.join(profile_path, "Cookies")
|
||||
if not os.path.exists(db_path):
|
||||
db_path = os.path.join(profile_path, "Network", "Cookies")
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
cookies = []
|
||||
try:
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT host_key, name, path, creation_utc, expires_utc,
|
||||
is_secure, is_httponly, samesite
|
||||
FROM cookies ORDER BY creation_utc DESC LIMIT 2000
|
||||
""")
|
||||
for row in cursor.fetchall():
|
||||
cookies.append({
|
||||
"host": row[0], "name": row[1], "path": row[2],
|
||||
"created": chrome_time_to_datetime(row[3]),
|
||||
"expires": chrome_time_to_datetime(row[4]),
|
||||
"secure": bool(row[5]), "httponly": bool(row[6]),
|
||||
"samesite": row[7],
|
||||
})
|
||||
conn.close()
|
||||
except sqlite3.Error as e:
|
||||
cookies.append({"error": str(e)})
|
||||
return cookies
|
||||
|
||||
|
||||
def parse_autofill(profile_path):
|
||||
"""Parse autofill data from Web Data database."""
|
||||
db_path = os.path.join(profile_path, "Web Data")
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
entries = []
|
||||
try:
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT name, value, count, date_created, date_last_used
|
||||
FROM autofill ORDER BY date_last_used DESC LIMIT 500
|
||||
""")
|
||||
for row in cursor.fetchall():
|
||||
entries.append({
|
||||
"field_name": row[0], "value": row[1][:50] + "..." if len(row[1]) > 50 else row[1],
|
||||
"usage_count": row[2],
|
||||
"created": chrome_time_to_datetime(row[3] * 1000000 if row[3] else 0),
|
||||
"last_used": chrome_time_to_datetime(row[4] * 1000000 if row[4] else 0),
|
||||
})
|
||||
conn.close()
|
||||
except sqlite3.Error as e:
|
||||
entries.append({"error": str(e)})
|
||||
return entries
|
||||
|
||||
|
||||
def parse_extensions(profile_path):
|
||||
"""Parse installed browser extensions."""
|
||||
ext_dir = os.path.join(profile_path, "Extensions")
|
||||
extensions = []
|
||||
if not os.path.isdir(ext_dir):
|
||||
return extensions
|
||||
for ext_id in os.listdir(ext_dir):
|
||||
ext_path = os.path.join(ext_dir, ext_id)
|
||||
if os.path.isdir(ext_path):
|
||||
versions = sorted(os.listdir(ext_path))
|
||||
manifest_path = os.path.join(ext_path, versions[-1], "manifest.json") if versions else None
|
||||
name = ext_id
|
||||
if manifest_path and os.path.exists(manifest_path):
|
||||
try:
|
||||
with open(manifest_path, "r", encoding="utf-8") as f:
|
||||
manifest = json.load(f)
|
||||
name = manifest.get("name", ext_id)
|
||||
extensions.append({
|
||||
"id": ext_id, "name": name,
|
||||
"version": manifest.get("version", "?"),
|
||||
"permissions": manifest.get("permissions", [])[:10],
|
||||
})
|
||||
except (json.JSONDecodeError, IOError):
|
||||
extensions.append({"id": ext_id, "name": name, "version": "unknown"})
|
||||
return extensions
|
||||
|
||||
|
||||
def detect_suspicious_activity(history, downloads):
|
||||
"""Flag suspicious browsing and download patterns."""
|
||||
findings = []
|
||||
suspicious_domains = ["pastebin.com", "ngrok.io", "raw.githubusercontent.com",
|
||||
"transfer.sh", "file.io", "temp.sh", "anonfiles.com"]
|
||||
for entry in history:
|
||||
url = entry.get("url", "").lower()
|
||||
for domain in suspicious_domains:
|
||||
if domain in url:
|
||||
findings.append({
|
||||
"type": "suspicious_url", "url": entry["url"],
|
||||
"domain": domain, "time": entry.get("visit_time"),
|
||||
})
|
||||
dangerous_mimes = ["application/x-msdownload", "application/x-msdos-program",
|
||||
"application/x-executable", "application/vnd.ms-excel.sheet.macroEnabled"]
|
||||
for dl in downloads:
|
||||
if dl.get("danger_type", 0) > 0:
|
||||
findings.append({
|
||||
"type": "dangerous_download", "path": dl.get("target_path"),
|
||||
"source": dl.get("source_url"), "danger_type": dl.get("danger_type"),
|
||||
})
|
||||
if dl.get("mime_type", "") in dangerous_mimes:
|
||||
findings.append({
|
||||
"type": "suspicious_mime", "mime": dl.get("mime_type"),
|
||||
"path": dl.get("target_path"),
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Browser Forensics Analysis Agent")
|
||||
print("Chromium history, downloads, cookies, extensions")
|
||||
print("=" * 60)
|
||||
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
profiles = find_browser_profiles(target)
|
||||
|
||||
if not profiles:
|
||||
print("\n[!] No browser profiles found.")
|
||||
print("[DEMO] Usage: python agent.py <profile_path>")
|
||||
print(" e.g. python agent.py ~/AppData/Local/Google/Chrome/User\\ Data/Default")
|
||||
sys.exit(0)
|
||||
|
||||
for profile in profiles:
|
||||
print(f"\n[*] Profile: {profile}")
|
||||
|
||||
history = parse_history(profile)
|
||||
print(f" History entries: {len(history)}")
|
||||
for h in history[:5]:
|
||||
print(f" {h.get('visit_time', '?')} | {h.get('title', '')[:50]} | {h.get('url', '')[:60]}")
|
||||
|
||||
downloads = parse_downloads(profile)
|
||||
print(f" Downloads: {len(downloads)}")
|
||||
for d in downloads[:5]:
|
||||
print(f" {d.get('start_time', '?')} | {d.get('mime_type', '?')} | {os.path.basename(d.get('target_path', ''))}")
|
||||
|
||||
cookies = parse_cookies(profile)
|
||||
print(f" Cookies: {len(cookies)}")
|
||||
|
||||
extensions = parse_extensions(profile)
|
||||
print(f" Extensions: {len(extensions)}")
|
||||
for ext in extensions[:5]:
|
||||
print(f" {ext.get('name', '?')} v{ext.get('version', '?')} [{ext.get('id', '')[:20]}]")
|
||||
|
||||
findings = detect_suspicious_activity(history, downloads)
|
||||
print(f"\n --- Suspicious Activity: {len(findings)} findings ---")
|
||||
for f in findings[:10]:
|
||||
print(f" [{f['type']}] {f.get('url', f.get('path', ''))}")
|
||||
Reference in New Issue
Block a user