reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
name: handling-malware-samples-safely
|
||||
description: 'Establishes safe practices for acquiring, storing, transferring, and disposing
|
||||
of malware samples: password-protected archives, neutralized extensions, hashing for
|
||||
identity, and chain-of-custody. Activates for requests about safely storing or sharing
|
||||
malware, sample handling hygiene, or defanging artifacts.'
|
||||
domain: cybersecurity
|
||||
subdomain: lab-foundations
|
||||
tags:
|
||||
- malware
|
||||
- sample-handling
|
||||
- hashing
|
||||
- defanging
|
||||
- chain-of-custody
|
||||
- hygiene
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
d3fend:
|
||||
- D3-FA
|
||||
references:
|
||||
- 'MalwareBazaar — https://bazaar.abuse.ch/'
|
||||
- 'NIST SP 800-86 Guide to Integrating Forensic Techniques — https://csrc.nist.gov/pubs/sp/800/86/final'
|
||||
---
|
||||
|
||||
# Handling Malware Samples Safely
|
||||
|
||||
## When to Use
|
||||
|
||||
- You are receiving, storing, or sharing a malicious file and need to prevent accidental
|
||||
execution or contamination.
|
||||
- You need a consistent naming and identity scheme so a sample can be tracked across tools
|
||||
and tickets.
|
||||
- You are packaging a sample to send to another analyst or a sandbox.
|
||||
|
||||
**Do not use** these conventions as a substitute for an isolated detonation environment; safe
|
||||
storage prevents accidents but analysis still requires an isolated lab.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An archive tool that supports password-protected, encrypted archives (7-Zip).
|
||||
- A hashing utility (`sha256sum`, `certutil`, or the bundled script).
|
||||
- A defined storage location with restricted access, separate from general file shares.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Treat every sample as live. Never double-click; never leave it with its original
|
||||
executable extension on a working machine.
|
||||
- Store samples inside an **encrypted, password-protected archive** with the conventional
|
||||
password `infected`.
|
||||
- **Neutralize the extension** on disk (e.g. `sample.exe` → `sample.exe.bin` or
|
||||
`sample.exe_`) so the OS will not execute it on a stray click.
|
||||
- **Defang indicators** in any text leaving the lab: `hxxp://`, `1.2.3[.]4`, `evil[.]com`.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Identify the sample by hash
|
||||
|
||||
A sample's identity is its SHA-256, not its filename. Compute hashes immediately:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py hash sample.exe
|
||||
# md5, sha1, sha256, size, ssdeep-like size bucket
|
||||
```
|
||||
|
||||
Rename the working copy to its SHA-256 so identity travels with the file:
|
||||
|
||||
```bash
|
||||
mv sample.exe 9f86d0818...<sha256>.bin
|
||||
```
|
||||
|
||||
### Step 2: Store in an encrypted archive
|
||||
|
||||
```bash
|
||||
7z a -p"infected" -mhe=on <sha256>.7z <sha256>.bin
|
||||
# -mhe=on encrypts filenames too
|
||||
```
|
||||
|
||||
Keep the loose, neutralized copy only inside the lab; the archive is the storage/transfer
|
||||
form.
|
||||
|
||||
### Step 3: Record provenance
|
||||
|
||||
Capture where the sample came from, when, and who handled it. The script can emit a JSON
|
||||
metadata record:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py record <sha256>.bin --source "MalwareBazaar" --case CASE-001
|
||||
```
|
||||
|
||||
### Step 4: Defang before sharing
|
||||
|
||||
When pasting URLs/IPs into a report or ticket, defang them:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py defang ioc.txt
|
||||
# http://evil.com/x -> hxxp://evil[.]com/x
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
- `7z l -slt <sha256>.7z` shows encrypted headers (filenames not visible without password).
|
||||
- The on-disk working copy has a neutralized extension and matches the recorded SHA-256.
|
||||
- The provenance record contains source, timestamp, case ID, and all three hashes.
|
||||
- No raw clickable URLs/IPs remain in any artifact destined to leave the lab.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Sharing a sample in a plain zip — mail gateways and AV will quarantine or mangle it, and
|
||||
recipients risk accidental execution. Always password-encrypt.
|
||||
- Trusting the filename or first-seen label as identity; only the hash is authoritative.
|
||||
- Forgetting filename encryption (`-mhe=on`), which leaks the sample name and your naming
|
||||
scheme.
|
||||
- Defanging only URLs but leaving live IPs or email addresses in the report.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the hashing,
|
||||
provenance, and defang tooling.
|
||||
- MalwareBazaar and NIST SP 800-86 (linked in frontmatter).
|
||||
@@ -0,0 +1,47 @@
|
||||
# API Reference: Handling Malware Samples Safely
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` provides three safe, read-only operations: computing a sample's hash
|
||||
identity, emitting a provenance record, and defanging IOCs before sharing. It never executes
|
||||
a sample.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | Hashing, JSON, regex |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| 7-Zip | Encrypted, password-protected sample archives (`-p`, `-mhe=on`) |
|
||||
| certutil / sha256sum | Hashing outside Python where convenient |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `compute_hashes(filepath)`
|
||||
Returns `md5`, `sha1`, `sha256`, `size` for identification.
|
||||
|
||||
### `make_record(filepath, source, case)`
|
||||
Returns a provenance dict: case, source, filename, UTC timestamp, and hashes.
|
||||
|
||||
### `defang(text)`
|
||||
Rewrites `http(s)://` to `hxxp(s)://` and brackets dots in URLs/IPs so indicators are not
|
||||
clickable.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py hash sample.bin
|
||||
python scripts/analyst.py record sample.bin --source MalwareBazaar --case CASE-001
|
||||
python scripts/analyst.py defang iocs.txt
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- MalwareBazaar (abuse.ch) sample-sharing conventions — https://bazaar.abuse.ch/
|
||||
- NIST SP 800-86, Guide to Integrating Forensic Techniques into Incident Response —
|
||||
https://csrc.nist.gov/pubs/sp/800/86/final
|
||||
- 7-Zip command-line documentation (vendor)
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Safe sample-handling helpers: hashing, provenance records, and IOC defanging.
|
||||
|
||||
Operates only on file bytes and text — it never executes a sample. Use it to compute a
|
||||
sample's identity, emit a provenance record, and defang indicators before they leave the lab.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as _dt
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_DEFANG_SCHEME = re.compile(r"\bhttp(s?)://", re.I)
|
||||
_DEFANG_DOT = re.compile(r"\.(?=[A-Za-z0-9])")
|
||||
_IPV4 = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
|
||||
|
||||
|
||||
def compute_hashes(filepath: str | Path) -> dict:
|
||||
data = Path(filepath).read_bytes()
|
||||
return {
|
||||
"md5": hashlib.md5(data).hexdigest(), # noqa: S324 - identification only
|
||||
"sha1": hashlib.sha1(data).hexdigest(), # noqa: S324
|
||||
"sha256": hashlib.sha256(data).hexdigest(),
|
||||
"size": len(data),
|
||||
}
|
||||
|
||||
|
||||
def make_record(filepath: str | Path, source: str, case: str) -> dict:
|
||||
rec = {
|
||||
"case": case,
|
||||
"source": source,
|
||||
"filename": Path(filepath).name,
|
||||
"recorded_at": _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
}
|
||||
rec.update(compute_hashes(filepath))
|
||||
return rec
|
||||
|
||||
|
||||
def defang(text: str) -> str:
|
||||
"""Render URLs/IPs non-clickable for safe sharing."""
|
||||
def _ip(m: re.Match) -> str:
|
||||
return m.group(0).replace(".", "[.]")
|
||||
text = _IPV4.sub(_ip, text)
|
||||
text = _DEFANG_SCHEME.sub(lambda m: "hxxp" + ("s" if m.group(1) else "") + "://", text)
|
||||
# bracket the dot in domains within URLs only (lines containing hxxp)
|
||||
out_lines = []
|
||||
for line in text.splitlines():
|
||||
if "hxxp" in line:
|
||||
line = _DEFANG_DOT.sub("[.]", line)
|
||||
out_lines.append(line)
|
||||
return "\n".join(out_lines)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Safe sample-handling helpers")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
p_hash = sub.add_parser("hash", help="compute file hashes")
|
||||
p_hash.add_argument("file")
|
||||
|
||||
p_rec = sub.add_parser("record", help="emit a provenance JSON record")
|
||||
p_rec.add_argument("file")
|
||||
p_rec.add_argument("--source", required=True)
|
||||
p_rec.add_argument("--case", required=True)
|
||||
|
||||
p_def = sub.add_parser("defang", help="defang URLs/IPs in a text file")
|
||||
p_def.add_argument("file")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.cmd == "hash":
|
||||
print(json.dumps(compute_hashes(args.file), indent=2))
|
||||
elif args.cmd == "record":
|
||||
print(json.dumps(make_record(args.file, args.source, args.case), indent=2))
|
||||
elif args.cmd == "defang":
|
||||
print(defang(Path(args.file).read_text(encoding="utf-8", errors="replace")))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user