Files
Anthropic-Cybersecurity-Skills/skills/acquiring-disk-image-with-dd-and-dcfldd/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

182 lines
6.7 KiB
Python

#!/usr/bin/env python3
"""Forensic disk image acquisition agent using dd and dcfldd with hash verification."""
import shlex
import subprocess
import hashlib
import os
import datetime
import json
def run_cmd(cmd, capture=True):
"""Execute a command and return output."""
if isinstance(cmd, str):
cmd = shlex.split(cmd)
result = subprocess.run(cmd, capture_output=capture, text=True, timeout=120)
return result.stdout.strip(), result.stderr.strip(), result.returncode
def list_block_devices():
"""Enumerate connected block devices."""
stdout, _, rc = run_cmd("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,MODEL,SERIAL,RO")
if rc == 0 and stdout:
return json.loads(stdout)
return {"blockdevices": []}
def check_write_protection(device):
"""Verify a device is set to read-only mode."""
stdout, _, rc = run_cmd(f"blockdev --getro {device}")
if rc == 0:
return stdout.strip() == "1"
return False
def enable_write_protection(device):
"""Enable software write-blocking on the target device."""
_, _, rc = run_cmd(f"blockdev --setro {device}")
if rc != 0:
print(f"[ERROR] Failed to set {device} read-only. Run as root.")
return False
if check_write_protection(device):
print(f"[OK] Write protection enabled on {device}")
return True
print(f"[ERROR] Write protection verification failed for {device}")
return False
def compute_hash(path, algorithm="sha256", block_size=65536):
"""Compute the SHA-256 or MD5 hash of a file or device."""
h = hashlib.new(algorithm)
try:
with open(path, "rb") as f:
while True:
block = f.read(block_size)
if not block:
break
h.update(block)
except PermissionError:
print(f"[ERROR] Permission denied reading {path}. Run as root.")
return None
except FileNotFoundError:
print(f"[ERROR] Path not found: {path}")
return None
return h.hexdigest()
def acquire_with_dd(source, destination, block_size=4096, log_file=None):
"""Acquire a forensic image using dd with error handling."""
dd_cmd = [
"dd", f"if={source}", f"of={destination}",
f"bs={block_size}", "conv=noerror,sync", "status=progress"
]
print(f"[*] Starting dd acquisition: {source} -> {destination}")
print(f"[*] Block size: {block_size}")
start = datetime.datetime.utcnow()
if log_file:
dd_proc = subprocess.run(dd_cmd, capture_output=True, text=True, timeout=120)
combined = (dd_proc.stdout or "") + (dd_proc.stderr or "")
with open(log_file, "w") as lf:
lf.write(combined)
rc = dd_proc.returncode
else:
result = subprocess.run(dd_cmd, text=True, timeout=120)
rc = result.returncode
elapsed = (datetime.datetime.utcnow() - start).total_seconds()
print(f"[*] Acquisition completed in {elapsed:.1f} seconds (rc={rc})")
return rc == 0
def acquire_with_dcfldd(source, destination, hash_alg="sha256", hash_log=None,
error_log=None, block_size=4096, split_size=None):
"""Acquire a forensic image using dcfldd with built-in hashing."""
cmd = [
"dcfldd", f"if={source}", f"of={destination}",
f"bs={block_size}", "conv=noerror,sync",
f"hash={hash_alg}", "hashwindow=1G",
]
if hash_log:
cmd.append(f"hashlog={hash_log}")
if error_log:
cmd.append(f"errlog={error_log}")
if split_size:
cmd.extend([f"split={split_size}", "splitformat=aa"])
print(f"[*] Starting dcfldd acquisition: {source} -> {destination}")
start = datetime.datetime.utcnow()
result = subprocess.run(cmd, text=True, timeout=120)
rc = result.returncode
elapsed = (datetime.datetime.utcnow() - start).total_seconds()
print(f"[*] dcfldd completed in {elapsed:.1f} seconds (rc={rc})")
return rc == 0
def verify_image(source, image_path, algorithm="sha256"):
"""Verify image integrity by comparing hashes of source and acquired image."""
print(f"[*] Computing {algorithm} hash of source: {source}")
source_hash = compute_hash(source, algorithm)
print(f" Source hash: {source_hash}")
print(f"[*] Computing {algorithm} hash of image: {image_path}")
image_hash = compute_hash(image_path, algorithm)
print(f" Image hash: {image_hash}")
if source_hash and image_hash:
match = source_hash == image_hash
status = "PASSED" if match else "FAILED"
print(f"[{'OK' if match else 'FAIL'}] Verification: {status}")
return match, source_hash, image_hash
return False, source_hash, image_hash
def generate_report(case_dir, source_device, image_path, tool_used,
source_hash, image_hash, verified, elapsed_seconds=0):
"""Generate a forensic acquisition report."""
report = {
"report_type": "Disk Image Acquisition",
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
"case_directory": case_dir,
"source_device": source_device,
"image_file": image_path,
"acquisition_tool": tool_used,
"block_size": 4096,
"source_hash_sha256": source_hash,
"image_hash_sha256": image_hash,
"hash_verified": verified,
"duration_seconds": elapsed_seconds,
}
report_path = os.path.join(case_dir, "acquisition_report.json")
with open(report_path, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {report_path}")
return report
if __name__ == "__main__":
print("=" * 60)
print("Forensic Disk Image Acquisition Agent")
print("Tools: dd / dcfldd with SHA-256 verification")
print("=" * 60)
# Demo: list block devices
print("\n[*] Enumerating block devices...")
devices = list_block_devices()
for dev in devices.get("blockdevices", []):
name = dev.get("name", "?")
size = dev.get("size", "?")
dtype = dev.get("type", "?")
model = dev.get("model", "N/A")
ro = "RO" if dev.get("ro") else "RW"
print(f" /dev/{name} {size} {dtype} {model} [{ro}]")
# Demo workflow (dry run)
demo_source = "/dev/sdb"
demo_case = "/cases/demo-case/images"
demo_image = os.path.join(demo_case, "evidence.dd")
print(f"\n[DEMO] Acquisition workflow for {demo_source}:")
print(f" 1. Enable write protection: blockdev --setro {demo_source}")
print(f" 2. Acquire with dcfldd: dcfldd if={demo_source} of={demo_image} "
f"hash=sha256 hashwindow=1G bs=4096 conv=noerror,sync")
print(f" 3. Verify: compare SHA-256 of {demo_source} and {demo_image}")
print(f" 4. Generate acquisition report with chain-of-custody metadata")
print("\n[*] Agent ready. Provide a source device and case directory to begin.")