reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,100 @@
---
name: analyzing-android-dex-malware
description: 'Reverses Android malware: unpacking APKs, decompiling DEX bytecode to readable
Java, auditing the manifest for abused permissions and components, and locating dynamically
loaded or native payloads. Activates for requests to analyze an APK, decompile DEX, or
investigate a suspicious Android app.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- android
- dex
- apk
- mobile
- decompilation
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1059
- T1406
- T1577
d3fend:
- D3-FCR
- D3-DA
references:
- 'Android DEX bytecode format — https://source.android.com/docs/core/runtime/dex-format'
- 'jadx DEX-to-Java decompiler — https://github.com/skylot/jadx'
---
# Analyzing Android DEX Malware
## When to Use
- You have a suspicious `.apk` (or bare `.dex`) and need to understand its behavior.
- You need to audit the manifest for dangerous permissions, exported components, and the
declared entry points.
- The app loads code dynamically (`DexClassLoader`) or ships a native `.so` you must locate.
**Do not use** this workflow for iOS apps — DEX/APK tooling does not apply to Mach-O/IPA.
## Prerequisites
- jadx (or apktool + a decompiler) and `unzip`; an Android emulator/sandbox for dynamic runs.
- `aapt`/manifest parsing for permissions and components.
## Workflow
### Step 1: Unpack the APK
An APK is a ZIP. Extract and inventory the DEX files, native libraries, and assets:
```bash
python scripts/analyst.py inspect sample.apk
```
```text
dex : classes.dex, classes2.dex
native : lib/arm64-v8a/libpayload.so
assets : assets/config.enc (possible encrypted payload)
manifest : AndroidManifest.xml (binary)
```
### Step 2: Audit the manifest
Decode `AndroidManifest.xml` and review requested permissions (SMS, accessibility, device
admin, `REQUEST_INSTALL_PACKAGES`), exported components, and the launcher/`BOOT_COMPLETED`
receivers.
### Step 3: Decompile DEX
Run jadx to recover Java. Start at the launcher activity and any `BroadcastReceiver`/`Service`
declared in the manifest.
### Step 4: Find dynamic and native code
Search for `DexClassLoader`/`loadDex`, asset decryption, and `System.loadLibrary`. Dump and
recurse on dynamically loaded DEX; analyze native `.so` separately if needed.
### Step 5: Extract behavior and IOCs
Recover C2 URLs, overlay/accessibility abuse, SMS interception, and config; map to ATT&CK for
mobile in the report.
## Validation
- Every DEX and native library in the APK is accounted for.
- Dangerous permissions are tied to concrete code paths (not just declared).
- Dynamically loaded payloads are dumped and analyzed, not just noted.
## Pitfalls
- Reading only `classes.dex` and missing `classes2.dex`/`classes3.dex`.
- Trusting the manifest alone; behavior may hide behind dynamic loading.
- Ignoring encrypted assets that become the real payload at runtime.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the APK inspector.
- Android DEX format and jadx (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Analyzing Android DEX Malware
## Overview
`scripts/analyst.py` treats an APK as a ZIP and inventories its DEX files, native libraries,
assets, and manifest presence, flagging multidex and likely encrypted payload assets. It does
not decompile or execute — use jadx/apktool for decompilation.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | zipfile, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| jadx | Decompile DEX bytecode to Java |
| apktool | Decode resources and binary AndroidManifest.xml |
| Android emulator | Dynamic analysis of dynamic/native payloads |
## Core functions
### `inspect(path)`
Returns the DEX list, native libraries, assets, manifest presence, multidex flag, and triage
notes.
## Usage
```bash
python scripts/analyst.py inspect sample.apk
```
## Sources
- Android DEX bytecode format — https://source.android.com/docs/core/runtime/dex-format
- APK packaging (ZIP container, AndroidManifest.xml) — Android documentation
- jadx DEX-to-Java decompiler — https://github.com/skylot/jadx

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Inspect an APK (ZIP) and inventory its DEX, native libraries, and assets.
Lists the components relevant to triage — DEX files, native ``.so`` libraries, the manifest,
and notable assets — without decompiling or executing anything. Use jadx/apktool for the
actual decompilation step.
"""
from __future__ import annotations
import argparse
import json
import sys
import zipfile
from pathlib import Path
def inspect(path: str) -> dict:
if not zipfile.is_zipfile(path):
return {"error": "not a ZIP/APK"}
dex, native, assets, other = [], [], [], []
with zipfile.ZipFile(path) as zf:
for name in zf.namelist():
if name.endswith(".dex"):
dex.append(name)
elif name.startswith("lib/") and name.endswith(".so"):
native.append(name)
elif name.startswith("assets/"):
assets.append(name)
elif name == "AndroidManifest.xml":
other.append(name)
return {
"dex": sorted(dex),
"native_libs": sorted(native),
"assets": sorted(assets),
"has_manifest": "AndroidManifest.xml" in other,
"multidex": len(dex) > 1,
"notes": _notes(dex, native, assets),
}
def _notes(dex, native, assets) -> list[str]:
notes = []
if len(dex) > 1:
notes.append("multidex: analyze all classesN.dex")
if native:
notes.append("native libraries present: analyze .so separately")
enc = [a for a in assets if a.endswith((".enc", ".dat", ".bin"))]
if enc:
notes.append(f"possible encrypted payload assets: {enc}")
return notes
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Inspect an APK")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("inspect")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(inspect(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,83 @@
---
name: analyzing-api-call-traces
description: 'Analyzes API call traces from a sandbox or API monitor (JSON) to group calls by
category, reconstruct high-level behaviors (process injection, file drops, network, crypto), and
flag suspicious call sequences. Activates for requests to analyze an API trace, interpret sandbox
API logs, or identify behavior from Win32/Native API calls.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- dynamic-analysis
- api-trace
- winapi
- sandbox
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1106
- T1055
- T1027
d3fend:
- D3-PSA
- D3-SDA
references:
- 'MITRE ATT&CK T1106 Native API — https://attack.mitre.org/techniques/T1106/'
- 'Windows API Index — https://learn.microsoft.com/windows/win32/apiindex/windows-api-list'
---
# Analyzing API Call Traces
## When to Use
- You have a JSON API trace (CAPE/Cuckoo "apistats"/calls, API Monitor export) from a detonation.
- You need to translate raw API calls into high-level behaviors and flag suspicious sequences
(e.g., `VirtualAllocEx` → `WriteProcessMemory` → `CreateRemoteThread`).
**Do not use** this as a substitute for detonation — it consumes an existing trace produced in an
isolated sandbox.
## Prerequisites
- A JSON API trace with at least an API name per call (and optionally arguments).
## Safety & Handling
- Consume traces only; never execute the sample to generate one outside a sandbox.
## Workflow
### Step 1: Group calls by category
```bash
python scripts/analyst.py classify trace.json
```
Buckets calls into process, memory, file, registry, network, crypto, and synchronization.
### Step 2: Detect behavior signatures
Matches ordered API sequences that indicate techniques — remote injection, hollowing,
self-deletion, dynamic API resolution (`LoadLibrary`/`GetProcAddress` bursts).
### Step 3: Prioritize and report
Rank detected behaviors by severity and map each to ATT&CK.
## Validation
- Each API call is categorized; unknown APIs are reported, not dropped silently.
- Injection-style sequences are detected only when the ordered prerequisites are present.
- Detected behaviors map to ATT&CK techniques.
## Pitfalls
- Flagging individual benign APIs out of context — the sequence matters.
- Assuming call order in the JSON equals execution order without a timestamp/index.
- Ignoring failed calls (non-zero error) that never actually executed the behavior.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the classifier.
- ATT&CK T1106 and the Windows API Index (linked in frontmatter).

View File

@@ -0,0 +1,42 @@
# API Reference: Analyzing API Call Traces
## Overview
`scripts/analyst.py` classifies a JSON API trace into behavioral categories and detects ordered
API sequences indicative of process injection, hollowing, dynamic API resolution, and crypto
routines. It parses JSON only and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | json, argparse |
## Accepted input shapes
- A list of strings (API names) or call objects with `api`/`name`/`function` keys.
- A dict containing `calls`, `apis`, or a CAPE-style `apistats` mapping.
## Core functions
### `classify(path)`
Returns total call count, per-category counts, detected behaviors (with ATT&CK IDs), and unknown
APIs.
## Sequence signatures
`VirtualAllocEx → WriteProcessMemory → CreateRemoteThread` (T1055.002), hollowing (T1055.012),
`LoadLibrary → GetProcAddress` (T1027.007), `CryptAcquireContext → CryptDecrypt` (T1027). Matching
respects order via a sliding cursor.
## Usage
```bash
python scripts/analyst.py classify trace.json
```
## Sources
- MITRE ATT&CK T1106 Native API; T1055 Process Injection — https://attack.mitre.org/
- Windows API Index — https://learn.microsoft.com/windows/win32/apiindex/windows-api-list
- CAPE/Cuckoo report `behavior.apistats` structure (public sandbox schema)

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Classify a JSON API call trace and detect behavior signatures.
Accepts a flexible JSON trace: a list of call objects, or a dict with a "calls"/"apis" list, or a
CAPE-style apistats mapping. Categorizes API names and matches ordered sequences indicative of
process injection, dynamic API resolution, and self-deletion. Parses JSON only; executes nothing.
"""
from __future__ import annotations
import argparse
import json
import sys
CATEGORIES = {
"process": ["createprocess", "openprocess", "createremotethread", "ntcreatethreadex",
"resumethread", "terminateprocess"],
"memory": ["virtualalloc", "virtualallocex", "virtualprotect", "writeprocessmemory",
"readprocessmemory", "ntmapviewofsection", "ntunmapviewofsection"],
"file": ["createfile", "writefile", "readfile", "deletefile", "copyfile", "movefile"],
"registry": ["regopenkey", "regsetvalue", "regcreatekey", "regqueryvalue"],
"network": ["internetopen", "internetconnect", "httpsendrequest", "wininet", "winhttp",
"send", "recv", "connect", "getaddrinfo"],
"crypto": ["cryptencrypt", "cryptdecrypt", "cryptacquirecontext", "bcryptencrypt",
"cryptstringtobinary"],
"resolve": ["loadlibrary", "getprocaddress", "ldrloaddll", "ldrgetprocedureaddress"],
"sync": ["createmutex", "openmutex", "createevent", "sleep", "waitforsingleobject"],
}
SIGNATURES = [
("remote_process_injection", ["virtualallocex", "writeprocessmemory", "createremotethread"],
"T1055.002"),
("process_hollowing", ["createprocess", "ntunmapviewofsection", "writeprocessmemory"],
"T1055.012"),
("dynamic_api_resolution", ["loadlibrary", "getprocaddress"], "T1027.007"),
("crypto_routine", ["cryptacquirecontext", "cryptdecrypt"], "T1027"),
]
def _extract_calls(obj) -> list[str]:
if isinstance(obj, list):
out = []
for c in obj:
if isinstance(c, str):
out.append(c)
elif isinstance(c, dict):
out.append(c.get("api") or c.get("name") or c.get("function") or "")
return [c for c in out if c]
if isinstance(obj, dict):
for key in ("calls", "apis", "apistats"):
if key in obj:
val = obj[key]
if isinstance(val, dict):
return list(val.keys())
return _extract_calls(val)
return []
def _category(api: str) -> str:
low = api.lower()
for cat, names in CATEGORIES.items():
if any(n in low for n in names):
return cat
return "other"
def classify(path: str) -> dict:
data = json.loads(open(path, encoding="utf-8-sig").read())
calls = _extract_calls(data)
lowered = [c.lower() for c in calls]
cats: dict[str, int] = {}
unknown = set()
for api in calls:
cat = _category(api)
cats[cat] = cats.get(cat, 0) + 1
if cat == "other":
unknown.add(api)
detected = []
for name, seq, tech in SIGNATURES:
pos = 0
for needle in seq:
found = next((i for i in range(pos, len(lowered)) if needle in lowered[i]), -1)
if found == -1:
break
pos = found + 1
else:
detected.append({"behavior": name, "attack": tech})
return {
"total_calls": len(calls),
"categories": cats,
"behaviors": detected,
"unknown_apis": sorted(unknown)[:50],
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Classify an API call trace")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("classify")
p.add_argument("trace")
args = parser.parse_args(argv)
print(json.dumps(classify(args.trace), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,91 @@
---
name: analyzing-authenticode-signatures
description: 'Analyzes Windows Authenticode signatures on PE files: checking for a signature,
reading the signer certificate chain, detecting revoked/expired/stolen certs, and recognizing
signature-stripping and catalog-signing abuse. Activates for requests to analyze a code
signature, verify Authenticode, or assess signer trust on a PE.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- authenticode
- code-signing
- pe
- certificates
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1553.002
- T1036.001
d3fend:
- D3-FCR
references:
- 'Windows Authenticode PE signature format — https://learn.microsoft.com/windows-hardware/drivers/install/authenticode'
- 'Microsoft PE/COFF specification (security directory) — https://learn.microsoft.com/windows/win32/debug/pe-format'
---
# Analyzing Authenticode Signatures
## When to Use
- You need to know whether a PE is signed and whether the signer is trustworthy.
- You suspect a stolen/abused code-signing certificate or a forged/invalid signature.
- You are distinguishing legitimately signed software from signed malware.
**Do not use** "is signed" as "is safe" — adversaries use stolen certs, abuse catalog signing,
and append invalid signatures; a valid signature only attests the signer, not benignity.
## Prerequisites
- Access to the PE security directory (Python `struct`/`pefile`); on Windows, `signtool`/PowerShell
`Get-AuthenticodeSignature` for authoritative verification.
## Safety & Handling
- Parse the signature statically; never execute the sample.
- Keep the sample password-protected at rest and reference it by hash.
## Workflow
### Step 1: Check for a signature
Read the PE security directory (certificate table). Absent entry = unsigned; present = embedded
PKCS#7. Note that valid signing can also be via a catalog file (not embedded).
```bash
python scripts/analyst.py check sample.exe
```
### Step 2: Extract signer details
Parse the PKCS#7 to read the signer certificate: subject, issuer, validity dates, and serial.
Cross-reference against known-abused/stolen certs.
### Step 3: Verify, do not just read
On Windows, verify the signature cryptographically and check revocation
(`Get-AuthenticodeSignature`). A present-but-invalid signature is a strong red flag.
### Step 4: Assess trust in context
Weigh signer reputation, certificate age, and whether the signed file's behavior matches the
purported publisher. Treat anomalies (recently issued cert, mismatched publisher) as suspicious.
## Validation
- Signature presence is determined from the security directory, and verified (not just read) on
Windows.
- Signer certificate details are extracted and checked against abuse intel.
- The trust conclusion accounts for revocation/validity, not mere presence.
## Pitfalls
- Equating a present signature with safety; stolen-cert malware is common.
- Missing catalog-signed files by only checking embedded signatures.
- Reading the signature without cryptographically verifying it.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the signature checker.
- Authenticode format and the PE/COFF security directory (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Analyzing Authenticode Signatures
## Overview
`scripts/analyst.py` parses the PE security data directory (index 4) to detect an embedded PKCS#7
Authenticode signature (WIN_CERTIFICATE), reporting presence, size, revision, and type. It is a
dependency-free presence check; cryptographic verification requires OS tooling. It parses bytes
statically and never executes the sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | struct parsing, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| signtool / Get-AuthenticodeSignature | Cryptographically verify the signature and revocation |
## Core functions
### `check(path)`
Returns whether an embedded signature exists and the WIN_CERTIFICATE header fields.
## Usage
```bash
python scripts/analyst.py check sample.exe
```
## Sources
- Windows Authenticode PE signature format —
https://learn.microsoft.com/windows-hardware/drivers/install/authenticode
- PE/COFF security data directory (index 4, file-offset semantics) —
https://learn.microsoft.com/windows/win32/debug/pe-format
- Stolen/abused signing certificates (T1553.002), MITRE ATT&CK

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Check a PE for an embedded Authenticode signature and summarize the certificate blob.
Parses the PE optional-header security data directory (index 4) to detect an embedded PKCS#7
signature (WIN_CERTIFICATE), reporting its presence, size, and revision/type. Deep certificate
parsing and cryptographic verification require a PKCS#7 library / OS tooling; this gives a fast,
dependency-free presence check. It parses bytes statically and never executes the sample.
"""
from __future__ import annotations
import argparse
import json
import struct
import sys
from pathlib import Path
WIN_CERT_TYPE = {0x0001: "X509", 0x0002: "PKCS_SIGNED_DATA", 0x0003: "RESERVED",
0x0004: "TS_STACK_SIGNED"}
def check(path: str) -> dict:
data = Path(path).read_bytes()
if data[:2] != b"MZ":
return {"error": "not a PE"}
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
return {"error": "no PE signature"}
opt = e_lfanew + 24
magic = struct.unpack_from("<H", data, opt)[0]
pe32_plus = magic == 0x20B
dd_offset = opt + (112 if pe32_plus else 96)
sec_dir = dd_offset + 4 * 8 # security directory is index 4
rva, size = struct.unpack_from("<II", data, sec_dir)
if not rva or not size:
return {"signed_embedded": False,
"note": "no embedded signature (may still be catalog-signed)"}
# For the security directory, 'rva' is a file offset (not an RVA)
dwlen, wrev, wtype = struct.unpack_from("<IHH", data, rva)
return {
"signed_embedded": True,
"certificate_table_offset": rva,
"certificate_table_size": size,
"win_cert_length": dwlen,
"revision": hex(wrev),
"type": WIN_CERT_TYPE.get(wtype, hex(wtype)),
"note": "presence only; verify cryptographically with signtool / "
"Get-AuthenticodeSignature",
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Check Authenticode signature presence")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("check")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(check(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,88 @@
---
name: analyzing-banking-trojan-webinjects
description: 'Analyzes banking trojan webinject configurations to extract targeted institutions,
injected JavaScript/HTML, and data-theft hooks, mapping the fraud workflow. Activates for
requests to analyze banking trojan webinjects, parse a webinject config, or map targeted banks
and credential-theft injections.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- banking-trojan
- webinject
- fraud
- credential-theft
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1185
- T1557
- T1056.001
d3fend:
- D3-SDA
- D3-DA
references:
- 'MITRE ATT&CK T1185 Browser Session Hijacking — https://attack.mitre.org/techniques/T1185/'
- 'Zeus/SpyEye webinject format (public malware research) — https://attack.mitre.org/software/S0259/'
---
# Analyzing Banking Trojan Webinjects
## When to Use
- You have a banking trojan webinject config (Zeus/Gozi/Dridex-style `set_url`/`data_before`/
`data_inject` blocks) and want to extract targeted institutions and injected code.
- You are mapping the web-fraud workflow (form-grabbing, fake fields, MFA interception).
**Do not use** this to deploy or test injects against live banking sites. This skill parses the
config statically and executes nothing.
## Prerequisites
- The decoded webinject config text (decrypt the config first if needed).
## Safety & Handling
- Treat injected JS/HTML as malicious; do not render it. Defang any exfil URLs.
## Workflow
### Step 1: Parse webinject blocks
```bash
python scripts/analyst.py parse webinjects.txt
```
Parses `set_url`/`data_before`/`data_inject`/`data_after` blocks into structured entries with the
targeted URL pattern and the injected snippet.
### Step 2: Extract targets and exfil endpoints
List targeted institution URL patterns and any exfil endpoints referenced by the injected code.
### Step 3: Classify the injection technique
Identify form-grabbing, fake additional fields (SSN, PIN, card), and MFA/OTP interception from the
injected markup/JS.
### Step 4: Defang and report
Defang URLs and summarize targeted banks and theft techniques.
## Validation
- Each webinject block is parsed into target URL + injection.
- Exfil endpoints in injected code are extracted and defanged.
- Injection techniques are classified from the snippet content.
## Pitfalls
- Encrypted/packed configs needing decryption before parsing.
- Wildcard URL patterns matching broadly — note the scope.
- Obfuscated injected JS hiding the true exfil endpoint.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the parser.
- ATT&CK T1185 and webinject format references (linked in frontmatter).

View File

@@ -0,0 +1,43 @@
# API Reference: Analyzing Banking Trojan Webinjects
## Overview
`scripts/analyst.py` parses Zeus/SpyEye-style webinject configs (`set_url` + `data_before`/
`data_inject`/`data_after` blocks), associates each target URL pattern with its injected snippet,
classifies the injection technique, and extracts/defangs exfil endpoints. It parses config text
only, executes nothing, and never renders injected code.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `parse(path)`
Returns webinject count, targeted URL patterns, and per-entry technique/exfil/preview details.
## Technique classes
- form_grabbing (onsubmit/form hooks)
- fake_fields (SSN/PIN/card/CVV/DOB additions)
- mfa_interception (OTP/2FA/token capture)
- balance_manipulation (innerHTML/balance rewriting)
## Webinject format
`set_url <pattern> <flags>` followed by `data_before … data_end`, `data_inject … data_end`,
`data_after … data_end`.
## Usage
```bash
python scripts/analyst.py parse webinjects.txt
```
## Sources
- MITRE ATT&CK T1185 Browser Session Hijacking — https://attack.mitre.org/techniques/T1185/
- Zeus/SpyEye webinject grammar (set_url/data_before/data_inject) from public malware research
- Web-fraud injection techniques (form-grab, fake fields, OTP capture) from banking-trojan analyses

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""Parse banking trojan webinject configs (Zeus/SpyEye-style) into structured entries.
Parses set_url / data_before / data_inject / data_after blocks, extracts the targeted URL pattern
and injected snippet, classifies the injection technique, and extracts/defangs exfil endpoints.
Parses config text only; executes nothing and does not render injected code.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
RE_SETURL = re.compile(r"set_url\s+(\S+)(?:\s+(\S+))?", re.I)
RE_BLOCK = re.compile(
r"data_(before|inject|after)\b(.*?)data_end", re.I | re.S)
RE_URL = re.compile(r"https?://[^\s\"'<>]+", re.I)
TECHNIQUE_HINTS = {
"form_grabbing": ["onsubmit", "form", "addeventlistener", "password"],
"fake_fields": ["ssn", "social security", "pin", "card number", "cvv", "mother",
"date of birth", "atm"],
"mfa_interception": ["otp", "one-time", "token", "2fa", "verification code", "sms code"],
"balance_manipulation": ["balance", "innerhtml", "replace"],
}
def parse(path: str) -> dict:
text = Path(path).read_text(encoding="utf-8-sig", errors="replace")
entries = []
# Split on set_url boundaries to associate each target with its following blocks.
chunks = re.split(r"(?=set_url\s)", text, flags=re.I)
for chunk in chunks:
m = RE_SETURL.search(chunk)
if not m:
continue
target = m.group(1)
inject = ""
for bname, body in RE_BLOCK.findall(chunk):
if bname.lower() == "inject":
inject = body.strip()
low = inject.lower()
techniques = [t for t, needles in TECHNIQUE_HINTS.items()
if any(n in low for n in needles)]
exfil = sorted({u.replace("http", "hxxp").replace(".", "[.]")
for u in RE_URL.findall(inject)})
entries.append({
"target_url": target,
"techniques": techniques,
"exfil_endpoints": exfil,
"inject_preview": inject[:160],
})
targets = sorted({e["target_url"] for e in entries})
return {
"webinject_count": len(entries),
"targeted_url_patterns": targets,
"entries": entries,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Parse banking trojan webinjects")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("parse")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(parse(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,89 @@
---
name: analyzing-compiled-python-malware
description: 'Analyzes Python-based malware packaged as PyInstaller/py2exe executables by detecting
the packer, locating the embedded archive, and identifying bundled .pyc modules for extraction and
decompilation. Activates for requests to analyze compiled Python malware, unpack a PyInstaller
binary, or extract pyc modules from a frozen Python executable.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- python
- pyinstaller
- pyc
- unpacking
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1059.006
- T1587.001
d3fend:
- D3-SDA
- D3-DA
references:
- 'PyInstaller archive format (CArchive/PKG) — https://pyinstaller.org/en/stable/advanced-topics.html'
- 'Python bytecode (dis module / .pyc magic) — https://docs.python.org/3/library/dis.html'
---
# Analyzing Compiled Python Malware
## When to Use
- You have an executable that is actually a frozen Python app (PyInstaller, py2exe, cx_Freeze) and
need to identify the packer and locate the embedded Python modules.
- You want to extract `.pyc` files for decompilation.
**Do not use** this to run the executable — it identifies and locates the embedded archive
statically. Decompile extracted `.pyc` in an isolated environment.
## Prerequisites
- The frozen executable (read inertly).
## Safety & Handling
- Read bytes statically; treat extracted modules as malicious until reviewed.
## Workflow
### Step 1: Detect the packer
```bash
python scripts/analyst.py detect sample.exe
```
Looks for PyInstaller markers (`pyi-`, `PYZ-00.pyz`, the `MEI` CArchive cookie `MEI\014\013\012\013\016`),
py2exe (`PYTHONSCRIPT`, `zipfile.zip`), and embedded `python3x.dll` references.
### Step 2: Locate the embedded archive
Find the CArchive cookie near the end of the file and report the offset and the embedded Python
version string (`python3.x`).
### Step 3: Extract and decompile
Use a PyInstaller extractor to dump the archive, then decompile `.pyc` (matching the detected
Python version) for source recovery.
### Step 4: Analyze the source
Review the recovered Python for C2, persistence, and capability; map to ATT&CK.
## Validation
- The packer is identified by its specific marker, not just the presence of Python strings.
- The CArchive cookie offset and Python version are reported when PyInstaller is present.
- Findings distinguish the bootloader stub from the embedded Python payload.
## Pitfalls
- `.pyc` version mismatch breaking decompilation — match the interpreter version.
- Stripped/obfuscated bytecode (e.g., custom magic) needing header repair before decompiling.
- Encrypted PYZ archives (PyInstaller `--key`) requiring the key.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the detector.
- PyInstaller archive and Python bytecode references (linked in frontmatter).

View File

@@ -0,0 +1,37 @@
# API Reference: Analyzing Compiled Python Malware
## Overview
`scripts/analyst.py` detects frozen-Python packers (PyInstaller, py2exe, cx_Freeze) by their markers,
locates the PyInstaller CArchive magic cookie, and extracts embedded Python version strings. It
reads bytes statically and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `detect(path)`
Returns the detected packer, PyInstaller cookie offset, and Python version strings, plus the
recommended next step.
## Markers
- PyInstaller: `_MEIPASS`, `PYZ-00.pyz`, `pyiboot01_bootstrap`, CArchive cookie `MEI\\014\\013\\012\\013\\016`.
- py2exe: `PYTHONSCRIPT`.
- cx_Freeze: `cx_Freeze` strings.
## Usage
```bash
python scripts/analyst.py detect sample.exe
```
## Sources
- PyInstaller archive format (CArchive/PKG, MEI cookie) — https://pyinstaller.org/en/stable/advanced-topics.html
- Python bytecode / .pyc magic (dis module) — https://docs.python.org/3/library/dis.html
- pyinstxtractor extraction workflow (public PyInstaller-unpacking tooling)

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""Detect frozen-Python packers and locate the embedded archive.
Scans an executable for PyInstaller, py2exe, and cx_Freeze markers, locates the PyInstaller
CArchive magic cookie, and extracts embedded Python version strings. Reads bytes statically;
executes nothing.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
PYI_COOKIE = b"MEI\014\013\012\013\016"
PYI_MARKERS = [b"pyi-windows-manifest-filename", b"PYZ-00.pyz", b"pyiboot01_bootstrap",
b"_MEIPASS"]
PY2EXE_MARKERS = [b"PYTHONSCRIPT", b"py2exe"]
CXFREEZE_MARKERS = [b"cx_Freeze", b"cx_freeze__init__"]
RE_PYVER = re.compile(rb"python(\d)\.?(\d{1,2})")
RE_PYDLL = re.compile(rb"python(\d)(\d{1,2})\.dll", re.I)
def detect(path: str) -> dict:
data = Path(path).read_bytes()
pyi = any(m in data for m in PYI_MARKERS) or PYI_COOKIE in data
py2exe = any(m in data for m in PY2EXE_MARKERS)
cxfreeze = any(m in data for m in CXFREEZE_MARKERS)
cookie_off = data.rfind(PYI_COOKIE)
versions = sorted({f"{m.group(1).decode()}.{m.group(2).decode()}"
for m in RE_PYVER.finditer(data)})
dll_versions = sorted({f"{m.group(1).decode()}.{m.group(2).decode()}"
for m in RE_PYDLL.finditer(data)})
packer = "PyInstaller" if pyi else "py2exe" if py2exe else \
"cx_Freeze" if cxfreeze else "unknown"
return {
"is_frozen_python": pyi or py2exe or cxfreeze,
"packer": packer,
"pyinstaller_cookie_offset": cookie_off if cookie_off != -1 else None,
"python_versions": versions[:5],
"python_dll_versions": dll_versions[:5],
"next_step": "extract the archive (e.g. pyinstxtractor) then decompile matching .pyc",
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Detect frozen-Python packers")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("detect")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(detect(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,88 @@
---
name: analyzing-cryptominer-malware
description: 'Analyzes cryptojacking/coinminer malware by extracting mining pool endpoints
(stratum), wallet addresses, algorithm and miner identifiers (XMRig and forks), and persistence
or resource-control settings from static strings and config. Activates for requests to analyze a
cryptominer, extract mining pool and wallet IOCs, or identify coinminer configuration.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- cryptominer
- cryptojacking
- xmrig
- resource-hijacking
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1496
- T1543
- T1027
d3fend:
- D3-PSA
- D3-DA
references:
- 'MITRE ATT&CK T1496 Resource Hijacking — https://attack.mitre.org/techniques/T1496/'
- 'XMRig configuration documentation — https://xmrig.com/docs/miner/config'
---
# Analyzing Cryptominer Malware
## When to Use
- You have a suspected coinminer (often an XMRig fork) and need to extract its pool, wallet,
algorithm, and resource-control configuration for IOCs and attribution.
- You are confirming a host's high CPU/GPU usage is malicious mining.
**Do not use** this to run the miner — extract its configuration statically.
## Prerequisites
- The miner sample or its extracted config/strings, read inertly.
## Safety & Handling
- Read bytes statically; defang pool endpoints; never start the miner.
## Workflow
### Step 1: Extract mining configuration
```bash
python scripts/analyst.py profile sample.bin
```
Extracts `stratum+tcp://`/`stratum+ssl://` pool URLs, wallet addresses (Monero and common
formats), algorithm names (`rx/0`, `cn/r`, etc.), and the XMRig/miner identifier strings.
### Step 2: Identify resource and stealth controls
Surface `max-cpu-usage`, `--background`, idle-detection, and `donate-level` settings, plus
watchdog/persistence indicators.
### Step 3: Build IOCs
Record defanged pool endpoints, wallet addresses, and the miner family for detection and takedown
referral.
### Step 4: Document
Summarize the campaign's pool/wallet and the resource-control posture.
## Validation
- Pool URLs use a recognized `stratum` scheme and resolve to plausible hosts.
- Wallet addresses match a known coin's format/length.
- The miner family is identified from its config keys/strings.
## Pitfalls
- Legitimate mining software shares the same config keys — context (deployment, stealth) matters.
- Encrypted/packed config requiring decryption first.
- Proxy pools masking the true upstream pool.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the profiler.
- ATT&CK T1496 and the XMRig config reference (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Analyzing Cryptominer Malware
## Overview
`scripts/analyst.py` extracts a coinminer's configuration from static strings: stratum pool
endpoints, wallet addresses (Monero/Bitcoin/Ethereum formats), algorithm identifiers, miner
family strings, and resource-control/stealth keys. It reads bytes statically, defangs pool
endpoints, and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `profile(path)`
Returns pool endpoints, wallet addresses (by coin), algorithms, miner family, resource controls,
and a cryptominer likelihood flag.
## Extracted indicators
- **Pools**: `stratum+tcp://` / `stratum+ssl://`.
- **Wallets**: Monero (`4…`, 95 chars), Bitcoin (legacy + bech32), Ethereum (`0x…40 hex`).
- **Algorithms**: `rx/0`, `cn/r`, `randomx`, `kawpow`, `ethash`, etc.
- **Resource controls**: `max-cpu-usage`, `donate-level`, `background`, `idle`.
## Usage
```bash
python scripts/analyst.py profile sample.bin
```
## Sources
- MITRE ATT&CK T1496 Resource Hijacking — https://attack.mitre.org/techniques/T1496/
- XMRig configuration keys — https://xmrig.com/docs/miner/config
- Coin address formats from each coin's public address specification

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Profile a cryptominer's configuration from static strings.
Extracts stratum pool endpoints, wallet addresses, algorithm identifiers, miner family strings,
and resource-control/stealth settings. Reads bytes statically, defangs pool endpoints, and
executes nothing.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ASCII_RE = re.compile(rb"[\x20-\x7e]{4,}")
RE_STRATUM = re.compile(r"stratum\+(?:tcp|ssl)://[^\s\"'<>]+", re.I)
RE_MONERO = re.compile(r"\b4[0-9AB][0-9A-Za-z]{93}\b")
RE_BTC = re.compile(r"\b(?:bc1[0-9a-z]{20,60}|[13][a-km-zA-HJ-NP-Z1-9]{25,34})\b")
RE_ETH = re.compile(r"\b0x[a-fA-F0-9]{40}\b")
ALGOS = ["rx/0", "rx/wow", "cn/r", "cn/2", "cn-heavy", "argon2", "kawpow", "ethash", "randomx",
"cryptonight"]
MINER_IDS = ["xmrig", "xmr-stak", "minerd", "cpuminer", "nbminer", "phoenixminer", "t-rex",
"lolminer", "nanominer"]
RESOURCE_KEYS = ["max-cpu-usage", "cpu-priority", "donate-level", "background", "max-threads-hint",
"huge-pages", "idle", "watchdog"]
def _strings(data: bytes) -> str:
return "\n".join(m.decode("latin-1", "ignore") for m in ASCII_RE.findall(data))
def _defang(s: str) -> str:
return s.replace("stratum", "stratum-x").replace("://", "[://]").replace(".", "[.]")
def profile(path: str) -> dict:
blob = _strings(Path(path).read_bytes())
low = blob.lower()
pools = sorted({_defang(p) for p in RE_STRATUM.findall(blob)})
wallets = {
"monero": sorted(set(RE_MONERO.findall(blob)))[:10],
"bitcoin": sorted(set(RE_BTC.findall(blob)))[:10],
"ethereum": sorted(set(RE_ETH.findall(blob)))[:10],
}
wallets = {k: v for k, v in wallets.items() if v}
algos = sorted({a for a in ALGOS if a in low})
miners = sorted({m for m in MINER_IDS if m in low})
resource = sorted({r for r in RESOURCE_KEYS if r in low})
return {
"pool_endpoints": pools,
"wallet_addresses": wallets,
"algorithms": algos,
"miner_family": miners,
"resource_controls": resource,
"is_likely_cryptominer": bool(pools or (miners and algos)),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Profile a cryptominer")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("profile")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(profile(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,96 @@
---
name: analyzing-dotnet-malware-internals
description: 'Reverses .NET/managed malware: decompiling MSIL back to C#, defeating common
.NET protectors and string encryptors, and tracing reflection-based loaders to recover the
real payload. Activates for requests to analyze a .NET sample, decompile MSIL, or unpack a
managed loader.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- dotnet
- msil
- decompilation
- deobfuscation
- managed-code
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1620
- T1055
d3fend:
- D3-FCR
- D3-DA
references:
- 'ECMA-335 Common Language Infrastructure (CIL/MSIL) — https://ecma-international.org/publications-and-standards/standards/ecma-335/'
- 'ILSpy decompiler — https://github.com/icsharpcode/ILSpy'
---
# Analyzing .NET Malware Internals
## When to Use
- A sample is a managed (.NET) assembly — confirmed by a CLR header / `mscoree` import or
`BSJB` metadata signature.
- You need readable C# from MSIL and want to defeat .NET-specific obfuscation.
- A loader uses reflection (`Assembly.Load`) to run an in-memory payload you must recover.
**Do not use** native disassembly workflows (Ghidra for x86) as the primary tool — managed
code decompiles far more cleanly with a .NET decompiler.
## Prerequisites
- ILSpy / dnSpyEx for decompilation and (with dnSpyEx) managed debugging.
- de4dot or equivalent for known protectors; familiarity with common .NET obfuscators.
## Workflow
### Step 1: Confirm it is managed
Check for the CLR runtime header and the `BSJB` metadata magic:
```bash
python scripts/analyst.py identify sample.exe
```
### Step 2: Decompile
Open in ILSpy/dnSpyEx and review the entry point, `Main`, and module initializer
(`<Module>.cctor`), which protectors often abuse.
### Step 3: Handle obfuscation
Recognize and undo common schemes:
- **String encryption** — a decryptor method called everywhere; run/trace it to recover
plaintext (de4dot can often static-decrypt).
- **Control-flow flattening** — follow the dispatcher state machine.
- **Proxy methods / renaming** — rely on decompiler analysis rather than names.
### Step 4: Trace reflection loaders
Find `Assembly.Load(byte[])` / `Activator.CreateInstance`; dump the byte array argument at
runtime (managed debugger breakpoint) to recover the real second-stage assembly, then recurse.
### Step 5: Analyze the payload
Decompile the recovered stage; extract C2, configuration, and capabilities for the report.
## Validation
- Decompiled C# is coherent (named or recovered) and the entry path is traced.
- Encrypted strings are recovered to plaintext.
- The reflection-loaded stage is dumped and itself decompiles.
## Pitfalls
- Treating the loader as the payload — managed malware is frequently multi-stage.
- Ignoring the module initializer where protectors install hooks.
- Static-decrypting strings when the scheme is runtime-keyed; debug and dump instead.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the CLR identifier.
- ECMA-335 and ILSpy (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Reverse Engineering .NET Malware
## Overview
`scripts/analyst.py` parses a PE to detect the CLR runtime header (COM descriptor data directory
index 14) and the `BSJB` metadata-root signature, confirming whether a sample is managed. It
parses bytes only and never executes the sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | struct parsing, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| ILSpy / dnSpyEx | Decompile MSIL to C#; managed debugging |
| de4dot | Static deobfuscation of known protectors |
## Core functions
### `identify(path)`
Returns whether the sample is managed, whether the `BSJB` metadata signature is present, and
diagnostic notes (mscoree/_CorExeMain references).
## Usage
```bash
python scripts/analyst.py identify sample.exe
```
## Sources
- ECMA-335 Common Language Infrastructure (CIL/MSIL metadata, BSJB root) —
https://ecma-international.org/publications-and-standards/standards/ecma-335/
- PE COM descriptor data directory (index 14) — PE/COFF specification
- ILSpy decompiler — https://github.com/icsharpcode/ILSpy

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Identify a .NET/managed assembly and surface basic CLR metadata signals.
Parses the PE far enough to detect the COM descriptor (CLR) data directory and searches for
the ``BSJB`` metadata-root signature. Static parsing only; the sample is never executed.
"""
from __future__ import annotations
import argparse
import json
import struct
import sys
from pathlib import Path
def identify(path: str) -> dict:
data = Path(path).read_bytes()
result = {"managed": False, "bsjb_metadata": False, "notes": []}
if data[:2] != b"MZ":
result["notes"].append("not a PE (no MZ)")
return result
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
result["notes"].append("no PE signature")
return result
coff = e_lfanew + 4
opt = coff + 20
magic = struct.unpack_from("<H", data, opt)[0]
pe32_plus = magic == 0x20B
# data directories start after the standard+windows optional header fields
dd_offset = opt + (112 if pe32_plus else 96)
# COM descriptor is data directory index 14
com_dir = dd_offset + 14 * 8
rva, size = struct.unpack_from("<II", data, com_dir)
if rva and size:
result["managed"] = True
result["notes"].append("CLR runtime header (COM descriptor) present")
if b"BSJB" in data:
result["bsjb_metadata"] = True
result["notes"].append("BSJB metadata root signature found")
if b"mscoree.dll" in data or b"_CorExeMain" in data:
result["notes"].append("mscoree / _CorExeMain reference")
result["managed"] = result["managed"] or result["bsjb_metadata"]
return result
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Identify managed .NET assembly")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("identify")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(identify(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,99 @@
---
name: analyzing-elf-binaries-on-linux
description: 'Statically analyzes Linux ELF malware: ELF header and sections, dynamic symbols
and imports, segment permissions, embedded strings, and packing indicators to infer
capability without execution. Activates for requests to analyze an ELF binary, Linux
malware, or shared object.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware
- static-analysis
- elf
- linux
- symbols
- entropy
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1059.004
- T1129
d3fend:
- D3-FA
- D3-FCR
references:
- 'ELF specification (System V ABI) — https://refspecs.linuxfoundation.org/elf/elf.pdf'
- 'pyelftools — https://github.com/eliben/pyelftools'
---
# Analyzing ELF Binaries on Linux
## When to Use
- You have a Linux executable or shared object and need to assess it statically.
- You want to read the ELF header, segments, dynamic symbols, and strings to infer behavior.
- You are checking for static linking, stripped symbols, or packing.
**Do not use** symbol absence as proof of nothing — stripped or statically linked Go/Rust
binaries hide structure; switch to disassembly when static metadata is thin.
## Prerequisites
- `readelf`/`nm`/`strings` (binutils) or `pyelftools` (`pip install pyelftools`).
- The sample in neutralized form inside the lab.
## Safety & Handling
- Static only: parse the file; do not set it executable or run it.
## Workflow
### Step 1: Read the ELF header
```bash
python scripts/analyst.py analyze sample.elf
```
Note class (ELF32/64), endianness, type (EXEC/DYN/REL), machine (x86-64, ARM, MIPS), and
whether it is stripped.
### Step 2: Examine segments and section permissions
Writable+executable segments, or a single large segment, suggest packing or self-modifying
code. Compare `PT_LOAD` permissions against expectations.
### Step 3: Inspect dynamic symbols and needed libraries
Imported functions hint at capability: `socket`/`connect` (network), `ptrace` (anti-debug or
injection), `fork`/`execve` (process control), `crypt`/`EVP_*` (encryption).
### Step 4: Detect static linking and packing
Static binaries lack a dynamic symbol table and `NEEDED` entries. High whole-file entropy and
a tiny section table suggest a packer (e.g. UPX leaves `UPX!` markers).
### Step 5: Skim strings and constructors
Check `.init_array`/constructors (code before `main`), and strings for paths, URLs, and shell
commands.
## Validation
- Header `type` and segment permissions are consistent with the inferred behavior.
- Imported symbols correspond to plausible capabilities.
- Packing call (entropy + missing sections) matches the strings observed.
## Pitfalls
- Assuming dynamic symbols are complete — they only cover imported/exported names, not
internal functions.
- Treating a stripped Go binary as "empty"; its structure lives in runtime metadata, not the
symbol table.
- Ignoring constructors/`.init_array`, which run before `main`.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the ELF analyzer.
- ELF/System V ABI spec and pyelftools (linked in frontmatter).

View File

@@ -0,0 +1,43 @@
# API Reference: Analyzing ELF Binaries on Linux
## Overview
`scripts/analyst.py` reports ELF header facts, dynamic symbols and `NEEDED` libraries, a strip
check, and capability groupings (network/process/crypto). It uses `pyelftools` when present
and falls back to a minimal header/entropy parse otherwise.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| pyelftools | >= 0.30 | Full ELF parsing (optional) |
| (stdlib) | — | Header/entropy fallback |
## External tools
| Tool | Purpose |
|------|---------|
| readelf / nm (binutils) | Authoritative ELF inspection |
| strings | Embedded string review |
## Core functions
### `analyze(path)`
Dispatches to the pyelftools or minimal parser; returns header, symbols, needed libs, and
capability groupings.
### `entropy(data)`
Byte-level Shannon entropy for packing assessment.
## Usage
```bash
pip install pyelftools # optional
python scripts/analyst.py analyze sample.elf
```
## Sources
- ELF / System V ABI specification — https://refspecs.linuxfoundation.org/elf/elf.pdf
- pyelftools — https://github.com/eliben/pyelftools
- UPX section/marker conventions (vendor docs)

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Static ELF analysis: header, sections, dynamic symbols, and packing hints.
Uses ``pyelftools`` when available; otherwise parses the ELF header and section table
directly so basic facts (class, type, machine, sections, entropy) are still reported.
Never executes the sample.
"""
from __future__ import annotations
import argparse
import json
import math
import struct
import sys
from collections import Counter
from pathlib import Path
_NETWORK = {"socket", "connect", "send", "recv", "bind", "listen", "gethostbyname"}
_PROC = {"fork", "execve", "ptrace", "clone", "kill", "system", "popen"}
_CRYPTO = {"crypt", "evp_encryptinit_ex", "aes_encrypt", "rc4"}
def entropy(data: bytes) -> float:
if not data:
return 0.0
counts = Counter(data)
n = len(data)
return -sum((c / n) * math.log2(c / n) for c in counts.values())
def analyze_with_pyelftools(path: str) -> dict:
from elftools.elf.elffile import ELFFile # type: ignore
from elftools.elf.dynamic import DynamicSection # type: ignore
with open(path, "rb") as fh:
elf = ELFFile(fh)
header = {
"class": elf.elfclass,
"type": elf["e_type"],
"machine": elf["e_machine"],
"entry": hex(elf["e_entry"]),
}
symbols: list[str] = []
needed: list[str] = []
dynsym = elf.get_section_by_name(".dynsym")
if dynsym:
symbols = [s.name for s in dynsym.iter_symbols() if s.name]
for sec in elf.iter_sections():
if isinstance(sec, DynamicSection):
for tag in sec.iter_tags():
if tag.entry.d_tag == "DT_NEEDED":
needed.append(tag.needed)
stripped = elf.get_section_by_name(".symtab") is None
low = {s.lower() for s in symbols}
return {
"header": header,
"stripped": stripped,
"needed": needed,
"dynamic_symbol_count": len(symbols),
"capabilities": {
"network": sorted(low & _NETWORK),
"process": sorted(low & _PROC),
"crypto": sorted(low & _CRYPTO),
},
}
def analyze_minimal(path: str) -> dict:
data = Path(path).read_bytes()
if data[:4] != b"\x7fELF":
raise ValueError("not an ELF")
is64 = data[4] == 2
le = data[5] == 1
endian = "<" if le else ">"
e_type = struct.unpack_from(endian + "H", data, 16)[0]
e_machine = struct.unpack_from(endian + "H", data, 18)[0]
upx = b"UPX!" in data
return {
"header": {"class": 64 if is64 else 32, "type": e_type, "machine": e_machine},
"whole_file_entropy": round(entropy(data), 3),
"upx_marker": upx,
"note": "pyelftools not installed; minimal parse",
}
def analyze(path: str) -> dict:
try:
return analyze_with_pyelftools(path)
except ImportError:
return analyze_minimal(path)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Static ELF analysis")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("analyze")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,89 @@
---
name: analyzing-excel-4-macro-malware
description: 'Analyzes legacy Excel 4.0 (XLM) macro malware by parsing extracted macro-sheet
formulas for auto-executing names, obfuscation (FORMULA.FILL, CHAR concatenation), and download
or execution primitives (EXEC, CALL, REGISTER). Activates for requests to analyze XLM macros,
examine Excel 4.0 macro sheets, or deobfuscate legacy spreadsheet macros.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- xlm
- excel4
- macros
- document-malware
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1059.005
- T1204.002
- T1027
d3fend:
- D3-FCR
- D3-DA
references:
- 'MITRE ATT&CK T1059.005 Visual Basic / macros — https://attack.mitre.org/techniques/T1059/005/'
- 'Microsoft Excel 4.0 macro functions reference — https://learn.microsoft.com/office/'
---
# Analyzing Excel 4.0 Macro Malware
## When to Use
- You have an extracted Excel 4.0 (XLM) macro sheet (e.g., from a `.xls`/`.xlsm` dumped with
oletools/XLMMacroDeobfuscator) and need to find auto-run cells and execution primitives.
- You are triaging maldocs that hide logic in legacy macro sheets rather than VBA.
**Do not use** Excel to open the document to "see" the macro — opening triggers the auto-run
cells. Work from the extracted formula text statically.
## Prerequisites
- The extracted XLM formula text (cell address → formula). Optional: `oletools` to extract it.
## Safety & Handling
- Never open the workbook in Excel; analyze the extracted formulas inertly. Defang URLs.
## Workflow
### Step 1: Find auto-executing entry points
```bash
python scripts/analyst.py analyze macros.txt
```
Flags defined-name triggers (`Auto_Open`, `Auto_Close`) and the cells they point to, plus
`=HALT()`/`=RETURN()` flow markers.
### Step 2: Identify execution and download primitives
Detects `EXEC(`, `CALL(`, `REGISTER(` (Win32 imports), and URLDownload-style `CALL` patterns that
fetch and run a payload.
### Step 3: Unwind obfuscation
Surfaces `CHAR()`/`&` string-building, `FORMULA.FILL`/`FORMULA` self-writing, and base/`MID`
slicing used to hide strings; reconstructs concatenated literals where possible.
### Step 4: Extract IOCs
Pull URLs/paths and defang them; record the execution method (regsvr32, rundll32, mshta).
## Validation
- Auto-run entry cells are identified, not just the presence of macros.
- Execution primitives (EXEC/CALL/REGISTER) are reported with their arguments.
- Reconstructed strings and URLs are defanged.
## Pitfalls
- Heavily obfuscated sheets that self-write cells at runtime — static reconstruction is partial.
- Macro sheets hidden as `Very Hidden` that simple viewers miss.
- Confusing benign legacy spreadsheets that legitimately use XLM.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the analyzer.
- ATT&CK T1059.005 and the Excel 4.0 macro reference (linked in frontmatter).

View File

@@ -0,0 +1,41 @@
# API Reference: Analyzing Excel 4.0 Macro Malware
## Overview
`scripts/analyst.py` parses an extracted XLM macro-sheet formula dump and flags auto-run triggers,
execution/download primitives, executors, and obfuscation, reconstructing `CHAR()` string chains
and defanging URLs. It parses text only, never opens the workbook, and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
| oletools | optional | extract the formulas upstream (not required by this script) |
## Core functions
### `analyze(path)`
Returns auto-run triggers, execution primitives, download indicators, executors, obfuscation
techniques, defanged URLs, reconstructed `CHAR()` strings, and a maliciousness flag.
### `_reconstruct_char(line)`
Rebuilds a string from a `CHAR(n)&CHAR(n)…` concatenation chain.
## Indicators
- Auto-run: `Auto_Open`, `Auto_Close`, `Auto_Activate`.
- Execution: `EXEC(`, `CALL(`, `REGISTER(` (Win32 import declaration).
- Download: `URLDownloadToFile`, `MSXML2`, `WinHTTP`.
## Usage
```bash
python scripts/analyst.py analyze macros.txt
```
## Sources
- MITRE ATT&CK T1059.005 — https://attack.mitre.org/techniques/T1059/005/
- Excel 4.0 macro function semantics (EXEC/CALL/REGISTER/FORMULA.FILL) from Microsoft Office docs
- XLM obfuscation patterns from public maldoc analysis (XLMMacroDeobfuscator methodology)

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Analyze extracted Excel 4.0 (XLM) macro formulas for malicious behavior.
Reads a text dump of macro-sheet formulas and flags auto-run entry points, execution/download
primitives (EXEC/CALL/REGISTER), and obfuscation (CHAR concatenation, FORMULA.FILL self-writing).
Parses text only; executes nothing and never opens the workbook. Defangs recovered URLs.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
AUTO_NAMES = ["auto_open", "auto_close", "auto_activate", "auto_deactivate"]
EXEC_PRIMS = ["exec(", "call(", "register(", "fopen(", "fwrite("]
DOWNLOAD_HINTS = ["urlmon", "urldownloadtofile", "http", "msxml2", "winhttp", "shellexecute"]
EXECUTORS = ["regsvr32", "rundll32", "mshta", "powershell", "cmd.exe", "wscript", "cscript"]
OBFUSCATION = ["char(", "formula.fill", "formula(", "mid(", "&", "concatenate("]
RE_URL = re.compile(r"https?://[^\s\"'<>)]+", re.I)
RE_CHAR = re.compile(r"CHAR\((\d{1,3})\)", re.I)
def _defang(s: str) -> str:
return s.replace("http", "hxxp").replace(".", "[.]")
def _reconstruct_char(line: str) -> str:
chars = RE_CHAR.findall(line)
if len(chars) < 3:
return ""
try:
return "".join(chr(int(c)) for c in chars if 0 <= int(c) < 256)
except ValueError:
return ""
def analyze(path: str) -> dict:
text = Path(path).read_text(encoding="utf-8-sig", errors="replace")
low = text.lower()
auto = sorted({n for n in AUTO_NAMES if n in low})
exec_prims = sorted({p.rstrip("(") for p in EXEC_PRIMS if p in low})
downloads = sorted({d for d in DOWNLOAD_HINTS if d in low})
executors = sorted({e for e in EXECUTORS if e in low})
obf = sorted({o.rstrip("(") for o in OBFUSCATION if o in low})
urls = sorted({_defang(u) for u in RE_URL.findall(text)})
reconstructed = sorted({r for line in text.splitlines()
if (r := _reconstruct_char(line))})[:25]
return {
"auto_run_triggers": auto,
"execution_primitives": exec_prims,
"download_indicators": downloads,
"executors": executors,
"obfuscation_techniques": obf,
"urls": urls[:50],
"reconstructed_char_strings": reconstructed,
"is_likely_malicious_xlm": bool(auto and (exec_prims or downloads)),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze Excel 4.0 (XLM) macros")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("analyze")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,88 @@
---
name: analyzing-golang-malware-internals
description: 'Analyzes Go-compiled malware by recovering function names from the pclntab, detecting
the Go build/version string, and listing embedded package paths to overcome stripped symbols.
Activates for requests to analyze Go malware, recover Go function names, or identify a Golang
binary build.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- golang
- symbol-recovery
- pclntab
- stripped-binaries
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1587.001
- T1059
d3fend:
- D3-SDA
- D3-DA
references:
- 'Go runtime pclntab format (Go source: runtime/symtab.go) — https://go.dev/src/runtime/symtab.go'
- 'Go binary build info (debug/buildinfo) — https://pkg.go.dev/debug/buildinfo'
---
# Analyzing Golang Malware Internals
## When to Use
- You have a Go-compiled binary (often large, statically linked) and standard symbol recovery
fails because user symbols are stripped.
- You need function names from the pclntab, the Go version, and embedded package/module paths.
**Do not use** this for non-Go binaries — confirm the Go build signature first. This skill reads
the binary statically and executes nothing.
## Prerequisites
- The Go binary (read inertly).
## Safety & Handling
- Read bytes statically; treat strings as untrusted data.
## Workflow
### Step 1: Confirm it is Go and get the version
```bash
python scripts/analyst.py info sample.bin
```
Looks for the `Go build ID`, the `go1.x` version string, and `runtime.` references.
### Step 2: Recover function names from pclntab
The pclntab (preceded by a magic like `\xfb\xff\xff\xff`/`\xf0\xff\xff\xff` across Go versions)
contains function name strings; extract `main.*`, package-qualified, and `runtime.*` names.
### Step 3: Enumerate package paths
List embedded import/module paths (`github.com/...`, vendored deps) to fingerprint capabilities
and third-party libraries.
### Step 4: Map and document
Map suspicious packages (networking, crypto, exec) to behavior and ATT&CK.
## Validation
- The Go build signature/version is confirmed before deeper parsing.
- Recovered names include plausible `main.*`/package-qualified functions.
- Package paths are real module paths, not random strings.
## Pitfalls
- pclntab magic differs across Go versions; try the known set.
- Obfuscators (garble) rename symbols and strip build info — names may be hashed.
- Confusing vendored library code with the author's `main` package.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the analyzer.
- Go pclntab/buildinfo references (linked in frontmatter).

View File

@@ -0,0 +1,38 @@
# API Reference: Analyzing Golang Malware Internals
## Overview
`scripts/analyst.py` confirms a Go build (build ID / `go1.x` / `runtime.main`), detects pclntab
magic markers, and recovers Go function names and package import paths from printable strings,
mapping notable packages to capabilities. It reads bytes statically and executes nothing. This is
a heuristic scan, not a full pclntab structure parser.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `info(path)`
Returns Go detection, versions, pclntab magics present, recovered functions, package paths, and
capabilities.
## Heuristics
- Go detection: `Go build ID`, `go1.x`, `runtime.main`.
- pclntab magics: `\xfb\xff\xff\xff`, `\xfa…`, `\xf0…`, `\xf1…` (across Go versions).
- Functions: `main.*`, `runtime.*`, `net.*`, `crypto.*`, `os/exec.*`, `syscall.*`.
## Usage
```bash
python scripts/analyst.py info sample.bin
```
## Sources
- Go runtime pclntab format (runtime/symtab.go) — https://go.dev/src/runtime/symtab.go
- Go build info (debug/buildinfo) — https://pkg.go.dev/debug/buildinfo
- Go RE techniques (pclntab name recovery, garble obfuscation) from public Go-malware research

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Analyze a Go-compiled binary: confirm Go build, recover names, list package paths.
Detects the Go build signature/version, scans printable strings for Go function/package names
(main.*, runtime.*, github.com/... import paths), and reports likely capabilities. Reads bytes
statically; executes nothing. This is a heuristic string/pclntab-marker scan, not a full pclntab
parser.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ASCII_RE = re.compile(rb"[\x20-\x7e]{3,}")
RE_GOVER = re.compile(rb"go1\.\d+(?:\.\d+)?")
PCLNTAB_MAGICS = [b"\xfb\xff\xff\xff", b"\xfa\xff\xff\xff", b"\xf0\xff\xff\xff",
b"\xf1\xff\xff\xff"]
RE_FUNC = re.compile(r"^(?:main|runtime|net|crypto|os/exec|syscall)[./][\w./*()]+")
RE_PKG = re.compile(r"(?:github\.com|gitlab\.com|golang\.org|gopkg\.in)/[\w./-]+")
CAP_PKGS = {
"net/http": "HTTP networking", "net": "raw networking", "crypto/aes": "AES crypto",
"os/exec": "process execution", "syscall": "direct syscalls",
"golang.org/x/sys": "low-level OS access",
}
def info(path: str) -> dict:
data = Path(path).read_bytes()
strings = [m.decode("latin-1", "ignore") for m in ASCII_RE.findall(data)]
blob = "\n".join(strings)
is_go = b"Go build ID" in data or bool(RE_GOVER.search(data)) or b"runtime.main" in data
versions = sorted({m.decode() for m in RE_GOVER.findall(data)})
funcs = sorted({s for s in strings if RE_FUNC.match(s)})[:200]
pkgs = sorted(set(RE_PKG.findall(blob)))[:100]
caps = sorted({desc for pkg, desc in CAP_PKGS.items() if pkg in blob})
pcln = [m.hex() for m in PCLNTAB_MAGICS if m in data]
return {
"is_go_binary": is_go,
"go_versions": versions,
"pclntab_magics_present": pcln,
"recovered_functions": funcs,
"package_paths": pkgs,
"capabilities": caps,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze a Go binary")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("info")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(info(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,88 @@
---
name: analyzing-infostealer-credential-theft
description: 'Analyzes infostealer samples by mapping the browser, credential store, wallet, and
application paths they target, and the exfiltration channel used, from static strings and
behavior. Activates for requests to analyze an infostealer, identify targeted credential stores,
or map stealer collection and exfiltration behavior.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- infostealer
- credential-theft
- exfiltration
- triage
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1555.003
- T1539
- T1005
d3fend:
- D3-FAPA
- D3-DA
references:
- 'MITRE ATT&CK T1555.003 Credentials from Web Browsers — https://attack.mitre.org/techniques/T1555/003/'
- 'MITRE ATT&CK T1539 Steal Web Session Cookie — https://attack.mitre.org/techniques/T1539/'
---
# Analyzing Infostealer Credential Theft
## When to Use
- You have an infostealer sample and need to enumerate what it targets: browser credential/cookie
stores, crypto wallets, FTP/VPN/messaging app configs, and the exfil channel.
- You are building detections from the file paths and endpoints a stealer references.
**Do not use** this to recover credentials yourself — it characterizes what the malware targets
from inert static analysis.
## Prerequisites
- The sample (read inertly), optionally with extracted strings.
## Safety & Handling
- Read bytes statically; defang exfil endpoints; never run the stealer.
## Workflow
### Step 1: Map targeted artifacts
```bash
python scripts/analyst.py profile sample.bin
```
Matches references to known browser paths (`Login Data`, `Cookies`, `Web Data`), wallet
directories, app config paths, and credential APIs, grouped by category.
### Step 2: Identify exfiltration channel
Detects HTTP(S) POST endpoints, Telegram bot tokens, Discord webhooks, and FTP/SMTP usage in
strings.
### Step 3: Build the target/exfil profile
Summarize targeted stores and the exfil channel, mapping to ATT&CK.
### Step 4: Defang and report
Defang endpoints and produce IOCs for detection.
## Validation
- Targeted artifacts are grouped (browsers, wallets, apps, system credential stores).
- The exfil channel is identified with a defanged endpoint where present.
- Findings map to ATT&CK credential-access/collection/exfiltration techniques.
## Pitfalls
- Generic browser paths can appear in benign tools — corroborate with theft behavior.
- Missing wallet/app targets that use obfuscated path strings.
- Reporting live exfil endpoints (webhooks, bot tokens) without defanging.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the profiler.
- ATT&CK T1555.003 and T1539 (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Analyzing Infostealer Credential Theft
## Overview
`scripts/analyst.py` extracts strings from a sample and maps them to targeted artifacts (browser
credential stores, crypto wallets, application configs, credential APIs) and exfiltration channels
(Telegram, Discord webhooks, HTTP POST, FTP, SMTP). It reads bytes statically, defangs endpoints,
and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `profile(path)`
Returns targeted artifacts (grouped), exfiltration channels (defanged), and an infostealer
likelihood flag (≥2 target categories).
## Target categories
- **browser_credentials**: Chromium `Login Data`/`Cookies`/`Local State`, Firefox `key4.db`/`logins.json`.
- **crypto_wallets**: `wallet.dat`, Electrum/Exodus/MetaMask/Monero/Ledger.
- **applications**: FileZilla, OpenVPN, Telegram `tdata`, Discord, WinSCP, AnyDesk.
- **credential_api**: `CryptUnprotectData`, `CredEnumerate`, vault/LSA APIs.
## Usage
```bash
python scripts/analyst.py profile sample.bin
```
## Sources
- MITRE ATT&CK T1555.003 Credentials from Web Browsers — https://attack.mitre.org/techniques/T1555/003/
- MITRE ATT&CK T1539 Steal Web Session Cookie — https://attack.mitre.org/techniques/T1539/
- Browser/wallet artifact paths from public stealer family analyses

View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Profile an infostealer's targeted artifacts and exfiltration channel from static strings.
Extracts ASCII/UTF-16LE strings and matches them against curated lists of browser credential
stores, crypto wallets, application configs, and credential APIs, plus exfil-channel indicators
(HTTP POST, Telegram, Discord webhooks, FTP/SMTP). Reads bytes statically; executes nothing.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ASCII_RE = re.compile(rb"[\x20-\x7e]{4,}")
UTF16_RE = re.compile(rb"(?:[\x20-\x7e]\x00){4,}")
TARGETS = {
"browser_credentials": ["login data", "web data", "cookies", "logins.json", "key4.db",
"signons.sqlite", "\\user data\\", "local state"],
"crypto_wallets": ["wallet.dat", "\\electrum\\", "\\exodus\\", "\\ethereum\\keystore",
"metamask", "\\monero\\", "ledger live"],
"applications": ["filezilla\\recentservers.xml", "\\openvpn\\", "\\telegram desktop\\tdata",
"\\discord\\local storage", "winscp.ini", "\\pidgin\\", "anydesk"],
"credential_api": ["cryptunprotectdata", "vaultcli", "credenumerate", "lsadump"],
}
EXFIL = {
"telegram": [r"api\.telegram\.org", r"bot\d{6,}:[a-z0-9_-]{30,}"],
"discord_webhook": [r"discord(?:app)?\.com/api/webhooks/"],
"http_post": [r"https?://[^\s\"']+"],
"ftp": [r"ftp://[^\s\"']+"],
"smtp": [r"smtp\.[a-z0-9.-]+"],
}
def _strings(data: bytes) -> str:
parts = [m.decode("ascii", "ignore") for m in ASCII_RE.findall(data)]
parts += [m.decode("utf-16-le", "ignore") for m in UTF16_RE.findall(data)]
return "\n".join(parts)
def _defang(s: str) -> str:
return s.replace("http", "hxxp").replace(".", "[.]")
def profile(path: str) -> dict:
blob = _strings(Path(path).read_bytes())
low = blob.lower()
targets = {}
for cat, needles in TARGETS.items():
hits = sorted({n for n in needles if n in low})
if hits:
targets[cat] = hits
exfil = {}
for chan, patterns in EXFIL.items():
found = set()
for pat in patterns:
for m in re.findall(pat, blob, re.I):
found.add(_defang(m))
if found:
exfil[chan] = sorted(found)[:10]
return {
"targeted_artifacts": targets,
"exfiltration_channels": exfil,
"is_likely_infostealer": len(targets) >= 2,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Profile infostealer targets/exfil")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("profile")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(profile(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,90 @@
---
name: analyzing-java-jar-malware
description: 'Analyzes Java/JAR malware (such as Adwind/jRAT-class cross-platform RATs) by
inventorying the archive, reading the manifest entry point, detecting obfuscators and string
encryption, and flagging suspicious runtime, reflection, and networking class usage. Activates
for requests to analyze a malicious JAR, inspect Java malware, or identify a Java RAT.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- java
- jar
- rat
- decompilation
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1059
- T1027
- T1620
d3fend:
- D3-SDA
- D3-DA
references:
- 'JAR/ZIP and Java class file format — https://docs.oracle.com/javase/specs/jvms/se17/html/'
- 'MITRE ATT&CK T1027 Obfuscated Files or Information — https://attack.mitre.org/techniques/T1027/'
---
# Analyzing Java/JAR Malware
## When to Use
- You have a malicious or suspicious `.jar` (often a cross-platform RAT) and need to map its
structure, entry point, obfuscation, and capability surface before decompiling.
- You want to triage a Java payload without running the JVM.
**Do not use** `java -jar` to run it — that executes the malware. Treat the JAR as a ZIP and read
its contents statically.
## Prerequisites
- The JAR file, read inertly. Optional: a Java decompiler (CFR, Procyon) for the next stage.
## Safety & Handling
- Read the archive statically; never launch the JVM on the sample. Defang any URLs found.
## Workflow
### Step 1: Inventory the archive and entry point
```bash
python scripts/analyst.py inspect sample.jar
```
Lists `.class` files, embedded resources/payloads (nested JARs, scripts, encrypted blobs), and
reads `META-INF/MANIFEST.MF` for `Main-Class`/`Premain-Class`.
### Step 2: Detect obfuscation and packers
Flags obfuscator fingerprints (Allatori, ProGuard, Zelix), single-character class/package names,
and string-decryption indicators.
### Step 3: Flag capability classes
Surface dangerous API usage in strings/constant pools: `Runtime.exec`/`ProcessBuilder`,
`java.lang.reflect`, `URLClassLoader`, `javax.crypto`, `java.net.Socket`, registry/persistence
helpers.
### Step 4: Route to decompilation
Hand the key classes to a decompiler (CFR/Procyon) for source recovery; record IOCs.
## Validation
- The manifest entry point is read and reported.
- Embedded payloads/nested archives are enumerated.
- Capability flags are backed by concrete class/string evidence.
## Pitfalls
- String-encrypted samples where capability strings appear only after decryption.
- Multi-stage droppers that unpack a second JAR at runtime.
- Benign obfuscated commercial JARs — corroborate with capability and delivery context.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the inspector.
- JVM/JAR format and ATT&CK T1027 references (linked in frontmatter).

View File

@@ -0,0 +1,38 @@
# API Reference: Analyzing Java/JAR Malware
## Overview
`scripts/analyst.py` treats a JAR as a ZIP and inventories `.class` files and embedded payloads,
reads the manifest entry point, detects obfuscator fingerprints and name obfuscation, and scans
class constant pools for dangerous API usage. It reads the archive statically, never launches the
JVM, and defangs URLs.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | zipfile, regex, JSON, file IO |
| CFR / Procyon | optional | decompile recovered classes downstream (not required here) |
## Core functions
### `inspect(path)`
Returns class count, `Main-Class`, embedded payloads, resources, obfuscators, a name-obfuscation
flag, capability APIs, and defanged URLs.
## Capability APIs flagged
`Runtime`/`ProcessBuilder` (exec), `java.lang.reflect` + `URLClassLoader` (reflective loading),
`javax.crypto` (encryption), `java.net.Socket`/`URL` (networking), `java.awt.Robot` (surveillance).
## Usage
```bash
python scripts/analyst.py inspect sample.jar
```
## Sources
- JVM specification (class file format, constant pool) — https://docs.oracle.com/javase/specs/jvms/se17/html/
- JAR/ZIP structure and `MANIFEST.MF` `Main-Class` from the Java platform documentation
- Obfuscator fingerprints (Allatori/ProGuard/Zelix) from public Java-malware analyses

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Inspect a Java/JAR sample statically: structure, manifest, obfuscation, capabilities.
Treats the JAR as a ZIP, inventories .class files and embedded payloads, reads the manifest entry
point, and scans class bytes for obfuscator fingerprints and dangerous API usage. Reads the archive
statically; never launches the JVM. Defangs URLs.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
import zipfile
from pathlib import Path
EMBED_SIGS = [(b"PK\x03\x04", "nested ZIP/JAR"), (b"MZ", "PE executable"),
(b"#!", "script"), (b"\x7fELF", "ELF")]
OBFUSCATORS = [b"allatori", b"proguard", b"zelix", b"klassmaster", b"stringer", b"dasho"]
CAPABILITY_APIS = [b"java/lang/Runtime", b"java/lang/ProcessBuilder", b"java/lang/reflect",
b"java/net/URLClassLoader", b"javax/crypto", b"java/net/Socket",
b"java/net/URL", b"java/awt/Robot", b"java/util/Base64"]
RE_URL = re.compile(rb"https?://[\x21-\x7e]{4,}")
def _defang(s: str) -> str:
return s.replace("http", "hxxp").replace(".", "[.]")
def inspect(path: str) -> dict:
data = Path(path).read_bytes()
result: dict = {"is_jar": False}
if not zipfile.is_zipfile(path):
return {"error": "not a ZIP/JAR archive"}
result["is_jar"] = True
classes = []
resources = []
manifest = ""
obf = set()
caps = set()
urls = set()
nested = []
with zipfile.ZipFile(path) as zf:
for name in zf.namelist():
if name.endswith(".class"):
classes.append(name)
try:
body = zf.read(name)
except Exception:
continue
for o in OBFUSCATORS:
if o in body.lower():
obf.add(o.decode())
for api in CAPABILITY_APIS:
if api in body:
caps.add(api.decode().replace("/", "."))
for u in RE_URL.findall(body):
urls.add(_defang(u.decode("latin-1", "ignore")))
elif name == "META-INF/MANIFEST.MF":
manifest = zf.read(name).decode("latin-1", "ignore")
else:
resources.append(name)
try:
head = zf.read(name)[:8]
except Exception:
head = b""
for sig, label in EMBED_SIGS:
if head.startswith(sig):
nested.append({"name": name, "type": label})
main_class = ""
for line in manifest.splitlines():
if line.lower().startswith(("main-class:", "premain-class:")):
main_class = line.split(":", 1)[1].strip()
break
# single-character class names suggest name obfuscation
short_names = sum(1 for c in classes if len(Path(c).stem) <= 2)
return {
"is_jar": True,
"class_count": len(classes),
"main_class": main_class,
"embedded_payloads": nested[:50],
"resources": resources[:50],
"obfuscators": sorted(obf),
"name_obfuscation_suspected": short_names > max(3, len(classes) // 5),
"capability_apis": sorted(caps),
"urls": sorted(urls)[:50],
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Inspect a Java/JAR sample")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("inspect")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(inspect(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,90 @@
---
name: analyzing-loaders-and-droppers
description: 'Analyzes loader and dropper samples by identifying staging behavior — embedded or
downloaded next-stage payloads, decode/decrypt routines, and the execution mechanism used to run
the next stage. Activates for requests to analyze a loader or dropper, find a staged payload, or
map multi-stage delivery behavior.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- loader
- dropper
- multi-stage
- delivery
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1105
- T1140
- T1574
d3fend:
- D3-DA
- D3-FCR
references:
- 'MITRE ATT&CK T1105 Ingress Tool Transfer — https://attack.mitre.org/techniques/T1105/'
- 'MITRE ATT&CK T1140 Deobfuscate/Decode Files or Information — https://attack.mitre.org/techniques/T1140/'
---
# Analyzing Loaders and Droppers
## When to Use
- You have a first-stage loader/dropper and need to find the next stage: an embedded resource, an
appended overlay, or a download URL.
- You need to identify the decode/decrypt routine and the execution mechanism (process creation,
injection, scheduled task, DLL side-loading).
**Do not use** this to run the loader and fetch live stages — characterize staging statically and
retrieve next stages only in an isolated sandbox.
## Prerequisites
- The loader/dropper sample (read inertly).
## Safety & Handling
- Read bytes statically; defang download URLs; never execute to pull live payloads.
## Workflow
### Step 1: Locate staged payloads
```bash
python scripts/analyst.py stage sample.bin
```
Detects embedded PE/archive signatures, an appended overlay beyond the PE's mapped size, large
high-entropy blobs, and download URLs.
### Step 2: Identify decode/execution mechanism
Scan imports/strings for decode APIs (`CryptDecrypt`, base64), download APIs (`URLDownloadToFile`,
`WinHttp`, `InternetReadFile`), and execution (`CreateProcess`, `ShellExecute`, `WinExec`,
side-loading hints).
### Step 3: Map the delivery chain
Document stage-1 → decode → stage-2 → execution and map each step to ATT&CK.
### Step 4: Defang and report
Defang URLs and produce IOCs for the staging infrastructure.
## Validation
- Embedded/overlay stages are confirmed by signature or entropy, with offsets recorded.
- Download URLs and execution mechanism are identified from imports/strings.
- The multi-stage chain maps cleanly to ATT&CK techniques.
## Pitfalls
- Treating a benign overlay (installer data, signature) as a payload without corroboration.
- Missing stages fetched at runtime when no URL is in plaintext (obfuscated/encoded).
- Confusing the decode routine for the payload itself.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the stager.
- ATT&CK T1105 and T1140 (linked in frontmatter).

View File

@@ -0,0 +1,41 @@
# API Reference: Analyzing Loaders and Droppers
## Overview
`scripts/analyst.py` analyzes a loader/dropper: it finds embedded PE/archive signatures (skipping
the loader's own header), counts high-entropy windows (packed/encrypted stages), and scans strings
for download, decode, and execution APIs plus URLs. It reads bytes statically, defangs URLs, and
executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | math, regex, Counter, JSON, file IO |
## Core functions
### `stage(path)`
Returns embedded signatures, high-entropy window count, download/decode/execution API hits,
defanged URLs, and a loader likelihood flag.
### `_entropy(chunk)`
Shannon entropy (bits/byte) over a window; >7.2 suggests compressed/encrypted data.
## API categories
- **download**: `URLDownloadToFile`, `InternetReadFile`, WinHTTP/WinINet.
- **decode**: `CryptDecrypt`, `CryptStringToBinary`, base64/RC4/XOR.
- **execution**: `CreateProcess`, `ShellExecute`, `WinExec`, `CreateRemoteThread`, `LoadLibrary`.
## Usage
```bash
python scripts/analyst.py stage sample.bin
```
## Sources
- MITRE ATT&CK T1105 Ingress Tool Transfer — https://attack.mitre.org/techniques/T1105/
- MITRE ATT&CK T1140 Deobfuscate/Decode Information — https://attack.mitre.org/techniques/T1140/
- Shannon entropy definition; overlay/staging patterns from public loader analyses

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Analyze a loader/dropper: locate staged payloads and identify decode/download/execution APIs.
Detects embedded PE/archive signatures, scans strings for download/decode/execution APIs and
URLs, and computes Shannon entropy over windows to flag large high-entropy (packed/encrypted)
blobs. Reads bytes statically; executes nothing.
"""
from __future__ import annotations
import argparse
import json
import math
import re
import sys
from collections import Counter
from pathlib import Path
ASCII_RE = re.compile(rb"[\x20-\x7e]{4,}")
UTF16_RE = re.compile(rb"(?:[\x20-\x7e]\x00){4,}")
RE_URL = re.compile(r"https?://[^\s\"'<>]+", re.I)
EMBED_SIGS = [(b"MZ", "PE"), (b"PK\x03\x04", "ZIP/Office"), (b"Rar!\x1a\x07", "RAR"),
(b"\x1f\x8b", "GZIP"), (b"7z\xbc\xaf\x27\x1c", "7z")]
DOWNLOAD_API = ["urldownloadtofile", "internetreadfile", "internetopenurl", "winhttp",
"httpopenrequest", "wininet"]
DECODE_API = ["cryptdecrypt", "cryptstringtobinary", "base64", "rc4", "xor"]
EXEC_API = ["createprocess", "shellexecute", "winexec", "createremotethread", "loadlibrary",
"ntcreateuserprocess", "wow64"]
def _strings(data: bytes) -> str:
parts = [m.decode("ascii", "ignore") for m in ASCII_RE.findall(data)]
parts += [m.decode("utf-16-le", "ignore") for m in UTF16_RE.findall(data)]
return "\n".join(parts)
def _entropy(chunk: bytes) -> float:
if not chunk:
return 0.0
counts = Counter(chunk)
total = len(chunk)
return -sum((c / total) * math.log2(c / total) for c in counts.values())
def stage(path: str) -> dict:
data = Path(path).read_bytes()
embedded = []
for sig, label in EMBED_SIGS:
idx = data.find(sig, 1) # skip offset 0 (the loader's own header)
if idx != -1:
embedded.append({"offset": idx, "type": label})
win = 4096
high_entropy = sum(1 for i in range(0, len(data), win)
if _entropy(data[i:i + win]) > 7.2)
blob = _strings(data)
low = blob.lower()
urls = sorted({u.replace("http", "hxxp").replace(".", "[.]") for u in RE_URL.findall(blob)})
return {
"embedded_signatures": embedded,
"high_entropy_windows": high_entropy,
"download_apis": sorted({a for a in DOWNLOAD_API if a in low}),
"decode_apis": sorted({a for a in DECODE_API if a in low}),
"execution_apis": sorted({a for a in EXEC_API if a in low}),
"urls": urls[:50],
"is_likely_loader": bool(embedded) or bool(urls and any(a in low for a in DOWNLOAD_API)),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze loader/dropper staging")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("stage")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(stage(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,90 @@
---
name: analyzing-mach-o-binaries-on-macos
description: 'Statically analyzes macOS Mach-O malware: parsing the header and load commands,
handling fat/universal binaries, reading linked dylibs and entitlements, and checking code
signatures to infer capability and trust. Activates for requests to analyze a Mach-O binary,
inspect macOS malware, or parse load commands and entitlements.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- mach-o
- macos
- static-analysis
- code-signing
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1059.002
d3fend:
- D3-FCR
- D3-DA
references:
- 'Apple Mach-O file format reference — https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h'
- 'macOS code signing overview — https://developer.apple.com/documentation/security/code_signing_services'
---
# Analyzing Mach-O Binaries on macOS
## When to Use
- You have a macOS sample (Mach-O) and need a static capability and trust read.
- You must handle a fat/universal binary containing multiple architecture slices.
- You need to inspect linked dylibs, entitlements, and code-signing status.
**Do not use** Windows PE tooling on Mach-O — the formats differ entirely; use Mach-O-aware
parsers.
## Prerequisites
- A Mach-O parser (Python stdlib `struct`, or `macholib`/LIEF); the sample handled inertly.
- For signing/entitlements on macOS, `codesign`/`otool` are authoritative.
## Safety & Handling
- Parse statically; never execute the sample, especially on a real macOS host.
- Keep the sample password-protected at rest and reference it by hash.
## Workflow
### Step 1: Detect fat vs. thin and architecture
Check the magic: `0xCAFEBABE` (fat/universal) vs. `0xFEEDFACE`/`0xFEEDFACF` (Mach-O 32/64). For
fat binaries, enumerate and analyze each slice.
```bash
python scripts/analyst.py header sample.macho
```
### Step 2: Parse load commands
Read load commands for linked dylibs (`LC_LOAD_DYLIB`), entry point (`LC_MAIN`), and signing
(`LC_CODE_SIGNATURE`). The dylib list hints at capability (networking, crypto).
### Step 3: Inspect entitlements and signing
On macOS, use `codesign`/`otool` to read entitlements and verify the signature. Ad-hoc or absent
signatures and suspicious entitlements are risk indicators.
### Step 4: Infer capability and route
Map linked frameworks/symbols to behaviors and route to disassembly/RE for deeper analysis.
## Validation
- Fat binaries are decomposed and each slice is analyzed, not just the first.
- Load commands, dylibs, and signing status are enumerated correctly.
- Capability inferences are corroborated by linked frameworks/symbols.
## Pitfalls
- Analyzing only one slice of a universal binary.
- Trusting a present signature without verifying it (ad-hoc signatures verify but aren't trusted).
- Applying PE assumptions (sections/imports) to Mach-O structures.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the Mach-O header parser.
- Apple Mach-O loader.h and code signing docs (linked in frontmatter).

View File

@@ -0,0 +1,42 @@
# API Reference: Analyzing Mach-O Binaries on macOS
## Overview
`scripts/analyst.py` parses Mach-O headers with stdlib `struct`: it detects fat/universal vs. thin
binaries, enumerates slices, and reads load commands for linked dylibs, the entry point, and the
presence of a code signature. It parses statically and never executes the sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | struct parsing, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| codesign | Verify signature and read entitlements on macOS |
| otool | Inspect load commands and linked libraries |
## Core functions
### `parse(path)`
Dispatches on magic to handle fat vs. thin binaries; returns format, slices, and per-slice data.
### `parse_thin(data, base)`
Parses one Mach-O image's load commands (dylibs, LC_MAIN, LC_CODE_SIGNATURE).
## Usage
```bash
python scripts/analyst.py header sample.macho
```
## Sources
- Apple Mach-O `loader.h` (header and load-command structures) —
https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h
- macOS code signing overview —
https://developer.apple.com/documentation/security/code_signing_services
- Fat/universal binary layout (FAT_MAGIC + slice table), Mach-O format documentation

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""Parse a Mach-O (or fat/universal) binary header and key load commands.
Detects fat vs. thin Mach-O, enumerates architecture slices, and parses load commands for linked
dylibs, the main entry point, and the presence of a code signature. Uses stdlib ``struct`` only.
It parses bytes statically and never executes the sample.
"""
from __future__ import annotations
import argparse
import json
import struct
import sys
from pathlib import Path
MH_MAGIC = 0xFEEDFACE
MH_MAGIC_64 = 0xFEEDFACF
MH_CIGAM = 0xCEFAEDFE
MH_CIGAM_64 = 0xCFFAEDFE
FAT_MAGIC = 0xCAFEBABE
FAT_CIGAM = 0xBEBAFECF
LC_LOAD_DYLIB = 0x0C
LC_MAIN = 0x80000028
LC_CODE_SIGNATURE = 0x1D
def _u32(data: bytes, off: int, be: bool) -> int:
return struct.unpack_from(">I" if be else "<I", data, off)[0]
def parse_thin(data: bytes, base: int) -> dict:
magic = struct.unpack_from(">I", data, base)[0]
be = magic in (MH_MAGIC, MH_MAGIC_64)
is64 = magic in (MH_MAGIC_64, MH_CIGAM_64)
hdr = base
ncmds = _u32(data, hdr + 16, be)
off = hdr + (32 if is64 else 28)
dylibs, signed, entry = [], False, None
for _ in range(ncmds):
cmd = _u32(data, off, be)
cmdsize = _u32(data, off + 4, be)
if cmdsize == 0:
break
if cmd == LC_LOAD_DYLIB:
name_off = _u32(data, off + 8, be)
raw = data[off + name_off:off + cmdsize].split(b"\x00", 1)[0]
dylibs.append(raw.decode("latin-1"))
elif cmd == (LC_MAIN & 0xFFFFFFFF) or cmd == LC_MAIN:
entry = "LC_MAIN"
elif cmd == LC_CODE_SIGNATURE:
signed = True
off += cmdsize
return {"arch": "64-bit" if is64 else "32-bit", "dylibs": dylibs,
"has_code_signature": signed, "entry": entry}
def parse(path: str) -> dict:
data = Path(path).read_bytes()
magic = struct.unpack_from(">I", data, 0)[0]
if magic in (FAT_MAGIC, FAT_CIGAM):
nfat = struct.unpack_from(">I", data, 4)[0]
slices = []
for i in range(nfat):
entry_off = 8 + i * 20
offset = struct.unpack_from(">I", data, entry_off + 8)[0]
slices.append(parse_thin(data, offset))
return {"format": "fat/universal", "slice_count": nfat, "slices": slices}
if magic in (MH_MAGIC, MH_MAGIC_64, MH_CIGAM, MH_CIGAM_64):
return {"format": "thin Mach-O", **parse_thin(data, 0)}
return {"error": "not a Mach-O binary"}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Parse Mach-O header/load commands")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("header")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(parse(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,89 @@
---
name: analyzing-malicious-iso-and-container-files
description: 'Analyzes malicious ISO, IMG, VHD, and similar container files used to smuggle payloads
past email and mark-of-the-web defenses by enumerating contents and flagging hidden executables,
LNKs, and scripts. Activates for requests to analyze a malicious ISO/IMG, inspect a container
delivery file, or find payloads hidden inside a disk image attachment.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- iso
- container
- delivery
- mark-of-the-web
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1553.005
- T1204.002
- T1027
d3fend:
- D3-FCR
- D3-FA
references:
- 'MITRE ATT&CK T1553.005 Mark-of-the-Web Bypass — https://attack.mitre.org/techniques/T1553/005/'
- 'ISO 9660 file system — https://wiki.osdev.org/ISO_9660'
---
# Analyzing Malicious ISO and Container Files
## When to Use
- You have an ISO/IMG/VHD attachment suspected of smuggling a payload (LNK + hidden DLL/EXE/script)
past MOTW and scanners.
- You need to enumerate contents and flag hidden/executable entries without mounting the image.
**Do not use** mounting/auto-run to inspect — that can trigger the lure. Parse the container's
directory records statically.
## Prerequisites
- The container file (read inertly). Optional: `pycdlib` for full ISO parsing (the script falls
back to signature/string carving if absent).
## Safety & Handling
- Never mount or open the container interactively; carve statically. Defang any URLs found.
## Workflow
### Step 1: Identify the container type
```bash
python scripts/analyst.py inspect sample.iso
```
Detects ISO 9660 (`CD001`), UDF, FAT/VHD signatures and reports the format.
### Step 2: Enumerate entries and flag payloads
Lists embedded file signatures (`MZ`, `PK`, LNK `\x4C\x00\x00\x00`, script headers) and flags
hidden/executable/decoy entries.
### Step 3: Assess the lure chain
A typical chain is a visible LNK pointing at a hidden DLL/EXE/script in the same image — document
the relationship.
### Step 4: Extract and route
Carve the hidden payload and route it to the appropriate analysis workflow; defang URLs.
## Validation
- The container format is identified by signature.
- Hidden executables/LNKs/scripts are enumerated with offsets.
- The lure→payload relationship is documented.
## Pitfalls
- Mounting the image and triggering the lure.
- Missing payloads in less-common container formats (VHDX, UDF) without the right parser.
- Overlooking hidden-attribute files that the file manager would not show.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the inspector.
- ATT&CK T1553.005 and ISO 9660 references (linked in frontmatter).

View File

@@ -0,0 +1,38 @@
# API Reference: Analyzing Malicious ISO and Container Files
## Overview
`scripts/analyst.py` identifies a container's format (ISO 9660, VHD/VHDX, FAT/IMG) by signature and
carves embedded file signatures (PE, ZIP/Office, LNK, scripts) with offsets. With pycdlib it adds a
root directory listing. It reads bytes statically and never mounts the image.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| pycdlib | latest | full ISO directory listing (optional) |
| (stdlib) | — | JSON, file IO |
## Core functions
### `inspect(path)`
Returns format, ISO directory (if pycdlib), embedded signatures, and a hidden-payload flag.
### `_format(data)`
Signature-based container format detection (`CD001`, `conectix`, `vhdxfile`, FAT markers).
## Carved signatures
PE (`MZ`), ZIP/Office (`PK`), LNK header, shebang/WSF/batch scripts.
## Usage
```bash
python scripts/analyst.py inspect sample.iso
```
## Sources
- MITRE ATT&CK T1553.005 Mark-of-the-Web Bypass — https://attack.mitre.org/techniques/T1553/005/
- ISO 9660 (`CD001` at sector 16) — https://wiki.osdev.org/ISO_9660
- VHD `conectix` / VHDX `vhdxfile` signatures from Microsoft VHD/VHDX specifications

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Inspect ISO/IMG/VHD container files for hidden/smuggled payloads.
Identifies the container format by signature and carves embedded file signatures (PE, archives,
LNK, scripts) with offsets. Uses pycdlib for full ISO directory listing when available; otherwise
falls back to signature carving. Reads bytes statically; never mounts the image.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
try:
import pycdlib # type: ignore
HAVE_PYCDLIB = True
except ImportError:
HAVE_PYCDLIB = False
EMBED_SIGS = [
(b"MZ", "PE executable"),
(b"PK\x03\x04", "ZIP/Office"),
(b"\x4c\x00\x00\x00\x01\x14\x02\x00", "LNK shortcut"),
(b"#!", "script (shebang)"),
(b"<job", "WSF job"),
(b"@echo", "batch script"),
]
def _format(data: bytes) -> str:
if data[0x8001:0x8006] == b"CD001" or b"CD001" in data[:0x10000]:
return "ISO 9660"
if data[:8] == b"conectix":
return "VHD"
if data[:8] == b"vhdxfile":
return "VHDX"
if data[0x36:0x3b] == b"FAT16" or data[0x52:0x57] == b"FAT32":
return "FAT (IMG)"
return "unknown"
def _carve(data: bytes) -> list[dict]:
found = []
for sig, label in EMBED_SIGS:
start = 0
while len(found) < 100:
idx = data.find(sig, start)
if idx == -1:
break
found.append({"offset": idx, "type": label})
start = idx + 1
return found
def _iso_listing(path: str) -> list[str]:
if not HAVE_PYCDLIB:
return []
names = []
try:
iso = pycdlib.PyCdlib()
iso.open(path)
for child in iso.list_children(iso_path="/"):
ident = getattr(child.file_identifier(), "decode", lambda *_: "")("utf-8", "ignore")
if ident and ident not in (".", ".."):
names.append(ident)
iso.close()
except Exception:
return []
return names
def inspect(path: str) -> dict:
data = Path(path).read_bytes()
carved = _carve(data)
return {
"format": _format(data),
"pycdlib_available": HAVE_PYCDLIB,
"iso_directory": _iso_listing(path),
"embedded_signatures": carved,
"hidden_payload_suspected": any(c["type"].startswith(("PE", "LNK", "script"))
for c in carved),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Inspect ISO/container files")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("inspect")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(inspect(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,96 @@
---
name: analyzing-malicious-lnk-files
description: 'Analyzes weaponized Windows shortcut (.lnk) files: parsing the shell link structure
for the target command, arguments, icon, and working directory, and recovering hidden
PowerShell/cmd payloads and embedded content used in phishing. Activates for requests to
analyze a malicious LNK, parse a shortcut file, or extract a command from a .lnk.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- lnk
- shortcut
- document-malware
- static-analysis
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1204.002
- T1059.001
- T1059.003
d3fend:
- D3-FCR
- D3-DA
references:
- 'Microsoft [MS-SHLLINK] Shell Link Binary File Format — https://learn.microsoft.com/openspecs/windows_protocols/ms-shllink/'
- 'LNK forensic structure references — https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc'
---
# Analyzing Malicious LNK Files
## When to Use
- You have a `.lnk` delivered via phishing (often in an archive or ISO) and need its real action.
- You must recover the target command line, arguments, and any embedded/hidden payload.
- You are extracting IOCs (commands, URLs, file paths) from a shortcut.
**Do not use** Windows Explorer to inspect or double-click the shortcut — that executes its
target. Parse the binary structure statically.
## Prerequisites
- An LNK parser (Python `struct`, or `lnkparse`/`LECmd`); the sample handled inertly.
## Safety & Handling
- Never double-click or hover-load the shortcut in Explorer; parse raw bytes only.
- Defang recovered URLs and store any embedded payload password-protected.
## Workflow
### Step 1: Confirm and parse the header
Verify the LNK magic (`4C 00 00 00` + the link CLSID) and read `LinkFlags` to know which optional
structures are present.
```bash
python scripts/analyst.py parse sample.lnk
```
### Step 2: Recover the target and arguments
Extract the relative/local path and, crucially, the `COMMAND_LINE_ARGUMENTS` string — malicious
LNKs hide `powershell -enc ...` or `cmd /c ...` here.
### Step 3: Inspect icon and metadata
Read the icon location (often spoofed to look like a document) and metadata (machine ID,
timestamps) useful for attribution/clustering.
### Step 4: Find appended/embedded payloads
Check for data appended after the LNK structure (some campaigns embed scripts/binaries past the
parsed end) and extract it.
### Step 5: Decode and extract IOCs
Decode encoded commands (UTF-16LE base64), defang URLs, and route extracted payloads to the right
analysis workflow.
## Validation
- The full command line, including arguments, is recovered (not just the target path).
- Encoded commands are decoded to cleartext.
- Appended/embedded payloads are detected and extracted, and IOCs are defanged.
## Pitfalls
- Reading only the target path and missing the malicious arguments.
- Inspecting in Explorer and executing the shortcut.
- Ignoring data appended past the LNK structure.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the LNK parser.
- MS-SHLLINK and the liblnk format notes (linked in frontmatter).

View File

@@ -0,0 +1,42 @@
# API Reference: Analyzing Malicious LNK Files
## Overview
`scripts/analyst.py` is a minimal [MS-SHLLINK] parser: it validates the LNK header and CLSID, reads
LinkFlags, skips the IDList/LinkInfo, and extracts the StringData fields (name, relative path,
working dir, arguments, icon), plus the count of any appended data. It parses statically and never
executes the shortcut.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | struct parsing, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| LECmd / lnkparse | Full-featured LNK parsing including extra data blocks |
## Core functions
### `parse(path)`
Returns LinkFlags and the recovered StringData fields, appended-data size, and a suspicious flag.
### `_read_stringdata(data, off, unicode_)`
Reads a CountedString (ANSI or UTF-16LE) per the IsUnicode flag.
## Usage
```bash
python scripts/analyst.py parse sample.lnk
```
## Sources
- Microsoft [MS-SHLLINK] Shell Link Binary File Format —
https://learn.microsoft.com/openspecs/windows_protocols/ms-shllink/
- liblnk LNK format documentation —
https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc
- Malicious-argument hiding in COMMAND_LINE_ARGUMENTS, established document-analysis practice

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Parse a Windows shell link (.lnk) and recover its command and arguments.
Implements a minimal [MS-SHLLINK] parser using stdlib ``struct``: it validates the header,
reads LinkFlags, and walks the optional StringData section to extract the name, relative path,
working directory, command-line arguments, and icon location. It also reports any data appended
past the structure. It parses bytes statically and never executes the shortcut.
"""
from __future__ import annotations
import argparse
import json
import struct
import sys
from pathlib import Path
HEADER_SIZE = 0x4C
LINK_CLSID = b"\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46"
# LinkFlags bits relevant to StringData (MS-SHLLINK 2.1.1)
F_HAS_LINK_TARGET_IDLIST = 0x00000001
F_HAS_NAME = 0x00000004
F_HAS_RELATIVE_PATH = 0x00000008
F_HAS_WORKING_DIR = 0x00000010
F_HAS_ARGUMENTS = 0x00000020
F_HAS_ICON_LOCATION = 0x00000040
F_IS_UNICODE = 0x00000080
def _read_stringdata(data: bytes, off: int, unicode_: bool) -> tuple[str, int]:
count = struct.unpack_from("<H", data, off)[0]
off += 2
if unicode_:
raw = data[off:off + count * 2]
off += count * 2
return raw.decode("utf-16-le", "replace"), off
raw = data[off:off + count]
off += count
return raw.decode("latin-1", "replace"), off
def parse(path: str) -> dict:
data = Path(path).read_bytes()
if len(data) < HEADER_SIZE or struct.unpack_from("<I", data, 0)[0] != HEADER_SIZE:
return {"error": "not a LNK (bad HeaderSize)"}
if data[4:20] != LINK_CLSID:
return {"error": "not a LNK (bad CLSID)"}
flags = struct.unpack_from("<I", data, 20)[0]
unicode_ = bool(flags & F_IS_UNICODE)
off = HEADER_SIZE
# Skip LinkTargetIDList if present
if flags & F_HAS_LINK_TARGET_IDLIST:
idlist_size = struct.unpack_from("<H", data, off)[0]
off += 2 + idlist_size
# Skip LinkInfo if present (HasLinkInfo = 0x2)
if flags & 0x00000002:
link_info_size = struct.unpack_from("<I", data, off)[0]
off += link_info_size
result = {"flags": hex(flags), "unicode": unicode_}
for bit, key in [(F_HAS_NAME, "name"), (F_HAS_RELATIVE_PATH, "relative_path"),
(F_HAS_WORKING_DIR, "working_dir"), (F_HAS_ARGUMENTS, "arguments"),
(F_HAS_ICON_LOCATION, "icon_location")]:
if flags & bit and off < len(data):
try:
value, off = _read_stringdata(data, off, unicode_)
result[key] = value
except struct.error:
break
result["appended_data_bytes"] = max(0, len(data) - off)
args = result.get("arguments", "")
result["suspicious"] = any(t in args.lower() for t in
("powershell", "-enc", "cmd", "mshta", "rundll32", "frombase64"))
return result
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Parse a malicious LNK")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("parse")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(parse(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,108 @@
---
name: analyzing-malicious-office-macros
description: 'Analyzes malicious Office documents by extracting and reviewing VBA macros and
OLE/OOXML structure: auto-exec triggers, obfuscation, dropped payloads, and shell/PowerShell
invocation. Activates for requests to analyze a malicious Word/Excel document, VBA macro, or
maldoc.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware
- maldoc
- vba
- office
- olevba
- phishing
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1566.001
- T1059.005
- T1204.002
d3fend:
- D3-FCR
- D3-DA
references:
- 'oletools (olevba) — https://github.com/decalage2/oletools'
- 'MS-OVBA VBA file format — https://learn.microsoft.com/openspecs/office_file_formats/ms-ovba/'
---
# Analyzing Malicious Office Macros
## When to Use
- A Word/Excel/PowerPoint document is suspected of delivering malware via macros.
- You need to extract VBA, identify auto-exec triggers, and see what the macro does.
- You are determining the next-stage payload or URL a maldoc fetches.
**Do not use** the document's "Enable Content" path to analyze it — never open a maldoc in
Office. Extract and read the macro statically; detonate only in the isolated lab if needed.
## Prerequisites
- `oletools` (`pip install oletools`) for `olevba`/`oleid`, or the bundled OLE/OOXML extractor.
- The document in neutralized form inside the lab.
## Safety & Handling
- Do **not** open the document in Microsoft Office. Macros may auto-run on open/close.
- Static extraction only; if dynamic analysis is required, detonate in the isolated victim VM.
## Workflow
### Step 1: Identify the container and locate macros
OOXML (`.docm`, `.xlsm`) is a ZIP; legacy (`.doc`, `.xls`) is OLE2. Macros live in a
`vbaProject.bin` OLE stream. Extract it:
```bash
python scripts/analyst.py extract maldoc.docm
```
### Step 2: Flag auto-exec triggers
Look for entry points that run without user action beyond enabling macros:
```text
AutoOpen, Document_Open, AutoClose, Workbook_Open, Auto_Open, Document_Close
```
### Step 3: Find suspicious calls
Identify execution and download primitives:
```text
Shell, WScript.Shell, CreateObject, powershell, cmd /c
URLDownloadToFile, MSXML2.XMLHTTP, ADODB.Stream (drop to disk)
Environ, GetObject("winmgmts:") (WMI)
```
### Step 4: Deobfuscate
Maldocs commonly use Chr()/string concatenation, base64, and split-and-join. Resolve these to
recover the real command and any URL or dropped path.
### Step 5: Extract IOCs and the next stage
Pull URLs, dropped file paths, and the spawned command line. These become the report's IOCs
and pivot points.
## Validation
- The recovered command line and URL make sense together (download → drop → execute).
- Auto-exec triggers explain how the macro starts.
- Deobfuscated strings match what dynamic detonation would reveal (if you confirm in the lab).
## Pitfalls
- Opening the document in Office "just to see" — this executes the macro.
- Stopping at obfuscated source without resolving it; the IOCs are inside the obfuscation.
- Missing stomped VBA (p-code present, source removed) — check for VBA stomping and analyze
p-code if the source looks empty.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the extractor and
indicator scanner.
- oletools and MS-OVBA (linked in frontmatter).

View File

@@ -0,0 +1,44 @@
# API Reference: Analyzing Malicious Office Macros
## Overview
`scripts/analyst.py` extracts VBA macros and flags auto-exec triggers, suspicious calls, and
URLs. It prefers `oletools`/olevba and falls back to locating `vbaProject.bin` in OOXML ZIP
containers (or scanning OLE2 bytes) when oletools is unavailable.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| oletools | >= 0.60 | VBA extraction (olevba), OLE inspection (optional but recommended) |
| (stdlib) | — | zipfile/regex fallback |
## External tools
| Tool | Purpose |
|------|---------|
| olevba / ol0id (oletools) | Macro extraction and document triage |
| oledump.py | Manual OLE stream inspection |
## Core functions
### `analyze(path)`
Dispatches to olevba or the minimal extractor; returns macro presence, auto-exec triggers,
suspicious calls, and URLs.
### `_scan_text(text)`
Flags auto-exec entry points, suspicious VBA calls, and extracts URLs from macro source.
## Usage
```bash
pip install oletools
python scripts/analyst.py extract maldoc.docm
```
## Sources
- oletools (olevba) — https://github.com/decalage2/oletools
- MS-OVBA VBA file format —
https://learn.microsoft.com/openspecs/office_file_formats/ms-ovba/
- VBA auto-exec trigger names (Office VBA object model documentation)

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""Extract and triage VBA macros from Office documents.
Prefers ``oletools`` (olevba) for robust extraction; falls back to locating ``vbaProject.bin``
inside OOXML (ZIP) containers and flagging suspicious indicators in the raw bytes. Never opens
the document in Office and never executes anything.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
import zipfile
from pathlib import Path
AUTOEXEC = ["AutoOpen", "Document_Open", "AutoClose", "Document_Close",
"Workbook_Open", "Auto_Open", "Auto_Close"]
SUSPICIOUS = ["Shell", "WScript.Shell", "CreateObject", "powershell", "cmd",
"URLDownloadToFile", "XMLHTTP", "ADODB.Stream", "Environ",
"winmgmts", "Base64", "Chr(", "GetObject"]
def _scan_text(text: str) -> dict:
found_auto = [k for k in AUTOEXEC if k.lower() in text.lower()]
found_sus = [k for k in SUSPICIOUS if k.lower() in text.lower()]
urls = sorted(set(re.findall(r"https?://[^\s\"'<>)]+", text)))
return {"auto_exec": found_auto, "suspicious": found_sus, "urls": urls}
def extract_with_olevba(path: str) -> dict:
from oletools.olevba import VBA_Parser # type: ignore
vp = VBA_Parser(path)
macros = []
if vp.detect_vba_macros():
for _, _, name, code in vp.extract_macros():
macros.append({"name": name, **_scan_text(code),
"lines": code.count("\n") + 1})
vp.close()
return {"engine": "olevba", "has_macros": bool(macros), "macros": macros}
def extract_minimal(path: str) -> dict:
p = Path(path)
data = p.read_bytes()
indicators = {}
if zipfile.is_zipfile(path):
with zipfile.ZipFile(path) as zf:
vba = [n for n in zf.namelist() if n.lower().endswith("vbaproject.bin")]
if not vba:
return {"engine": "minimal", "has_macros": False,
"note": "no vbaProject.bin in OOXML container"}
blob = zf.read(vba[0])
# vbaProject.bin is OLE/compressed; scan recoverable ASCII for indicators
text = blob.decode("latin-1", "replace")
indicators = _scan_text(text)
return {"engine": "minimal", "has_macros": True,
"stream": vba[0], "indicators": indicators,
"note": "install oletools for full VBA source extraction"}
# legacy OLE2: scan whole file bytes for indicator strings
text = data.decode("latin-1", "replace")
return {"engine": "minimal", "has_macros": b"Attribut" in data,
"indicators": _scan_text(text),
"note": "legacy OLE2; install oletools for reliable extraction"}
def analyze(path: str) -> dict:
try:
return extract_with_olevba(path)
except ImportError:
return extract_minimal(path)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze Office macros")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("extract")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,93 @@
---
name: analyzing-malicious-onenote-and-html-smuggling
description: 'Analyzes two modern delivery techniques: malicious OneNote (.one) attachments with
embedded payloads behind fake buttons, and HTML smuggling pages that reconstruct payloads in the
browser from embedded blobs. Activates for requests to analyze a malicious OneNote file, detect
HTML smuggling, or extract a smuggled/embedded payload.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- onenote
- html-smuggling
- document-malware
- delivery
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027.006
- T1204.002
- T1566.001
d3fend:
- D3-FCR
- D3-DA
references:
- 'MITRE ATT&CK T1027.006 HTML Smuggling — https://attack.mitre.org/techniques/T1027/006/'
- 'OneNote file format (MS-ONESTORE) — https://learn.microsoft.com/openspecs/office_file_formats/ms-onestore/'
---
# Analyzing Malicious OneNote and HTML Smuggling
## When to Use
- You have a OneNote (`.one`) attachment suspected of hiding an embedded executable/script behind a
"click to view" lure.
- You have an HTML/SVG page that reconstructs and auto-downloads a payload from an embedded blob
(HTML smuggling).
- You need to extract the hidden payload without triggering the lure.
**Do not use** OneNote or a browser to open these files for analysis — that is exactly the
delivery mechanism. Carve the embedded data statically.
## Prerequisites
- A static carver (Python) for embedded file signatures; the sample handled inertly.
## Safety & Handling
- Never open the `.one` in OneNote or the HTML in a browser.
- Defang URLs and store carved payloads password-protected.
## Workflow
### Step 1: For OneNote — carve embedded files
OneNote stores attached files in the document. Scan for embedded file signatures (`MZ`, script
headers, archives) and the `FileDataStoreObject` GUIDs, and carve them out.
```bash
python scripts/analyst.py carve sample.one
```
### Step 2: For HTML smuggling — find the embedded blob
Look for large base64/`Blob`/`Uint8Array` constructions, `data:` URIs, and a JS routine that
builds a Blob and triggers a download (`msSaveOrOpenBlob`, anchor `download`, `createObjectURL`).
### Step 3: Reconstruct the payload statically
Decode the embedded base64/byte array (and any XOR/char-code layer) to recover the payload as
data — without executing the page.
### Step 4: Analyze and extract IOCs
Hash carved payloads, identify their type, defang any URLs, and route executables/scripts to the
appropriate analysis workflow.
## Validation
- Embedded payloads are carved/reconstructed from the inert file, not by opening it.
- The recovered payload's type is identified and hashed.
- The lure/trigger mechanism (fake button, Blob download) is documented.
## Pitfalls
- Opening the OneNote/HTML to "see" the lure and executing the payload.
- Decoding only the first layer when the blob is additionally XOR/char-code encoded.
- Missing multiple embedded objects in a single OneNote page.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the carver/decoder.
- ATT&CK T1027.006 and MS-ONESTORE (linked in frontmatter).

View File

@@ -0,0 +1,34 @@
# API Reference: Analyzing Malicious OneNote and HTML Smuggling
## Overview
`scripts/analyst.py` carves embedded file signatures (PE, archives, scripts) from OneNote/binary
files by offset, and detects HTML smuggling by locating large base64 blobs that decode to
executables/archives alongside Blob-download hints. It reads statically and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | base64, regex, JSON, file IO |
## Core functions
### `carve(data)`
Returns offsets and types of embedded file signatures.
### `html_smuggle(data)`
Returns smuggling hints (Blob/Uint8Array/download) and any base64 blobs that decode to payloads.
## Usage
```bash
python scripts/analyst.py carve sample.one
```
## Sources
- MITRE ATT&CK T1027.006 HTML Smuggling — https://attack.mitre.org/techniques/T1027/006/
- OneNote file format (MS-ONESTORE, embedded FileDataStoreObject) —
https://learn.microsoft.com/openspecs/office_file_formats/ms-onestore/
- Blob/createObjectURL/msSaveOrOpenBlob smuggling pattern, established delivery-analysis practice

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Carve embedded payloads from OneNote files and reconstruct HTML-smuggling blobs.
For OneNote (and any binary), it scans for embedded file signatures (PE, archives, scripts) and
reports their offsets. For HTML, it locates large base64/byte-array blobs and decodes them to
recover a smuggled payload. It reads bytes/text statically and executes nothing.
"""
from __future__ import annotations
import argparse
import base64
import binascii
import json
import re
import sys
from pathlib import Path
EMBED_SIGS = [
(b"MZ", "PE executable"),
(b"PK\x03\x04", "ZIP/Office/JAR"),
(b"\x1f\x8b", "GZIP"),
(b"Rar!\x1a\x07", "RAR"),
(b"%PDF", "PDF"),
(b"#!", "script (shebang)"),
(b"<job", "WSF job"),
]
RE_B64_BLOB = re.compile(rb"[A-Za-z0-9+/]{200,}={0,2}")
SMUGGLE_HINTS = [b"msSaveOrOpenBlob", b"createObjectURL", b"Uint8Array", b"new Blob",
b"download=", b"base64"]
def carve(data: bytes) -> list[dict]:
found = []
for sig, label in EMBED_SIGS:
start = 0
while True:
idx = data.find(sig, start)
if idx == -1:
break
found.append({"offset": idx, "type": label})
start = idx + 1
if len(found) > 50:
return found
return found
def html_smuggle(data: bytes) -> dict:
hints = sorted({h.decode() for h in SMUGGLE_HINTS if h in data})
decoded = []
for m in RE_B64_BLOB.findall(data):
try:
raw = base64.b64decode(m)
except (binascii.Error, ValueError):
continue
if raw[:2] == b"MZ" or raw[:4] == b"PK\x03\x04" or raw[:4] == b"%PDF":
decoded.append({"size": len(raw), "carved_type": carve(raw)[:1]})
return {"smuggling_hints": hints, "decoded_blobs": decoded,
"likely_smuggling": bool(hints and decoded)}
def analyze(path: str) -> dict:
data = Path(path).read_bytes()
return {
"embedded_signatures": carve(data),
"html_smuggling": html_smuggle(data),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Carve OneNote / HTML-smuggling payloads")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("carve")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,91 @@
---
name: analyzing-malicious-pdfs
description: 'Analyzes malicious PDF documents: parsing the object structure for JavaScript,
OpenAction/launch triggers, embedded files, and URIs, then extracting and decoding suspicious
streams without opening the document in a reader. Activates for requests to analyze a malicious
PDF, inspect PDF JavaScript, or extract embedded payloads from a PDF.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- pdf
- document-malware
- javascript
- static-analysis
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1204.002
- T1059.007
- T1027
d3fend:
- D3-FCR
- D3-DA
references:
- 'PDF 32000-1:2008 (ISO PDF specification) — https://www.pdfa.org/resource/iso-32000-pdf/'
- 'Didier Stevens PDF tools (pdfid, pdf-parser) — https://blog.didierstevens.com/programs/pdf-tools/'
---
# Analyzing Malicious PDFs
## When to Use
- You have a suspicious PDF (often from phishing) and must determine if it is weaponized.
- You need to find auto-executing actions, embedded JavaScript, launch actions, or embedded files.
- You want to extract and decode suspicious streams safely.
**Do not use** a PDF reader to "just look" — opening in a viewer can trigger the very actions you
are investigating. Parse the structure statically.
## Prerequisites
- A PDF object parser (Python, or pdfid/pdf-parser/peepdf); the sample handled inertly.
## Safety & Handling
- Never open the PDF in a graphical reader; parse the raw object structure only.
- Defang any extracted URLs and store embedded payloads password-protected.
## Workflow
### Step 1: Triage the object inventory
Count high-risk keywords: `/JavaScript`, `/JS`, `/OpenAction`, `/AA`, `/Launch`, `/EmbeddedFile`,
`/URI`, `/RichMedia`. Their presence (especially auto-actions) flags weaponization.
```bash
python scripts/analyst.py triage sample.pdf
```
### Step 2: Locate auto-execution triggers
Resolve `/OpenAction` and `/AA` (additional actions) to see what runs on open, and any `/Launch`
actions that spawn external programs.
### Step 3: Extract and decode streams
Pull JavaScript and suspicious streams, applying filters (`/FlateDecode`, `/ASCIIHexDecode`) to
get the cleartext; deobfuscate layered JS as needed.
### Step 4: Recover payloads and IOCs
Extract embedded files and URIs; hash payloads, defang URLs, and route extracted executables to
PE analysis.
## Validation
- Auto-execution triggers (`/OpenAction`, `/AA`, `/Launch`) are resolved to concrete actions.
- JavaScript/streams are decoded through their filters, not left encoded.
- Embedded payloads and URIs are extracted, hashed, and defanged.
## Pitfalls
- Opening the PDF in a reader and triggering the payload.
- Missing object-stream (`/ObjStm`) compressed objects that hide the malicious content.
- Stopping at the first JS layer when it is multiply obfuscated.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the PDF triage parser.
- ISO 32000 and Didier Stevens' PDF tools (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Analyzing Malicious PDFs
## Overview
`scripts/analyst.py` scans raw PDF bytes for weaponization keywords (/JavaScript, /OpenAction,
/Launch, /EmbeddedFile, /URI, /ObjStm, etc.), counts objects/streams, extracts and defangs URIs,
and flags likely weaponization. It parses statically and never opens the document.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| pdfid / pdf-parser (Didier Stevens) | Object inventory and stream extraction with filters |
| peepdf | Interactive PDF analysis and JS extraction |
## Core functions
### `triage(path)`
Returns object/stream counts, risk-keyword counts, auto-action list, defanged URIs, and a
weaponization flag.
## Usage
```bash
python scripts/analyst.py triage sample.pdf
```
## Sources
- PDF 32000-1:2008 (ISO PDF specification) — https://www.pdfa.org/resource/iso-32000-pdf/
- Didier Stevens PDF tools (pdfid, pdf-parser) —
https://blog.didierstevens.com/programs/pdf-tools/
- /OpenAction, /AA, /Launch auto-execution and /ObjStm hiding, established PDF-analysis practice

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Triage a PDF's object structure for weaponization indicators.
Scans the raw PDF bytes for high-risk keywords (/JavaScript, /OpenAction, /Launch, /EmbeddedFile,
/URI, etc.), counts indirect objects and streams, and extracts URIs. It parses bytes statically
and never opens the document in a reader, so auto-actions cannot fire.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
KEYWORDS = ["/JavaScript", "/JS", "/OpenAction", "/AA", "/Launch", "/EmbeddedFile",
"/URI", "/RichMedia", "/SubmitForm", "/GoToR", "/ObjStm", "/AcroForm",
"/XFA", "/JBIG2Decode"]
AUTO_ACTION = {"/OpenAction", "/AA", "/Launch"}
RE_URI = re.compile(rb"/URI\s*\(([^)]*)\)")
RE_OBJ = re.compile(rb"\d+\s+\d+\s+obj")
RE_STREAM = re.compile(rb"\bstream\b")
def _defang(url: str) -> str:
return url.replace("http", "hxxp").replace(".", "[.]")
def triage(path: str) -> dict:
data = Path(path).read_bytes()
if not data.lstrip().startswith(b"%PDF"):
return {"error": "not a PDF (no %PDF header)"}
counts = {kw: len(re.findall(re.escape(kw.encode()), data)) for kw in KEYWORDS}
counts = {k: v for k, v in counts.items() if v}
uris = sorted({m.decode("latin-1", "replace") for m in RE_URI.findall(data)})
auto = [kw for kw in AUTO_ACTION if counts.get(kw)]
has_js = bool(counts.get("/JavaScript") or counts.get("/JS"))
return {
"objects": len(RE_OBJ.findall(data)),
"streams": len(RE_STREAM.findall(data)),
"risk_keywords": counts,
"auto_actions": auto,
"has_javascript": has_js,
"uris": [_defang(u) for u in uris],
"weaponized_likely": bool(auto and has_js) or bool(counts.get("/Launch")),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Triage a malicious PDF")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("triage")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(triage(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,91 @@
---
name: analyzing-malicious-vbscript-and-wsf
description: 'Analyzes malicious VBScript, WSF, and HTA scripts: parsing WSF/HTA containers,
decoding VBScript.Encode (#@~^) and string obfuscation, and identifying WScript.Shell/ActiveX
abuse to recover dropper behavior. Activates for requests to analyze VBScript, decode a WSF/HTA,
or investigate a Windows Script Host dropper.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- vbscript
- wsf
- hta
- document-malware
- deobfuscation
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1059.005
- T1059.003
- T1027
d3fend:
- D3-FCR
- D3-DA
references:
- 'Microsoft Windows Script Host documentation — https://learn.microsoft.com/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/'
- 'VBScript.Encode / Script Encoder format analysis — https://gist.github.com/bcse/1834878'
---
# Analyzing Malicious VBScript and WSF
## When to Use
- You have a `.vbs`, `.wsf`, or `.hta` delivered via phishing and need its real behavior.
- You must decode `VBScript.Encode`-obfuscated script (the `#@~^` marker) or string obfuscation.
- You are identifying WScript.Shell/ActiveX-based dropper actions and IOCs.
**Do not use** `wscript`/`cscript`/`mshta` to run the script for analysis — that executes the
dropper. Decode and read it statically.
## Prerequisites
- A static decoder (Python) or a script sandbox; the sample handled inertly in the lab.
## Safety & Handling
- Never run the script with Windows Script Host (`wscript`/`cscript`/`mshta`).
- Defang recovered URLs; store dropped payloads password-protected.
## Workflow
### Step 1: Parse the container
For WSF, parse the XML `<job>`/`<script>` elements (a WSF can bundle multiple languages). For HTA,
separate the HTML wrapper from the `<script>` body.
```bash
python scripts/analyst.py analyze sample.wsf
```
### Step 2: Decode VBScript.Encode
If you see the `#@~^` header, decode the Microsoft Script Encoder stream back to source.
### Step 3: Unroll string obfuscation
Resolve `Chr()`/`Asc()` builds, string concatenation, and `Replace()`-based deobfuscators to
reveal commands and URLs.
### Step 4: Identify dropper actions and IOCs
Find `CreateObject("WScript.Shell")`, `Run`/`Exec`, ADODB/MSXML download patterns, and PowerShell
hand-offs; recover URLs/paths, defang, and route payloads onward.
## Validation
- WSF/HTA containers are decomposed into their script bodies (all jobs/languages).
- `VBScript.Encode` streams are decoded to readable source.
- Dropper actions and IOCs are recovered without executing the script.
## Pitfalls
- Executing with `wscript`/`mshta` to "see" behavior, infecting the host.
- Missing one `<script>` block in a multi-job WSF.
- Overlooking the `#@~^` encoded section and analyzing only the cleartext stub.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the WSF/VBScript decoder.
- Windows Script Host docs and the Script Encoder format (linked in frontmatter).

View File

@@ -0,0 +1,37 @@
# API Reference: Analyzing Malicious VBScript and WSF
## Overview
`scripts/analyst.py` extracts `<script>` bodies from WSF/HTA containers, detects and isolates
Microsoft Script Encoder (`#@~^`) blocks, and flags suspicious WScript/ActiveX dropper APIs. It
parses text only and never invokes Windows Script Host. Full Script Encoder decoding is delegated
to a dedicated decoder; the script isolates encoded blocks and confirms their presence.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `analyze(text)`
Returns script-block counts, encoded-block presence, suspicious API hits, and defanged URLs.
### `_isolate_script_encoder(encoded)`
Strips the `#@~^`/`^#~@` markers and framing to isolate the encoded body for follow-on decoding.
## Usage
```bash
python scripts/analyst.py analyze sample.wsf
```
## Sources
- Microsoft Windows Script Host documentation —
https://learn.microsoft.com/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/
- VBScript.Encode / Script Encoder format (`#@~^` marker) analysis —
https://gist.github.com/bcse/1834878
- WScript.Shell / ADODB.Stream / MSXML2.XMLHTTP dropper patterns, established script-analysis
practice

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Analyze WSF/HTA/VBScript droppers statically.
Extracts ``<script>`` bodies from WSF/HTA XML/HTML containers, decodes Microsoft Script Encoder
(``VBScript.Encode``, ``#@~^``) streams, and flags suspicious WScript/ActiveX dropper patterns.
It parses text only and never invokes Windows Script Host.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
RE_SCRIPT = re.compile(r"<script\b[^>]*>(.*?)</script>", re.I | re.S)
RE_ENCODED = re.compile(r"#@~\^.*?\^#~@", re.S)
RE_URL = re.compile(r"https?://[^\s'\"<>)]+", re.I)
SUSPECT = ["wscript.shell", "createobject", ".run", ".exec", "powershell", "adodb.stream",
"msxml2.xmlhttp", "winhttp", "shell.application", "regwrite"]
def _isolate_script_encoder(encoded: str) -> str:
"""Isolate the body of a #@~^...^#~@ Script Encoder block for follow-on decoding.
Full Script Encoder decoding relies on a published ~140-entry substitution table; this
strips the wrapper markers and the 6-character length/checksum framing so the encoded body
can be handed to a dedicated decoder. The goal here is detection and isolation, not a
reimplementation of the entire table.
"""
m = re.match(r"#@~\^.{6}==(.*)\^#~@", encoded, re.S)
return m.group(1) if m else encoded
def analyze(text: str) -> dict:
scripts = [s.strip() for s in RE_SCRIPT.findall(text)]
if not scripts:
scripts = [text] # bare .vbs
encoded_blocks = RE_ENCODED.findall(text)
isolated = [_isolate_script_encoder(b) for b in encoded_blocks]
combined = "\n".join(scripts + isolated).lower()
indicators = sorted({s for s in SUSPECT if s in combined})
urls = sorted(set(RE_URL.findall(text)))
return {
"script_blocks": len(scripts),
"script_encoder_blocks": len(encoded_blocks),
"encoded_block_present": bool(encoded_blocks),
"suspicious_apis": indicators,
"urls": [u.replace("http", "hxxp").replace(".", "[.]") for u in urls],
"note": "encoded blocks isolated; finish Script Encoder decode with a dedicated decoder",
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze VBScript/WSF/HTA")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("analyze")
p.add_argument("file")
args = parser.parse_args(argv)
text = Path(args.file).read_text(encoding="utf-8-sig", errors="replace")
print(json.dumps(analyze(text), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,119 @@
---
name: analyzing-malware-in-memory-with-volatility3
description: 'Analyzes a memory image with Volatility 3 to find malware: rogue processes,
injected code, suspicious network connections, loaded modules, and persistence, then
extracts artifacts for further analysis. Activates for requests to do memory forensics,
analyze a RAM dump, or hunt malware in memory with Volatility.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware
- memory-forensics
- volatility3
- process-injection
- dfir
- windows
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1055
- T1057
- T1003
- T1056
d3fend:
- D3-PSA
- D3-PMAD
references:
- 'Volatility 3 documentation — https://volatility3.readthedocs.io/'
- 'The Art of Memory Forensics (concepts) — https://www.memoryanalysis.net/'
---
# Analyzing Malware in Memory with Volatility 3
## When to Use
- You have a RAM image from a suspected-infected host and need to find malicious activity.
- Disk artifacts are insufficient (fileless/in-memory malware) and you need volatile evidence.
- You want to extract injected code, command lines, or network connections for analysis.
**Do not use** Volatility plugins blindly without an order of investigation — start broad
(processes, network) before deep per-process dumps.
## Prerequisites
- Volatility 3 (`pip install volatility3`) with appropriate symbol tables.
- A memory image acquired with a sound tool (WinPmem, LiME, or hypervisor snapshot).
- Knowledge of the source OS/version to select the right symbols.
## Safety & Handling
- Work on a copy of the image; preserve the original with a recorded hash.
- Treat any dumped executable region as a live sample — store and handle it accordingly.
## Workflow
### Step 1: Enumerate processes and spot anomalies
```bash
vol -f memory.raw windows.pslist
vol -f memory.raw windows.pstree
```
Look for unusual parents (Word spawning cmd/powershell), masquerading names
(`scvhost.exe`), processes with no disk path, and orphaned children.
### Step 2: Hunt injected code
```bash
vol -f memory.raw windows.malfind
```
`malfind` flags private, executable, RWX regions with no backing file — classic injection.
Note the PID and base address for dumping.
### Step 3: Review network connections
```bash
vol -f memory.raw windows.netscan
```
Correlate listening/established connections with suspicious PIDs and the C2 endpoints from
other analysis.
### Step 4: Check modules, handles, and persistence
Examine loaded DLLs (`windows.dlllist`), services, and registry (`windows.registry.*`) for
persistence and unexpected modules.
### Step 5: Dump artifacts
Dump the suspicious process or injected region for static/RE analysis:
```bash
vol -f memory.raw windows.dumpfiles --pid <pid>
```
The helper script parses Volatility's JSON renderer output to highlight injection candidates.
```bash
vol -f memory.raw -r json windows.malfind | python scripts/analyst.py malfind -
```
## Validation
- Injection candidates from `malfind` correspond to anomalous processes from `pstree`.
- Network connections map to known C2 or the sample's extracted config.
- Dumped regions disassemble into meaningful code, not random bytes.
## Pitfalls
- Wrong symbols/profile producing empty or garbage results — confirm OS build first.
- Treating every RWX region as malicious; some legitimate JIT engines use RWX. Corroborate.
- Forgetting to hash and preserve the original image before analysis.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the malfind output
parser.
- Volatility 3 documentation and memory-forensics concepts (linked in frontmatter).

View File

@@ -0,0 +1,40 @@
# API Reference: Analyzing Malware in Memory with Volatility 3
## Overview
`scripts/analyst.py` consumes Volatility 3 `windows.malfind` JSON output and ranks memory
regions by injection signal (RWX protection, embedded MZ header, executable prologue). It
analyzes plugin output, not raw memory.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | JSON parsing |
| volatility3 | >= 2.5 | Produces the memory-plugin output this tool consumes |
## External tools
| Tool | Purpose |
|------|---------|
| Volatility 3 | Memory image analysis (pslist, pstree, malfind, netscan, dumpfiles) |
| WinPmem / LiME | Memory acquisition |
## Core functions
### `rank_malfind(doc)`
Parses malfind rows and scores each by injection indicators, returning a sorted candidate
list with PID, process, address, and protection.
## Usage
```bash
pip install volatility3
vol -f memory.raw -r json windows.malfind | python scripts/analyst.py malfind -
```
## Sources
- Volatility 3 documentation — https://volatility3.readthedocs.io/
- The Art of Memory Forensics — concepts of injection detection (malfind/RWX)
- Volatility 3 JSON renderer output structure (project docs)

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Parse Volatility 3 JSON output to highlight likely process-injection candidates.
Volatility 3 can render plugin output as JSON (``vol -r json ...``). This tool consumes a
``windows.malfind`` JSON document (from a file or stdin) and ranks regions by injection
signal: RWX protection and the presence of an MZ header or executable prologue bytes.
It analyzes plugin output only; it does not parse raw memory itself.
"""
from __future__ import annotations
import argparse
import json
import sys
def _rows(doc) -> list[dict]:
# Volatility 3 JSON renderer emits a list of row dicts; tolerate a wrapped form.
if isinstance(doc, list):
return doc
if isinstance(doc, dict):
for key in ("rows", "data"):
if isinstance(doc.get(key), list):
return doc[key]
return []
def rank_malfind(doc) -> list[dict]:
out = []
for row in _rows(doc):
prot = str(row.get("Protection", row.get("protection", ""))).upper()
disasm = str(row.get("Disasm", row.get("disasm", "")))
hexdump = str(row.get("Hexdump", row.get("hexdump", "")))
score = 0
if "EXECUTE_READWRITE" in prot or "RWX" in prot:
score += 3
if "4D 5A" in hexdump or hexdump.lstrip().startswith("MZ"):
score += 3 # PE header in an injected region
if any(op in disasm for op in ("push", "mov", "call", "jmp")):
score += 1
out.append({
"pid": row.get("PID", row.get("pid")),
"process": row.get("Process", row.get("process")),
"address": row.get("Start VPN", row.get("Address", row.get("address"))),
"protection": prot,
"injection_score": score,
})
return sorted(out, key=lambda r: r["injection_score"], reverse=True)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Rank Volatility malfind output")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("malfind")
p.add_argument("json", help="path to malfind JSON, or - for stdin")
args = parser.parse_args(argv)
text = sys.stdin.read() if args.json == "-" else open(args.json, encoding="utf-8").read()
doc = json.loads(text)
print(json.dumps(rank_malfind(doc), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,93 @@
---
name: analyzing-pe-imports-and-exports
description: 'Analyzes a PE file''s import and export tables to infer capability: mapping imported
APIs to behaviors (networking, crypto, injection, persistence), spotting dynamic-resolution
stubs, and reading exports of malicious DLLs. Activates for requests to analyze PE imports,
inspect the IAT/exports, or infer capability from API usage.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- pe
- imports
- exports
- capability
- static-analysis
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1106
- T1027
d3fend:
- D3-FCR
- D3-DA
references:
- 'Microsoft PE/COFF specification — https://learn.microsoft.com/windows/win32/debug/pe-format'
- 'pefile library — https://github.com/erocarrera/pefile'
---
# Analyzing PE Imports and Exports
## When to Use
- You have a Windows PE and want a fast capability read from its imported APIs.
- You need to identify suspicious API clusters (injection, crypto, anti-analysis) before deeper
reversing.
- You are inspecting a malicious DLL's exports to find its callable entry points.
**Do not use** the import table alone as a verdict — packers stub imports and malware resolves
APIs dynamically; a thin IAT can hide real capability.
## Prerequisites
- `pefile` (`pip install pefile`); the sample handled inertly in the lab.
## Safety & Handling
- Parse the PE statically; never run it during import analysis.
- Keep the sample password-protected at rest and reference it by hash.
## Workflow
### Step 1: Parse imports
Enumerate imported DLLs and functions from the import directory (IAT). A sparse IAT plus a
`GetProcAddress`/`LoadLibrary` pair suggests dynamic resolution.
```bash
python scripts/analyst.py imports sample.exe
```
### Step 2: Map APIs to behaviors
Cluster imports into behavior buckets: networking (`Ws2_32`, `WinINet`), process/injection
(`VirtualAllocEx`, `WriteProcessMemory`, `CreateRemoteThread`), crypto (`Crypt*`, `bcrypt`),
persistence (`Reg*`, service APIs), and anti-analysis (`IsDebuggerPresent`).
### Step 3: Inspect exports
For DLLs, list exports (by name and ordinal). Unusual export names or a single exported
`DllRegisterServer`/ordinal hint at how the module is invoked.
### Step 4: Flag dynamic resolution
Note `LoadLibrary`/`GetProcAddress`, ordinal-only imports, and tiny IATs as signs that the real
capability is resolved at runtime — escalate to dynamic/RE workflows.
## Validation
- Import behavior buckets are consistent with other static signals (strings, sections).
- A thin or stubbed IAT is recognized as a packing/dynamic-resolution indicator, not "benign".
- Exports of a malicious DLL are enumerated with their invocation method identified.
## Pitfalls
- Trusting a small IAT as low-capability when imports are resolved dynamically.
- Ignoring delay-load and bound imports.
- Reading ordinal-only imports without resolving them to function names.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the import/export parser.
- Microsoft PE/COFF spec and pefile (linked in frontmatter).

View File

@@ -0,0 +1,36 @@
# API Reference: Analyzing PE Imports and Exports
## Overview
`scripts/analyst.py` parses a PE's import and export tables with `pefile`, buckets imported APIs
into behavior categories (networking, injection, crypto, persistence, anti-analysis,
dynamic-resolution), and flags a thin IAT. It parses statically and never executes the sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| pefile | >= 2023.2.7 | PE import/export directory parsing |
## Core functions
### `analyze(path)`
Returns imported DLLs/functions, exports, capability buckets, and a `thin_iat` flag.
### `_bucketize(funcs)`
Maps API names to behavior categories via substring matching.
## Usage
```bash
pip install pefile
python scripts/analyst.py imports sample.exe
```
## Sources
- Microsoft PE/COFF specification (import/export directories) —
https://learn.microsoft.com/windows/win32/debug/pe-format
- pefile library — https://github.com/erocarrera/pefile
- API-to-behavior mapping (e.g., VirtualAllocEx+WriteProcessMemory+CreateRemoteThread =
injection), established static-analysis practice

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""Parse a PE file's imports/exports and bucket APIs into capability categories.
Uses ``pefile`` to enumerate imported DLLs/functions and exports, then maps imported API names to
behavior buckets (networking, injection, crypto, persistence, anti-analysis) and flags
dynamic-resolution indicators. It parses the PE statically and never executes it.
"""
from __future__ import annotations
import argparse
import json
import sys
BEHAVIOR = {
"networking": ["wsastartup", "connect", "send", "recv", "internetopen", "httpopenrequest",
"winhttpopen", "urldownloadtofile"],
"injection": ["virtualallocex", "writeprocessmemory", "createremotethread",
"ntunmapviewofsection", "queueuserapc", "setthreadcontext"],
"crypto": ["cryptencrypt", "cryptdecrypt", "cryptacquirecontext", "bcryptencrypt",
"cryptgenkey"],
"persistence": ["regsetvalue", "regcreatekey", "createservice", "openscmanager",
"schedule"],
"anti_analysis": ["isdebuggerpresent", "checkremotedebuggerpresent",
"ntqueryinformationprocess", "outputdebugstring"],
"dynamic_resolution": ["loadlibrary", "getprocaddress", "ldrloaddll"],
}
def _bucketize(funcs: list[str]) -> dict:
lowered = [f.lower() for f in funcs]
out = {}
for bucket, needles in BEHAVIOR.items():
hits = sorted({f for f in lowered if any(n in f for n in needles)})
if hits:
out[bucket] = hits
return out
def analyze(path: str) -> dict:
import pefile # type: ignore
pe = pefile.PE(path)
imports = {}
all_funcs = []
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
for entry in pe.DIRECTORY_ENTRY_IMPORT:
dll = entry.dll.decode("latin-1") if entry.dll else "?"
funcs = []
for imp in entry.imports:
name = imp.name.decode("latin-1") if imp.name else f"ordinal_{imp.ordinal}"
funcs.append(name)
imports[dll] = funcs
all_funcs.extend(funcs)
exports = []
if hasattr(pe, "DIRECTORY_ENTRY_EXPORT"):
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
exports.append({"name": exp.name.decode("latin-1") if exp.name else None,
"ordinal": exp.ordinal})
return {
"imported_dlls": len(imports),
"imported_functions": len(all_funcs),
"imports": imports,
"exports": exports,
"capabilities": _bucketize(all_funcs),
"thin_iat": len(all_funcs) < 10,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze PE imports/exports")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("imports")
p.add_argument("file")
args = parser.parse_args(argv)
try:
print(json.dumps(analyze(args.file), indent=2))
except ImportError:
print("missing dependency: pefile (pip install pefile)", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,88 @@
---
name: analyzing-position-independent-code
description: 'Analyzes position-independent code and shellcode by identifying GetPC/PEB-walk
techniques, API-hash resolution loops, and relocation-free addressing so the payload can be
disassembled at the right base. Activates for requests to analyze position-independent code,
identify PIC techniques, or understand how shellcode resolves APIs without imports.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- position-independent-code
- shellcode
- peb-walk
- api-hashing
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1027.007
- T1620
d3fend:
- D3-SDA
- D3-DA
references:
- 'PEB / LDR structures (Windows internals) — https://learn.microsoft.com/windows/win32/api/winternl/'
- 'MITRE ATT&CK T1027.007 Dynamic API Resolution — https://attack.mitre.org/techniques/T1027/007/'
---
# Analyzing Position-Independent Code
## When to Use
- You have a PIC blob/shellcode that resolves its own addresses and APIs without imports, and you
need to understand its technique before disassembly.
- You want to identify the base-resolution (GetPC), PEB-walk, and API-hashing mechanics.
**Do not use** this to execute the code — analyze it statically (and emulate separately if needed).
Treat the blob as malicious.
## Prerequisites
- The PIC/shellcode blob (read inertly).
## Safety & Handling
- Read bytes statically; store the blob password-protected and never execute it.
## Workflow
### Step 1: Identify PIC techniques
```bash
python scripts/analyst.py analyze shellcode.bin
```
Detects GetPC stubs (`call $+5`/`fnstenv`), PEB access (`fs:[30]`/`gs:[60]`), API-hash loops
(`lodsb` + `ror`), and stack-string construction.
### Step 2: Determine architecture and base behavior
Confirm 32- vs 64-bit PEB access and how the code computes its own base for relative addressing.
### Step 3: Map API resolution
Note the hashing algorithm and that resolved APIs are obtained by walking the PEB→LDR→export tables
(pair with the API-hash resolver skill).
### Step 4: Disassemble at the right base
Load the blob at the inferred base/entry in a disassembler/emulator and proceed.
## Validation
- PIC technique hits reference real patterns (GetPC/PEB/API-hash), not arbitrary bytes.
- Architecture is consistent with the PEB-access form detected.
- The analysis points to a concrete entry/base for disassembly.
## Pitfalls
- Encoded/staged shellcode where the PIC patterns appear only after decoding.
- Mixed 32/64-bit (WoW64) transitions.
- Custom GetPC variants not matching common signatures.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the analyzer.
- PEB/LDR and ATT&CK T1027.007 references (linked in frontmatter).

View File

@@ -0,0 +1,37 @@
# API Reference: Analyzing Position-Independent Code
## Overview
`scripts/analyst.py` identifies position-independent code techniques in a shellcode blob — GetPC
stubs, PEB access (x86/x64), API-hash loops, and stack-string construction — and infers the
architecture from the PEB-access form. It reads bytes statically and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | JSON, file IO |
## Core functions
### `analyze(path)`
Returns detected techniques, inferred architecture, PEB-walk/API-hashing flags, and a PIC flag.
## Techniques detected
- GetPC: `call $+5` (`E8 00 00 00 00`), `fldz; fnstenv`.
- PEB: `fs:[30]` (x86), `gs:[60]` (x64).
- API hashing: `lodsb` + `ror 13`.
- Stack strings: `mov [esp+x], imm`.
## Usage
```bash
python scripts/analyst.py analyze shellcode.bin
```
## Sources
- PEB/LDR structures (winternl.h) — https://learn.microsoft.com/windows/win32/api/winternl/
- MITRE ATT&CK T1027.007 Dynamic API Resolution — https://attack.mitre.org/techniques/T1027/007/
- GetPC / PEB-walk / ror13 patterns from public shellcode research

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Identify position-independent code techniques in a shellcode blob.
Scans for GetPC stubs, PEB access (x86/x64), API-hash loops, and stack-string construction, and
infers architecture from the PEB-access form. Reads bytes statically; executes nothing.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
TECHNIQUES = {
"getpc_call_pop": [b"\xe8\x00\x00\x00\x00"], # call $+5
"getpc_fnstenv": [b"\xd9\xee\xd9\x74\x24\xf4"], # fldz; fnstenv [esp-0xc]
"peb_access_x86": [b"\x64\xa1\x30\x00\x00\x00", b"\x64\x8b\x0d\x30", b"\x64\x8b\x15\x30"],
"peb_access_x64": [b"\x65\x48\x8b", b"\x65\x4c\x8b"], # gs:[..]
"api_hash_ror13": [b"\xac", b"\xc1\xcf\x0d", b"\xc1\xca\x0d"], # lodsb; ror reg,13
"stack_strings": [b"\xc6\x44\x24", b"\xc7\x44\x24"], # mov byte/dword [esp+x], imm
}
def analyze(path: str) -> dict:
data = Path(path).read_bytes()
found = {name: True for name, sigs in TECHNIQUES.items()
if any(s in data for s in sigs)}
arch = ("x64" if "peb_access_x64" in found
else "x86" if "peb_access_x86" in found else "unknown")
return {
"size": len(data),
"techniques": sorted(found.keys()),
"inferred_arch": arch,
"uses_peb_walk": "peb_access_x86" in found or "peb_access_x64" in found,
"uses_api_hashing": "api_hash_ror13" in found,
"is_position_independent": bool(found),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Analyze position-independent code")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("analyze")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,113 @@
---
name: analyzing-ransomware-encryption-behavior
description: 'Analyzes how a ransomware sample encrypts files: identifying the crypto scheme
(symmetric/asymmetric/hybrid), key handling, file targeting and extension/marker changes,
shadow-copy deletion, and ransom-note artifacts. Activates for requests to analyze
ransomware encryption, assess decryptability, or study ransomware behavior.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware
- ransomware
- cryptography
- encryption
- file-marker
- impact
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1486
- T1490
- T1083
- T1027
d3fend:
- D3-FA
- D3-DA
references:
- 'No More Ransom project — https://www.nomoreransom.org/'
- 'NIST cryptographic standards (AES/RSA) — https://csrc.nist.gov/'
---
# Analyzing Ransomware Encryption Behavior
## When to Use
- You need to understand how a ransomware sample encrypts data and whether recovery without
the key is feasible.
- You are documenting file targeting, markers, and the ransom workflow for an IR report.
- You want to identify the crypto primitives to assess decryptability and detection points.
**Do not use** this to attempt key recovery on production data without backups; analyze in the
lab on disposable test files only.
## Prerequisites
- Isolated lab with disposable test files of varied types/sizes.
- Static/RE tools (see the Ghidra and crypto-identification skills) and the dynamic-analysis
setup.
- A clean snapshot to revert.
## Safety & Handling
- Detonate only against **throwaway** files in the isolated victim VM; never real data.
- Treat the sample and any dropped components as live; revert after each run.
## Workflow
### Step 1: Identify the crypto scheme statically
Locate crypto constants and APIs to classify the scheme:
```text
Symmetric only (AES/ChaCha/RC4) -> key may be derivable/recoverable if mishandled
Asymmetric (RSA/ECC) wrapping -> per-file symmetric key wrapped with attacker pubkey
Hybrid (typical) -> AES per file, RSA-wrapped key in the encrypted file/footer
```
Look for AES S-boxes, ChaCha constants ("expand 32-byte k"), and `CryptImportKey`/`BCrypt*`.
### Step 2: Trace key generation and handling
Determine where the symmetric key comes from (CSPRNG vs weak source), whether it is stored,
and how it is protected. Weak/reused keys or keys left in memory are recovery opportunities.
### Step 3: Map file targeting
Identify which directories/extensions are targeted or skipped (allowlist of system paths),
size thresholds, and partial-vs-full encryption (many encrypt only the first N bytes/blocks
for speed).
### Step 4: Capture markers and metadata
Most families append an extension, write a per-file footer/magic marker, and store the
wrapped key or IV there. The script fingerprints before/after test files to extract markers.
```bash
python scripts/analyst.py compare plain/ encrypted/
```
### Step 5: Document recovery-relevant facts
Note shadow-copy/backup deletion (`vssadmin delete shadows`), the ransom note, and any
cryptographic mistakes that affect decryptability.
## Validation
- The identified scheme matches observed file changes (e.g. hybrid → high-entropy body + a
fixed-size footer).
- File markers/extensions are consistent across encrypted test files.
- Shadow-copy deletion and note-drop are confirmed dynamically.
## Pitfalls
- Assuming decryptability from the algorithm name; correct AES/RSA usage is not recoverable.
Recovery hinges on implementation flaws or leaked keys.
- Missing partial-encryption schemes, leading to wrong impact assessment.
- Running against real files instead of disposable test data.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the file-marker
comparison tool.
- No More Ransom and NIST crypto standards (linked in frontmatter).

View File

@@ -0,0 +1,41 @@
# API Reference: Analyzing Ransomware Encryption Behavior
## Overview
`scripts/analyst.py` compares original test files against their encrypted counterparts to
extract the appended extension, measure entropy change, and recover a common trailing footer
(where families often store a wrapped key or IV). It runs on disposable test files only.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | Entropy, file IO |
## External tools
| Tool | Purpose |
|------|---------|
| Ghidra / radare2 | Identify crypto routines and key handling |
| Detect-It-Easy / capa | Crypto-constant detection |
## Core functions
### `compare(plain_dir, enc_dir)`
Returns added extensions, a common encrypted footer, per-file entropy, size deltas, and an
encryption-coverage estimate (full vs partial).
### `_common_suffix(blobs)`
Finds the longest shared trailing bytes across encrypted files (candidate footer/marker).
## Usage
```bash
python scripts/analyst.py compare ./plain ./encrypted
```
## Sources
- No More Ransom project — https://www.nomoreransom.org/
- NIST cryptographic standards (AES, RSA) — https://csrc.nist.gov/
- Hybrid-encryption ransomware design (per-file AES + RSA-wrapped key), widely documented

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Compare plaintext vs ransomware-encrypted test files to extract markers and assess scheme.
Given a directory of original test files and a directory of their encrypted counterparts, it
reports added extensions, entropy change, and any common trailing footer/magic (often where a
wrapped key or IV is stored). Operates on inert test files only.
"""
from __future__ import annotations
import argparse
import json
import math
import sys
from collections import Counter
from pathlib import Path
def entropy(data: bytes) -> float:
if not data:
return 0.0
counts = Counter(data)
n = len(data)
return -sum((c / n) * math.log2(c / n) for c in counts.values())
def _common_suffix(blobs: list[bytes], max_len: int = 512) -> bytes:
if not blobs:
return b""
shortest = min(len(b) for b in blobs)
limit = min(shortest, max_len)
suffix = bytearray()
for i in range(1, limit + 1):
col = {b[-i] for b in blobs}
if len(col) == 1:
suffix.insert(0, col.pop())
else:
break
return bytes(suffix)
def compare(plain_dir: str, enc_dir: str) -> dict:
plain = {p.stem: p for p in Path(plain_dir).iterdir() if p.is_file()}
enc = [p for p in Path(enc_dir).iterdir() if p.is_file()]
added_ext = sorted({"".join(p.suffixes) for p in enc})
enc_blobs = [p.read_bytes() for p in enc]
footer = _common_suffix(enc_blobs)
per_file = []
for p in enc:
base = p.name
# strip a possible appended extension to match the plaintext stem
stem = p.stem
orig = plain.get(stem)
row = {
"encrypted": base,
"encrypted_entropy": round(entropy(p.read_bytes()), 3),
}
if orig:
row["original"] = orig.name
row["original_entropy"] = round(entropy(orig.read_bytes()), 3)
row["size_delta"] = p.stat().st_size - orig.stat().st_size
per_file.append(row)
likely_full = all(r.get("encrypted_entropy", 0) > 7.5 for r in per_file)
return {
"added_extensions": added_ext,
"common_footer_len": len(footer),
"common_footer_hex": footer.hex(),
"encryption_coverage": "full/high-entropy" if likely_full else "partial/mixed",
"files": per_file,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Compare plain vs encrypted test files")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("compare")
p.add_argument("plain_dir")
p.add_argument("enc_dir")
args = parser.parse_args(argv)
print(json.dumps(compare(args.plain_dir, args.enc_dir), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -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

View File

@@ -0,0 +1,89 @@
---
name: analyzing-rat-command-and-control
description: 'Analyzes remote access trojan command-and-control by mapping the RAT command set,
identifying the C2 transport and beacon parameters, and cataloging operator capabilities from
static strings and config. Activates for requests to analyze RAT C2, map a trojan command
handler, or identify remote-access capabilities and C2 transport.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- rat
- command-and-control
- c2
- capability-mapping
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1219
- T1071
- T1105
d3fend:
- D3-NTA
- D3-SDA
references:
- 'MITRE ATT&CK T1219 Remote Access Software — https://attack.mitre.org/techniques/T1219/'
- 'MITRE ATT&CK T1071 Application Layer Protocol — https://attack.mitre.org/techniques/T1071/'
---
# Analyzing RAT Command and Control
## When to Use
- You have a RAT sample and need to map its command set (shell, file ops, screenshot, keylog,
webcam, proxy), C2 transport, and beacon parameters.
- You are cataloging operator capabilities for detection and impact assessment.
**Do not use** this to operate the RAT or connect to its C2 — characterize capabilities from inert
static analysis.
## Prerequisites
- The RAT sample (read inertly), optionally with an extracted config.
## Safety & Handling
- Read bytes statically; defang C2 endpoints; never connect to the C2.
## Workflow
### Step 1: Map command handlers and capabilities
```bash
python scripts/analyst.py profile sample.bin
```
Matches command-keyword and API patterns for capability classes: remote shell, file transfer,
screenshot/keylog/webcam, persistence, proxy/relay, and self-update.
### Step 2: Identify the C2 transport
Detect HTTP(S), raw TCP, TLS, DNS, or messaging-platform transport from imports/strings, plus
beacon intervals/user-agents where present.
### Step 3: Build the capability matrix
Summarize capabilities and transport, mapping to ATT&CK.
### Step 4: Defang and report
Defang endpoints and produce IOCs.
## Validation
- Capabilities are grouped into classes with the matched evidence.
- The C2 transport is identified from concrete imports/strings.
- Findings map to ATT&CK T1219/T1071.
## Pitfalls
- Generic API presence (e.g., `gdi32`) misread as screenshot capability — corroborate with command
handlers.
- Commodity RAT builders sharing strings across families — avoid over-attribution.
- Encrypted command sets requiring config decryption first.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the profiler.
- ATT&CK T1219 and T1071 (linked in frontmatter).

View File

@@ -0,0 +1,37 @@
# API Reference: Analyzing RAT Command and Control
## Overview
`scripts/analyst.py` extracts strings from a RAT and maps them to capability classes (remote shell,
file ops, surveillance, persistence, proxy/relay, self-update, credential theft) and C2 transports
(HTTP, raw TCP, TLS, DNS, messaging), defanging C2 URLs. It reads bytes statically and executes
nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `profile(path)`
Returns grouped capabilities, C2 transports, defanged C2 URLs, and a RAT-likelihood flag
(≥3 capability classes).
## Capability classes
remote_shell, file_operations, surveillance (screenshot/keylog/webcam/audio), persistence,
proxy_relay, self_update, credential_theft.
## Usage
```bash
python scripts/analyst.py profile sample.bin
```
## Sources
- MITRE ATT&CK T1219 Remote Access Software — https://attack.mitre.org/techniques/T1219/
- MITRE ATT&CK T1071 Application Layer Protocol — https://attack.mitre.org/techniques/T1071/
- RAT capability/command-handler patterns from public family analyses

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Profile a RAT's capabilities and C2 transport from static strings.
Extracts ASCII/UTF-16LE strings and maps them to capability classes (remote shell, file ops,
surveillance, persistence, proxy, self-update) and C2 transports (HTTP, TCP, TLS, DNS, messaging).
Reads bytes statically, defangs endpoints, and executes nothing.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ASCII_RE = re.compile(rb"[\x20-\x7e]{4,}")
UTF16_RE = re.compile(rb"(?:[\x20-\x7e]\x00){4,}")
RE_URL = re.compile(r"https?://[^\s\"'<>]+", re.I)
CAPABILITIES = {
"remote_shell": ["cmd.exe", "/c ", "createprocess", "shell", "winexec", "system32\\cmd"],
"file_operations": ["upload", "download", "getfile", "putfile", "createfile", "deletefile",
"findfirstfile"],
"surveillance": ["screenshot", "bitblt", "getdc", "keylog", "getasynckeystate",
"setwindowshook", "webcam", "capgetdriverdescription", "waveinopen"],
"persistence": ["currentversion\\run", "schtasks", "createservice", "startup"],
"proxy_relay": ["socks", "proxy", "relay", "portforward", "reverse"],
"self_update": ["update", "selfdelete", "rename", "newversion"],
"credential_theft": ["cryptunprotectdata", "vault", "password", "credential"],
}
TRANSPORTS = {
"http": ["wininet", "winhttp", "httpsendrequest", "internetopen", "user-agent"],
"tcp_raw": ["ws2_32", "wsasocket", "connect", "recv", "send"],
"tls": ["schannel", "ssl_", "tls", "openssl"],
"dns": ["dnsquery", "dns_", "txt record"],
"messaging": ["api.telegram.org", "discord", "pastebin", "steamcommunity"],
}
def _strings(data: bytes) -> str:
parts = [m.decode("ascii", "ignore") for m in ASCII_RE.findall(data)]
parts += [m.decode("utf-16-le", "ignore") for m in UTF16_RE.findall(data)]
return "\n".join(parts)
def _defang(s: str) -> str:
return s.replace("http", "hxxp").replace(".", "[.]")
def profile(path: str) -> dict:
blob = _strings(Path(path).read_bytes())
low = blob.lower()
caps = {c: sorted({n for n in needles if n in low})
for c, needles in CAPABILITIES.items()}
caps = {c: hits for c, hits in caps.items() if hits}
transports = {t: sorted({n for n in needles if n in low})
for t, needles in TRANSPORTS.items()}
transports = {t: hits for t, hits in transports.items() if hits}
urls = sorted({_defang(u) for u in RE_URL.findall(blob)})[:50]
return {
"capabilities": caps,
"c2_transports": transports,
"c2_urls": urls,
"capability_count": len(caps),
"is_likely_rat": len(caps) >= 3,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Profile RAT C2/capabilities")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("profile")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(profile(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())

Some files were not shown because too many files have changed in this diff Show More