mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-30 07:56:52 +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,62 @@
|
||||
# API Reference: Browser History Extraction Agent
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| sqlite3 | stdlib | Query Chrome/Firefox SQLite databases |
|
||||
| csv | stdlib | Export results to CSV format |
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
python scripts/agent.py \
|
||||
--chrome-dir "/mnt/evidence/Users/suspect/AppData/Local/Google/Chrome/User Data/Default" \
|
||||
--firefox-dir "/mnt/evidence/Users/suspect/AppData/Roaming/Mozilla/Firefox/Profiles/abc.default" \
|
||||
--output-dir /cases/analysis/ \
|
||||
--output browser_report.json
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### `chrome_time_to_utc(chrome_ts) -> str`
|
||||
Converts Chrome/WebKit timestamp (microseconds since 1601-01-01) to ISO-8601 UTC string.
|
||||
|
||||
### `firefox_time_to_utc(ff_ts) -> str`
|
||||
Converts Firefox timestamp (microseconds since Unix epoch) to ISO-8601 UTC string.
|
||||
|
||||
### `extract_chrome_history(db_path, limit) -> list`
|
||||
Queries the `urls` table from Chrome's `History` SQLite DB. Returns URL, title, last_visit, visit_count.
|
||||
|
||||
### `extract_chrome_downloads(db_path, limit) -> list`
|
||||
Queries the `downloads` table for file path, source URL, size, timestamps, and danger type.
|
||||
|
||||
### `extract_chrome_cookies(db_path, limit) -> list`
|
||||
Queries the `cookies` table. Note: cookie values are DPAPI-encrypted on Windows.
|
||||
|
||||
### `extract_firefox_history(db_path, limit) -> list`
|
||||
Queries `moz_places` JOIN `moz_historyvisits` from Firefox `places.sqlite`.
|
||||
|
||||
### `extract_firefox_cookies(db_path, limit) -> list`
|
||||
Queries `moz_cookies` from Firefox `cookies.sqlite`.
|
||||
|
||||
### `export_to_csv(data, output_path)`
|
||||
Writes list of dicts to CSV with headers.
|
||||
|
||||
### `generate_report(chrome_dir, firefox_dir, output_dir) -> dict`
|
||||
Orchestrates extraction from both browsers and exports CSVs.
|
||||
|
||||
## Browser Database Locations (Windows)
|
||||
|
||||
| Browser | Path |
|
||||
|---------|------|
|
||||
| Chrome | `%LOCALAPPDATA%\Google\Chrome\User Data\Default\History` |
|
||||
| Edge | `%LOCALAPPDATA%\Microsoft\Edge\User Data\Default\History` |
|
||||
| Firefox | `%APPDATA%\Mozilla\Firefox\Profiles\*.default\places.sqlite` |
|
||||
|
||||
## Timestamp Formats
|
||||
|
||||
| Browser | Epoch | Unit |
|
||||
|---------|-------|------|
|
||||
| Chrome/Edge | 1601-01-01 | Microseconds |
|
||||
| Firefox | 1970-01-01 | Microseconds |
|
||||
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Browser history artifact extraction agent using sqlite3 for Chrome/Firefox/Edge forensics."""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Optional
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CHROME_EPOCH = datetime(1601, 1, 1)
|
||||
UNIX_EPOCH = datetime(1970, 1, 1)
|
||||
|
||||
|
||||
def chrome_time_to_utc(chrome_ts: int) -> str:
|
||||
"""Convert Chrome/WebKit timestamp (microseconds since 1601-01-01) to ISO UTC."""
|
||||
if not chrome_ts or chrome_ts < 0:
|
||||
return ""
|
||||
try:
|
||||
dt = CHROME_EPOCH + timedelta(microseconds=chrome_ts)
|
||||
return dt.isoformat() + "Z"
|
||||
except (OverflowError, ValueError):
|
||||
return ""
|
||||
|
||||
|
||||
def firefox_time_to_utc(ff_ts: int) -> str:
|
||||
"""Convert Firefox timestamp (microseconds since Unix epoch) to ISO UTC."""
|
||||
if not ff_ts or ff_ts < 0:
|
||||
return ""
|
||||
try:
|
||||
dt = UNIX_EPOCH + timedelta(microseconds=ff_ts)
|
||||
return dt.isoformat() + "Z"
|
||||
except (OverflowError, ValueError):
|
||||
return ""
|
||||
|
||||
|
||||
def extract_chrome_history(db_path: str, limit: int = 5000) -> List[dict]:
|
||||
"""Extract browsing history from Chrome/Edge History database."""
|
||||
if not os.path.exists(db_path):
|
||||
logger.warning("Chrome History DB not found: %s", db_path)
|
||||
return []
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT urls.url, urls.title, urls.last_visit_time, urls.visit_count, urls.typed_count
|
||||
FROM urls ORDER BY urls.last_visit_time DESC LIMIT ?
|
||||
""", (limit,))
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
results = []
|
||||
for url, title, last_visit, visit_count, typed_count in rows:
|
||||
results.append({
|
||||
"url": url, "title": title or "",
|
||||
"last_visit": chrome_time_to_utc(last_visit),
|
||||
"visit_count": visit_count, "typed_count": typed_count,
|
||||
})
|
||||
logger.info("Extracted %d Chrome history entries from %s", len(results), db_path)
|
||||
return results
|
||||
|
||||
|
||||
def extract_chrome_downloads(db_path: str, limit: int = 1000) -> List[dict]:
|
||||
"""Extract downloads from Chrome/Edge History database."""
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT current_path, tab_url, total_bytes, start_time, end_time, mime_type, danger_type
|
||||
FROM downloads ORDER BY start_time DESC LIMIT ?
|
||||
""", (limit,))
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [{
|
||||
"path": r[0], "source_url": r[1], "size_bytes": r[2],
|
||||
"start_time": chrome_time_to_utc(r[3]), "end_time": chrome_time_to_utc(r[4]),
|
||||
"mime_type": r[5], "danger_type": r[6],
|
||||
} for r in rows]
|
||||
|
||||
|
||||
def extract_chrome_cookies(db_path: str, limit: int = 5000) -> List[dict]:
|
||||
"""Extract cookies from Chrome Cookies database."""
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT host_key, name, path, creation_utc, last_access_utc, is_secure, is_httponly
|
||||
FROM cookies ORDER BY last_access_utc DESC LIMIT ?
|
||||
""", (limit,))
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [{
|
||||
"host": r[0], "name": r[1], "path": r[2],
|
||||
"created": chrome_time_to_utc(r[3]), "last_access": chrome_time_to_utc(r[4]),
|
||||
"secure": bool(r[5]), "httponly": bool(r[6]),
|
||||
} for r in rows]
|
||||
|
||||
|
||||
def extract_firefox_history(db_path: str, limit: int = 5000) -> List[dict]:
|
||||
"""Extract browsing history from Firefox places.sqlite."""
|
||||
if not os.path.exists(db_path):
|
||||
logger.warning("Firefox places.sqlite not found: %s", db_path)
|
||||
return []
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT moz_places.url, moz_places.title, moz_historyvisits.visit_date,
|
||||
moz_places.visit_count, moz_historyvisits.visit_type
|
||||
FROM moz_places
|
||||
JOIN moz_historyvisits ON moz_places.id = moz_historyvisits.place_id
|
||||
ORDER BY moz_historyvisits.visit_date DESC LIMIT ?
|
||||
""", (limit,))
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [{
|
||||
"url": r[0], "title": r[1] or "",
|
||||
"visit_date": firefox_time_to_utc(r[2]),
|
||||
"visit_count": r[3], "visit_type": r[4],
|
||||
} for r in rows]
|
||||
|
||||
|
||||
def extract_firefox_cookies(db_path: str, limit: int = 5000) -> List[dict]:
|
||||
"""Extract cookies from Firefox cookies.sqlite."""
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT host, name, path, creationTime, lastAccessed, isSecure, isHttpOnly
|
||||
FROM moz_cookies ORDER BY lastAccessed DESC LIMIT ?
|
||||
""", (limit,))
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [{
|
||||
"host": r[0], "name": r[1], "path": r[2],
|
||||
"created": firefox_time_to_utc(r[3]), "last_access": firefox_time_to_utc(r[4]),
|
||||
"secure": bool(r[5]), "httponly": bool(r[6]),
|
||||
} for r in rows]
|
||||
|
||||
|
||||
def export_to_csv(data: List[dict], output_path: str) -> None:
|
||||
"""Export extracted data to CSV."""
|
||||
if not data:
|
||||
return
|
||||
with open(output_path, "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=data[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(data)
|
||||
logger.info("Exported %d rows to %s", len(data), output_path)
|
||||
|
||||
|
||||
def generate_report(chrome_dir: str = "", firefox_dir: str = "",
|
||||
output_dir: str = ".") -> dict:
|
||||
"""Generate comprehensive browser forensics report."""
|
||||
report = {"analysis_date": datetime.utcnow().isoformat(), "browsers": {}}
|
||||
|
||||
if chrome_dir and os.path.isdir(chrome_dir):
|
||||
history = extract_chrome_history(os.path.join(chrome_dir, "History"))
|
||||
downloads = extract_chrome_downloads(os.path.join(chrome_dir, "History"))
|
||||
cookies = extract_chrome_cookies(os.path.join(chrome_dir, "Cookies"))
|
||||
report["browsers"]["chrome"] = {
|
||||
"history_count": len(history), "download_count": len(downloads),
|
||||
"cookie_count": len(cookies),
|
||||
}
|
||||
export_to_csv(history, os.path.join(output_dir, "chrome_history.csv"))
|
||||
export_to_csv(downloads, os.path.join(output_dir, "chrome_downloads.csv"))
|
||||
|
||||
if firefox_dir and os.path.isdir(firefox_dir):
|
||||
history = extract_firefox_history(os.path.join(firefox_dir, "places.sqlite"))
|
||||
cookies = extract_firefox_cookies(os.path.join(firefox_dir, "cookies.sqlite"))
|
||||
report["browsers"]["firefox"] = {
|
||||
"history_count": len(history), "cookie_count": len(cookies),
|
||||
}
|
||||
export_to_csv(history, os.path.join(output_dir, "firefox_history.csv"))
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Browser History Extraction Agent")
|
||||
parser.add_argument("--chrome-dir", default="", help="Path to Chrome/Edge User Data/Default")
|
||||
parser.add_argument("--firefox-dir", default="", help="Path to Firefox profile directory")
|
||||
parser.add_argument("--output-dir", default=".", help="Output directory for CSVs and report")
|
||||
parser.add_argument("--output", default="browser_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
report = generate_report(args.chrome_dir, args.firefox_dir, args.output_dir)
|
||||
with open(os.path.join(args.output_dir, args.output), "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Report saved")
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user