TLS Handshake Fields as a Covert Channel
The TLS handshake happens before encryption starts. Andreea Iacob built a C2 channel entirely inside those negotiation packets. The client hello carries one byte via GREASE fields, 32 bytes via the random field, and 32 more via session ID. The server hello sends commands back through its own 32-byte random field. No payload traffic, no post-encryption data — just the handshake itself, doing double duty.
Why GREASE, Random, and Session ID — Field Selection Rationale
GREASE came first, from a CTF. Caleb U. hid data in GREASE fields; it took Iacob three weeks in a packet capture to spot it. That answer prompted a question: GREASE carries one byte, split across two cipher-suite slots. But random is 32 bytes. Session ID is another 32. Could those carry more?
They could. The client hello sends one byte through GREASE and 32 bytes each through random and session ID. GREASE requires splitting the byte — the letter “a” (0x61) becomes 0x6A and 0x1A across two fields, with a third field set randomly as cover. The server hello sends commands back in its own 32-byte random field. Six fields, 65 bytes total per handshake, all in plaintext before encryption starts.
Patching OpenSSL to Inject Commands via Server Random
The patch adds a flag to OpenSSL’s S3 connection struct. When set, one function skips RAND_bytes and writes command bytes into server random instead. The client reads the first 16 bytes: 0xFD means no command, anything else goes to system(). A mutex resets the flag immediately after, so a long output doesn’t replay the same command across thousands of beacon responses. Iacob’s first approach, adding an external field to the struct, failed because the handshake resets that memory at multiple points. Then she found the deeper wall: server random feeds key derivation.
“TLDDR, don’t over complicate things and don’t face the final boss when you’re just getting started.” — Andreea Iacob
The flag approach routes around the crypto library entirely.
Live C2 Demo: Command Execution Over TLS Handshakes
In the C2 command execution demo — ipconfig and net localgroup (17:45), Iacob runs a Debian attacker server and a Windows client. She queues ipconfig on the server, starts the client, and within 3 seconds the beacon fires, reads the command from the server hello random, runs it, and ships the output back through the client hello’s GREASE and session ID fields. She then queues net localgroup administrators and the cycle repeats. Wireshark shows the output reassembling byte-by-byte across successive client hellos — plaintext, no encryption, deliberately so the wire traffic stays readable during the demo.
Detections and Mitigations: TLS Inspection, Suricata, Zeek, RITA
“There’s no such thing as unhackable and there’s no such thing as undetectable. If you hear anybody say unhackable or undetectable, they’re either oblivious or just straight up deceitful.” — Andreea Iacob
TLS inspection kills the channel outright. The SSL Split proxy breaks the C2 channel (22:41) by generating fresh handshake randoms — the client never receives a command. Suricata rules matching the first four FC bytes fire on this implementation, but one byte change breaks them. Zeek logs no random and no session ID, only the agreed cipher. The real signal is volume: the Zeek 14-hour beacon count anomaly (26:18) shows thousands of full TLS connections to one host, detectable without decryption. RITA’s show strobes, tuned to the connection count, is the right call.
Q&A
Are specific TLS versions more vulnerable or resistant? No specific version is vulnerable — the technique patches OpenSSL itself (tested on 1.1.1 and 3.5), so any version could be patched the same way. ▶ 29:08
Do you have any other protocol in mind for a future C2 project? She’s deciding between DNS (complex, well-trodden) and QUIC (newer, less explored), with QUIC the likely next target. ▶ 30:09
Did you think about encrypting the bytes that you send? Yes, encoding before send is straightforward, but the beaconing behavior — hundreds of one-byte connections opening and closing every 3 seconds — remains detectable regardless. ▶ 31:11
Notable Quotes
there’s no such thing as unhackable and there’s no such thing as undetectable, right? If you hear anybody say unhackable or undetectable, they’re either uh oblivious or just straight up deceitful Andreea Iacob · ▶ 21:14
TLDDR, don’t over complicate things and don’t face the final boss when you’re just getting started. Andreea Iacob · ▶ 12:21
the wire the great equalizer there is nothing that escapes the wire there’s nothing that escapes communication. Andreea Iacob · ▶ 27:34
you can just make byes do anything Andreea Iacob · ▶ 29:51
Key Takeaways
- Tunnel C2 commands through TLS handshake fields — GREASE, server random, and session ID — before encryption begins.
- Patch OpenSSL directly to control handshake bytes; the crypto library is the final boss, route around it.
- Monitor connection count and beacon cadence on the wire — behavior leaks even when payload bytes are encrypted or randomized.
About the Speaker
Andreea Iacob
After a 1 year stint as a pentester, I found myself drawn to research and probing network protocols. Following my TLS exfil research presented at BSides Bucharest, I’m back with a (very) low throughput TLS C2. Aside from the depths of troubleshooting hell, I like to spend my time volunteering in the community and staffing the Security Summer School at UPB.