reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: reverse-engineering-custom-c2-protocols
|
||||
description: 'Reverses proprietary command-and-control protocols: locating send/recv routines,
|
||||
recovering the message framing and encryption/encoding, and reconstructing the command set
|
||||
to build a decoder or emulator. Activates for requests to reverse a custom C2 protocol,
|
||||
decode beacon traffic, or document a malware command structure.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- command-and-control
|
||||
- protocol
|
||||
- network
|
||||
- decryption
|
||||
- beacon
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1071
|
||||
- T1095
|
||||
- T1573
|
||||
d3fend:
|
||||
- D3-NTA
|
||||
- D3-DA
|
||||
references:
|
||||
- 'MITRE ATT&CK T1071 Application Layer Protocol — https://attack.mitre.org/techniques/T1071/'
|
||||
- 'MITRE ATT&CK T1573 Encrypted Channel — https://attack.mitre.org/techniques/T1573/'
|
||||
---
|
||||
|
||||
# Reverse Engineering Custom C2 Protocols
|
||||
|
||||
## When to Use
|
||||
|
||||
- A sample uses a non-standard protocol (or a custom layer over HTTP/TCP) and you must decode
|
||||
its traffic.
|
||||
- You need to recover message framing, the encryption/encoding scheme, and the command set.
|
||||
- You want to build a standalone decoder or emulator to parse captured beacon traffic.
|
||||
|
||||
**Do not use** this for documented protocols handled by existing dissectors — use the protocol
|
||||
analyzer directly instead of reversing from scratch.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A disassembler/debugger and a network capture (PCAP) of the sample's traffic where possible.
|
||||
- The crypto/obfuscation skills for recovering keys and encodings.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Locate the network routines
|
||||
|
||||
Find `send`/`recv`/`WSASend`/`WinHTTP` calls (or the framework wrappers) and the buffers they
|
||||
operate on. These bracket the serialization and crypto.
|
||||
|
||||
### Step 2: Recover framing
|
||||
|
||||
Trace how outbound buffers are built: magic bytes, length prefixes, sequence/ID fields, and
|
||||
type/opcode fields. Document the header layout.
|
||||
|
||||
```text
|
||||
[ magic(4) ][ len(4 LE) ][ opcode(1) ][ flags(1) ][ payload(len) ]
|
||||
```
|
||||
|
||||
### Step 3: Recover crypto/encoding
|
||||
|
||||
Identify the transform applied before send / after recv (XOR with embedded key, RC4, AES,
|
||||
base64). Recover keys from the binary or from the key-setup routine.
|
||||
|
||||
### Step 4: Reconstruct the command set
|
||||
|
||||
Map opcodes to handlers (shell, download, upload, sleep/jitter, exit) by following the
|
||||
dispatch switch. Build a table of command → behavior.
|
||||
|
||||
### Step 5: Build a decoder and validate
|
||||
|
||||
Implement a parser/decryptor and run it against captured traffic; confirm it yields coherent
|
||||
commands and recovers the beacon's config.
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py decode capture.bin --key 0x5a --magic 4d5a4331
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
- The decoder parses every message in a real capture without desync.
|
||||
- Decrypted payloads are coherent (printable commands / structured config).
|
||||
- The opcode table matches the dispatch logic observed in the binary.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Assuming fixed keys when the protocol negotiates a session key in the first exchange.
|
||||
- Missing a length/sequence field and desynchronizing after the first message.
|
||||
- Confusing transport (TLS) with the inner custom layer; decode the inner layer.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the framing decoder.
|
||||
- MITRE ATT&CK T1071 and T1573 (linked in frontmatter).
|
||||
@@ -0,0 +1,34 @@
|
||||
# API Reference: Reverse Engineering Custom C2 Protocols
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` is a configurable reference decoder for a length-prefixed, XOR-obfuscated
|
||||
framing (`[magic][len][opcode][flags][payload]`). Adapt the field sizes and crypto to the
|
||||
protocol you reversed. It only parses a capture file and makes no network connections.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | struct parsing, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `decode(stream, key, magic)`
|
||||
Walks the byte stream, locates framed messages by magic, parses length/opcode/flags, and
|
||||
XOR-decrypts each payload; returns parsed messages.
|
||||
|
||||
### `xor(data, key)`
|
||||
Single-byte XOR helper used as the example payload transform.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py decode capture.bin --key 0x5a --magic 4d5a4331
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- MITRE ATT&CK T1071 Application Layer Protocol — https://attack.mitre.org/techniques/T1071/
|
||||
- MITRE ATT&CK T1573 Encrypted Channel — https://attack.mitre.org/techniques/T1573/
|
||||
- Common C2 framing patterns (magic + length prefix + opcode dispatch), general RE practice
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Decode a simple length-prefixed, XOR-obfuscated C2 message stream.
|
||||
|
||||
This is a configurable reference decoder for the common framing
|
||||
``[magic][len LE][opcode][flags][payload]`` with a single-byte XOR over the payload. Adjust the
|
||||
field sizes/crypto to match the protocol you reversed. It only parses bytes from a capture
|
||||
file; it makes no network connections.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def xor(data: bytes, key: int) -> bytes:
|
||||
return bytes(b ^ key for b in data)
|
||||
|
||||
|
||||
def decode(stream: bytes, key: int, magic: bytes) -> dict:
|
||||
messages = []
|
||||
off = 0
|
||||
n = len(stream)
|
||||
while off + 10 <= n:
|
||||
if stream[off:off + len(magic)] != magic:
|
||||
off += 1
|
||||
continue
|
||||
p = off + len(magic)
|
||||
(length,) = struct.unpack_from("<I", stream, p)
|
||||
opcode = stream[p + 4]
|
||||
flags = stream[p + 5]
|
||||
start = p + 6
|
||||
end = start + length
|
||||
if end > n:
|
||||
break
|
||||
payload = xor(stream[start:end], key)
|
||||
messages.append({
|
||||
"offset": off,
|
||||
"opcode": opcode,
|
||||
"flags": flags,
|
||||
"length": length,
|
||||
"payload_preview": payload[:64].decode("latin-1"),
|
||||
})
|
||||
off = end
|
||||
return {"messages": messages, "count": len(messages)}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Decode custom C2 framing")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("decode")
|
||||
p.add_argument("file")
|
||||
p.add_argument("--key", default="0x00", help="single-byte XOR key, e.g. 0x5a")
|
||||
p.add_argument("--magic", default="", help="hex magic prefix, e.g. 4d5a4331")
|
||||
args = parser.parse_args(argv)
|
||||
stream = Path(args.file).read_bytes()
|
||||
magic = bytes.fromhex(args.magic) if args.magic else b""
|
||||
print(json.dumps(decode(stream, int(args.key, 0), magic), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user