Overview
| Field | Value |
|---|---|
| Category | Intermediate |
| Binary | ELF 64-bit x86-64, FreeBSD 14.3, Go |
| Description | “This file was found on an infected host. Can you figure out what it does?” |
| Flag | CMO{fUn_w1th_m4g1c_p4ck3t5} |
Recon
The binary is a Go program compiled for FreeBSD with debug info and symbols. Key dependencies:
net/http– serves an HTTP endpoint on:8080github.com/google/gopacket/pcap– live packet capturecrypto/aes,crypto/cipher– AES-CBC decryption
Only two custom functions exist: main.main and main.handler.
Program Flow
main.main (0x747DC0)
- Prints a startup message
- Spawns a goroutine (
net_http.init_0at0x748080) – the real payload - Registers an HTTP handler on the default ServeMux
- Starts an HTTP server on
:8080as a cover
main.handler (0x747FA0)
Simple HTTP handler – responds to GET with “Nothing to see here :{” and rejects other methods. This is the innocuous-looking front.
Packet Sniffer Goroutine (0x748080)
This is the malicious payload:
- Opens pcap on interface
re0(FreeBSD Ethernet) with snaplen 1600 - Sets BPF filter to
"icmp"– only captures ICMP traffic - Creates a PacketSource and receives packets from a channel
- For each packet, calls
packet.Data()to get raw Ethernet frame bytes
Packet Filtering
The goroutine checks for a very specific ICMP Echo Request (magic packet):
| Offset | Field | Required Value |
|---|---|---|
| 0x10 | IP Total Length (big-endian) | 32 (0x0020) |
| 0x14 | IP Flags/FragOff + TTL + Proto | Variable (Protocol must be 1) |
| 0x22 | ICMP Type | 8 (Echo Request) |
| 0x24 | ICMP Checksum | Computed from message |
| 0x26 | ICMP Identifier (LE word) | 0x1337 |
| 0x2A | ICMP Data (LE dword) | 0xE55FDEC6 |
The offsets are relative to the raw Ethernet frame (14-byte Ethernet header + 20-byte IP header + ICMP).
AES Key Derivation
When a matching magic packet is found, a 16-byte AES key is constructed from packet fields:
key[0] = 0xE5 ^ checksum_lo_byte
key[1] = 0x5F ^ checksum_hi_byte
key[2] = IP_flags_hi_byte
key[3] = IP_flags_lo_byte
key[4] = TTL
key[5] = 0x01 (protocol)
key[6] = checksum_hi_byte
key[7] = checksum_lo_byte
key[8] = 0xC6 (magic byte 0)
key[9] = 0xDE (magic byte 1)
key[10] = 0x5F (magic byte 2)
key[11] = 0xE5 (magic byte 3)
key[12] = 0x37 (identifier byte 0)
key[13] = 0x13 (identifier byte 1)
key[14] = checksum_lo_byte ^ 0xDE
key[15] = checksum_hi_byte ^ 0xC6
Where the checksum bytes are the raw bytes at packet offset 0x24 (big-endian ICMP checksum), and the XOR’d values are derived using ROL word, 8 (byte swap) on the XOR of magic cookie halves with the checksum.
AES-CBC Decryption
- Algorithm: AES-128-CBC
- Key: 16 bytes derived from packet fields
- IV: Same as the key (IV = key)
- Ciphertext (32 bytes, hardcoded at
0x7484D2):51 F1 A5 29 B4 DF 7E C0 2A 3B 2F 8F 24 3D 4E B3 5A ED B0 CF 0B 9C DD 8C CD E6 0E 9B 3E C4 64 0C - Result is converted to a string and printed to stdout
Solve
The key is mostly determined by the fixed magic values. The unknowns reduce to:
- ICMP Sequence Number (determines checksum)
- IP Flags/Fragment Offset (2 bytes)
- IP TTL (1 byte)
Brute-force approach: for each combination of flags, TTL, and sequence number, compute the ICMP checksum, derive the AES key, decrypt, and check for the flag.
|
|
The matching packet parameters:
- IP Flags: 0x4000 (Don’t Fragment)
- TTL: 64 (standard FreeBSD default)
- ICMP Sequence: 1
- ICMP Checksum: 0x9A27
Lessons Learned
- Go binaries with debug info provide clear function names but the decompiler output is unreliable – always verify with disassembly.
ROL word, 8followed by a little-endian memory store effectively swaps byte positions in the stored result – the low byte of the rotated value lands at the lower address.- The malware disguises itself as an HTTP server while secretly sniffing for magic ICMP packets – a classic covert channel technique.
Flag
CMO{fUn_w1th_m4g1c_p4ck3t5}