mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-28 04:09: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.
|
||||
@@ -0,0 +1,87 @@
|
||||
# API Reference: Analyzing Windows LNK Files for Artifacts
|
||||
|
||||
## LnkParse3
|
||||
|
||||
### Parse a Single LNK File
|
||||
|
||||
```python
|
||||
import LnkParse3
|
||||
|
||||
with open("shortcut.lnk", "rb") as f:
|
||||
lnk = LnkParse3.lnk_file(f)
|
||||
info = lnk.get_json()
|
||||
|
||||
# Access header timestamps
|
||||
header = info["header"]
|
||||
print(header["creation_time"], header["modified_time"], header["accessed_time"])
|
||||
|
||||
# Access target path
|
||||
link_info = info.get("link_info", {})
|
||||
print(link_info.get("local_base_path"))
|
||||
|
||||
# Access volume info
|
||||
vol = link_info.get("volume_id", {})
|
||||
print(vol.get("drive_type"), vol.get("drive_serial_number"))
|
||||
|
||||
# Access tracker data (machine ID, MAC)
|
||||
extra = info.get("extra", {})
|
||||
tracker = extra.get("DISTRIBUTED_LINK_TRACKER_BLOCK", {})
|
||||
print(tracker.get("machine_id"), tracker.get("mac_address"))
|
||||
```
|
||||
|
||||
### LNK JSON Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"header": {
|
||||
"creation_time": "2024-01-15 14:32:00",
|
||||
"modified_time": "2024-01-15 14:32:00",
|
||||
"accessed_time": "2024-01-15 14:32:00",
|
||||
"file_size": 45056
|
||||
},
|
||||
"link_info": {
|
||||
"local_base_path": "E:\\Documents\\report.xlsx",
|
||||
"volume_id": {
|
||||
"drive_type": "DRIVE_REMOVABLE",
|
||||
"drive_serial_number": "1234-ABCD",
|
||||
"volume_label": "KINGSTON"
|
||||
}
|
||||
},
|
||||
"string_data": {
|
||||
"working_dir": "E:\\Documents",
|
||||
"command_line_arguments": ""
|
||||
},
|
||||
"extra": {
|
||||
"DISTRIBUTED_LINK_TRACKER_BLOCK": {
|
||||
"machine_id": "DESKTOP-ABC123",
|
||||
"mac_address": "AA:BB:CC:DD:EE:FF"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key LNK File Locations
|
||||
|
||||
| Location | Description |
|
||||
|----------|-------------|
|
||||
| `%APPDATA%\Microsoft\Windows\Recent\` | Recently accessed files |
|
||||
| `%APPDATA%\...\Recent\AutomaticDestinations\` | Jump Lists |
|
||||
| `%APPDATA%\...\Recent\CustomDestinations\` | Pinned Jump List items |
|
||||
| `%USERPROFILE%\Desktop\` | Desktop shortcuts |
|
||||
| `%APPDATA%\...\Startup\` | User startup (persistence) |
|
||||
| `%PROGRAMDATA%\...\Startup\` | System startup (persistence) |
|
||||
|
||||
## Drive Types
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| DRIVE_REMOVABLE | USB, SD card |
|
||||
| DRIVE_FIXED | Internal HDD/SSD |
|
||||
| DRIVE_REMOTE | Network share |
|
||||
| DRIVE_CDROM | Optical media |
|
||||
|
||||
### References
|
||||
|
||||
- LnkParse3: https://pypi.org/project/LnkParse3/
|
||||
- Shell Link Binary Format: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/
|
||||
- LECmd: https://github.com/EricZimmerman/LECmd
|
||||
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for analyzing Windows LNK shortcut files for forensic artifacts."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import csv
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import LnkParse3
|
||||
|
||||
|
||||
def parse_lnk_file(filepath):
|
||||
"""Parse a single LNK file and extract forensic artifacts."""
|
||||
with open(filepath, "rb") as f:
|
||||
lnk = LnkParse3.lnk_file(f)
|
||||
info = lnk.get_json()
|
||||
|
||||
parsed = {
|
||||
"lnk_file": os.path.basename(filepath),
|
||||
"target_path": "",
|
||||
"working_dir": "",
|
||||
"arguments": "",
|
||||
"target_created": "",
|
||||
"target_modified": "",
|
||||
"target_accessed": "",
|
||||
"file_size": "",
|
||||
"drive_type": "",
|
||||
"volume_serial": "",
|
||||
"volume_label": "",
|
||||
"machine_id": "",
|
||||
"mac_address": "",
|
||||
}
|
||||
|
||||
header = info.get("header", {})
|
||||
parsed["target_created"] = str(header.get("creation_time", ""))
|
||||
parsed["target_modified"] = str(header.get("modified_time", ""))
|
||||
parsed["target_accessed"] = str(header.get("accessed_time", ""))
|
||||
parsed["file_size"] = str(header.get("file_size", ""))
|
||||
|
||||
link_info = info.get("link_info", {})
|
||||
if link_info:
|
||||
local_path = link_info.get("local_base_path", "")
|
||||
net_link = link_info.get("common_network_relative_link", {})
|
||||
network_path = net_link.get("net_name", "") if net_link else ""
|
||||
parsed["target_path"] = local_path or network_path
|
||||
|
||||
vol_info = link_info.get("volume_id", {})
|
||||
if vol_info:
|
||||
parsed["drive_type"] = str(vol_info.get("drive_type", ""))
|
||||
parsed["volume_serial"] = str(vol_info.get("drive_serial_number", ""))
|
||||
parsed["volume_label"] = str(vol_info.get("volume_label", ""))
|
||||
|
||||
string_data = info.get("string_data", {})
|
||||
parsed["working_dir"] = str(string_data.get("working_dir", ""))
|
||||
parsed["arguments"] = str(string_data.get("command_line_arguments", ""))
|
||||
|
||||
extra = info.get("extra", {})
|
||||
tracker = extra.get("DISTRIBUTED_LINK_TRACKER_BLOCK", {})
|
||||
if tracker:
|
||||
parsed["machine_id"] = str(tracker.get("machine_id", ""))
|
||||
parsed["mac_address"] = str(tracker.get("mac_address", ""))
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
def parse_lnk_directory(directory):
|
||||
"""Parse all LNK files in a directory."""
|
||||
results = []
|
||||
for filename in sorted(os.listdir(directory)):
|
||||
if not filename.lower().endswith(".lnk"):
|
||||
continue
|
||||
filepath = os.path.join(directory, filename)
|
||||
try:
|
||||
parsed = parse_lnk_file(filepath)
|
||||
results.append(parsed)
|
||||
except Exception as e:
|
||||
print(f" Error parsing {filename}: {e}")
|
||||
return results
|
||||
|
||||
|
||||
def filter_removable_media(results):
|
||||
"""Filter LNK files that point to removable media."""
|
||||
return [r for r in results if "removable" in r.get("drive_type", "").lower()]
|
||||
|
||||
|
||||
def filter_network_shares(results):
|
||||
"""Filter LNK files pointing to network shares."""
|
||||
return [
|
||||
r for r in results
|
||||
if "network" in r.get("drive_type", "").lower()
|
||||
or r.get("target_path", "").startswith("\\\\")
|
||||
]
|
||||
|
||||
|
||||
def detect_suspicious_startup(startup_dir):
|
||||
"""Analyze Startup folder LNK files for potential persistence."""
|
||||
suspicious = []
|
||||
for filename in os.listdir(startup_dir):
|
||||
if not filename.lower().endswith(".lnk"):
|
||||
continue
|
||||
filepath = os.path.join(startup_dir, filename)
|
||||
try:
|
||||
parsed = parse_lnk_file(filepath)
|
||||
target = parsed["target_path"].lower()
|
||||
args = parsed["arguments"].lower()
|
||||
if any(s in target for s in ["temp", "appdata", "programdata", "public"]):
|
||||
parsed["risk"] = "HIGH"
|
||||
suspicious.append(parsed)
|
||||
elif any(s in args for s in ["-enc", "powershell", "cmd /c", "wscript"]):
|
||||
parsed["risk"] = "HIGH"
|
||||
suspicious.append(parsed)
|
||||
except Exception:
|
||||
pass
|
||||
return suspicious
|
||||
|
||||
|
||||
def export_csv(results, output_path):
|
||||
"""Export parsed LNK results to CSV."""
|
||||
if not results:
|
||||
return
|
||||
with open(output_path, "w", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=results[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(results)
|
||||
|
||||
|
||||
def extract_unique_machines(results):
|
||||
"""Extract unique machine IDs and MAC addresses from LNK files."""
|
||||
machines = {}
|
||||
for r in results:
|
||||
mid = r.get("machine_id", "")
|
||||
mac = r.get("mac_address", "")
|
||||
if mid:
|
||||
machines[mid] = mac
|
||||
return machines
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Windows LNK File Forensic Analysis Agent")
|
||||
parser.add_argument("--lnk-dir", required=True, help="Directory containing LNK files")
|
||||
parser.add_argument("--startup-dir", help="Startup folder to check for persistence")
|
||||
parser.add_argument("--output-dir", default="./lnk_analysis")
|
||||
parser.add_argument("--action", choices=[
|
||||
"parse_all", "removable", "network", "startup", "machines", "full_analysis"
|
||||
], default="full_analysis")
|
||||
args = parser.parse_args()
|
||||
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
all_results = parse_lnk_directory(args.lnk_dir)
|
||||
print(f"[+] Parsed {len(all_results)} LNK files")
|
||||
|
||||
if args.action in ("parse_all", "full_analysis"):
|
||||
csv_path = os.path.join(args.output_dir, "lnk_analysis.csv")
|
||||
export_csv(all_results, csv_path)
|
||||
print(f"[+] Exported to {csv_path}")
|
||||
|
||||
if args.action in ("removable", "full_analysis"):
|
||||
removable = filter_removable_media(all_results)
|
||||
print(f"[+] Removable media files: {len(removable)}")
|
||||
for r in removable:
|
||||
print(f" {r['target_modified']} | {r['target_path']} | Vol: {r['volume_serial']}")
|
||||
|
||||
if args.action in ("network", "full_analysis"):
|
||||
network = filter_network_shares(all_results)
|
||||
print(f"[+] Network share files: {len(network)}")
|
||||
|
||||
if args.action in ("startup", "full_analysis") and args.startup_dir:
|
||||
suspicious = detect_suspicious_startup(args.startup_dir)
|
||||
print(f"[+] Suspicious startup LNK: {len(suspicious)}")
|
||||
for s in suspicious:
|
||||
print(f" [{s.get('risk')}] {s['lnk_file']} -> {s['target_path']}")
|
||||
|
||||
if args.action in ("machines", "full_analysis"):
|
||||
machines = extract_unique_machines(all_results)
|
||||
print(f"[+] Unique machines: {len(machines)}")
|
||||
for mid, mac in machines.items():
|
||||
print(f" Machine: {mid} | MAC: {mac}")
|
||||
|
||||
print(json.dumps({"total_lnk": len(all_results), "generated_at": datetime.utcnow().isoformat()}, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user