← All writeups

Network

A FRAGILE TRUTH

Event: CDDC 2026 Category: Network / Crypto Flag: cddc2026{1_4m_n0t_r3ady_t0_f4ce_th3_truth_y3t}

Story

Headquarters has identified one of Defender's team members as a traitor. Following HQ's order ("Execute a full deletion of the suspect and the comms data, effective immediately. — Head__art__s"), Defender stopped the teammate. Growing doubts led Defender to secretly examine the last trace the teammate had left behind: a hastily sent transmission whose security may have been somewhat fragile.

We're given A FRAGILE TRUTH.pcapng containing 21 TLS sessions to port 4433 on 172.18.0.2.

Recon

Total packets : 399
TLS port      : 4433  (21 connections, 21 client/server flow pairs)
Clients       : 172.18.0.10 (×8), .11 (×8), .13 (×3), .12 (×1), .20 (×1)

Reassembling the streams and scanning every Server Hello reveals two cipher suites in use:

cipher suite seen on
0xC030 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 20 sessions
0x0035 TLS_RSA_WITH_AES_256_CBC_SHA 1 session (172.18.0.20:43311)

That one outlier is the suspect. ECDHE provides forward secrecy, but TLS_RSA does not — the client encrypts the pre-master secret directly with the server's RSA public key. Get the private key, decrypt the session.

The certificate

Extracting the server cert from the Certificate handshake message:

Subject     : CN=vulnerable.ctf, O=InternalPortal Corp, C=KR
Public-Key  : RSA, 511 bits  (!!)
Exponent    : 65537
Modulus N   : 5F61803FB6E70DB04C052A2212...EBA551E6D

A 511-bit modulus screams "factor me," but RSA-512 (≈154 digits) is still hours of CPU on commodity hardware with NFS — not a quick lookup. FactorDB has it as C154 with no factorization stored.

What does "fragile" mean here?

Brute factoring is the obvious-but-slow path. The challenge title hints at something more specific: a fragile choice of prime.

Pollard's p − 1 algorithm finds a prime factor p of N whenever p − 1 is B-smooth — i.e., all of its prime factors are ≤ B. A properly generated RSA prime should have at least one large factor in p − 1 to defeat this; the suspect, "hastily sending a transmission to evade HQ's surveillance," skipped that check.

I let yafu's auto-factor pipeline run:

ecm: 214/214 curves on C154, B1=50k        ... no factor (small ECM bucket)
pm1: starting B1 = 3750K, B2 = gmp-ecm default on C154
pm1: Process took 0.6547 seconds.
pm1: found prp77 factor = 72596913078035655369042280105449632221957231318118875016708473779461685758183

Total wall time including ECM warmup: 3.4 seconds. P − 1 is smooth at B1 = 3.75M.

p = 68811481578421750518769574540287122730773374684179588971811640397724817198987
q = 72596913078035655369042280105449632221957231318118875016708473779461685758183

Sanity check: p · q = N ✓.

Recovering the session keys

With (p, q) we have d = e⁻¹ mod φ(N).

Walking the 172.18.0.20:43311 flow:

Because the handshake negotiated Extended Master Secret (RFC 7627), the master is derived from a transcript hash, not the raw randoms:

session_hash = SHA-256( ClientHello || ServerHello || Certificate || ServerHelloDone || ClientKeyExchange )
master_secret = PRF-SHA256(pms, "extended master secret", session_hash, 48)

Then the standard key block:

key_block = PRF-SHA256(master, "key expansion",
                       server_random || client_random,
                       20+20+32+32 bytes)
                          ↑   ↑   ↑   ↑
                         CMAC SMAC CKEY SKEY

(SHA-1 MAC = 20 bytes, AES-256 key = 32 bytes; AES-CBC IV is explicit per record in TLS 1.2.)

Decrypting the records

With encrypt-then-MAC, each encrypted record is laid out as:

[ explicit IV (16) ] [ ciphertext (16·n) ] [ HMAC-SHA1 (20) ]

— and the MAC covers the encrypted body, not the plaintext. The earlier "ct length 52 not multiple of 16" failure was the giveaway: under standard MAC-then-encrypt the math doesn't work, under EtM it does (52 - 20 = 32 = 16·2).

Decrypt → strip CBC padding → server's seq=1 application-data record yields:

HTTP/1.0 200 OK
Content-Type: text/plain

TO. my dear

Hi, my dear.
What's going on at Head these days?
Lately, it feels like we're getting unreasonable orders all the time.
My colleagues are disappearing one by one, and I'm really scared.
I'm worried something might have happened to you.
If anything is wrong, please be sure to reply.
Since I'm sending this while evading HQ's surveillance,
I'm using a somewhat old-fashioned method.
I trust you'll be able to decode this message.
I look forward to seeing you again.

cddc2026{1_4m_n0t_r3ady_t0_f4ce_th3_truth_y3t}

FROM. Your dear

The client request, for completeness:

GET /flag.txt HTTP/1.0
Host: portal.internal
User-Agent: VerificationAgent/1.0

All MACs verify. ✓

Lessons / observations

  1. Cipher selection matters more than key size narrative. Twenty sessions used ECDHE and were forward-secret; one downgrade-style session used static RSA and exposed everything.
  2. "512-bit RSA is broken" usually means days of NFS — but not if p − 1 is smooth. Always pair a primality test with a Pollard p − 1 / p + 1 sanity check on your generated primes (most libraries do, ad-hoc CTF generators don't).
  3. Modern TLS 1.2 stacks negotiate EMS + EtM by default. Any TLS-decrypt script that assumes the textbook RFC 5246 layout will silently fail; check the Server Hello extensions before deriving keys.

Tooling used

Final flag

cddc2026{1_4m_n0t_r3ady_t0_f4ce_th3_truth_y3t}