Secure Steps

Cybersecurity assessments, insights, and clear remediation.

Category: ,

Censored Country Walkthrough — Smuggling Past a JA4 TLS Fingerprint Firewall

Censored Country Walkthrough — Smuggling Past a JA4 TLS Fingerprint Firewall

Platform: CyberTalents · Category: Web/Crypto · Difficulty: Hard
Goal: Get past a zero-trust gateway that only accepts one exact TLS ClientHello fingerprint.


Challenge

“Behind the Great Firewall…” — a gateway that TLS-fingerprints every client with JA4 and only lets one mysterious fingerprint through. The public instance is 54.153.42.171 (only ports 22 and 443 open).

Recon: the server leaks its own secret

The first HTTPS request — with any normal fingerprint — gets a 403 Forbidden. But look at the response headers:

X-Expected-JA4:   t13i1013h2_646c4f7e2ebe_77cf8e4d6e9f
X-Expected-JA4-R: t13i1013h2_1301,1302,1303,1304,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0015,0023,002b,002d,0033,4469,ff01_0403,0804

That’s the entire recipe, served on a platter. JA4-R is the raw fingerprint:

  • TLS 1.3 (t13)
  • no SNI — the i in t13i... means the ClientHello targets an IP with no SNI extension
  • 10 cipher suites
  • 13 extensions
  • ALPN h2
  • signature algorithms 0403, 0804

Understanding JA4

JA4 is a TLS client fingerprint. The full JA4 string is:

t13i1013h2_<b>_<c>

where the two hashed components are computed as:

b = sha256(ciphers sorted, comma-delimited hex)[:12]
c = sha256(extensions sorted (SNI/ALPN excluded), comma-delimited hex + "_" + sigalgs in order)[:12]

We verified both hashes against the leak: 646c4f7e2ebe and 77cf8e4d6e9f reproduce exactly. So the task is: send a ClientHello that produces this exact JA4.

The forge: uTLS

Normal TLS stacks won’t let you craft a ClientHello freely — Go’s crypto/tls and OpenSSL decide extensions for you. uTLS exists precisely to defeat fingerprinting: it lets you build a custom ClientHelloSpec.

The spec must contain, in the right order for hashing (order doesn’t matter for JA4 — it sorts — but it matters for real-world parsing):

  • 10 ciphers: 1301,1302,1303,1304,c02b,c02c,c02f,c030,cca8,cca9
  • 13 extensions, NO SNI: ALPN h2 (0x0010), status_request (0x0005), supported_groups x25519 (0x000a), ec_point_formats (0x000b), signature_algorithms [0403,0804] (0x000d), unknown 0x0012, padding→512 (0x0015), session_ticket (0x0023), supported_versions [1.3] (0x002b), psk_kex_modes dhe (0x002d), key_share x25519 (0x0033), unknown 0x4469, renegotiation_info (0xff01)

The two unknown extension types (0x0012, 0x4469) are emitted with GenericExtension — JA4 only hashes extension types, so their payloads don’t matter.

The pitfall that cost an hour

uTLS’s BoringPaddingStyle omits the padding extension entirely for ClientHellos under 256 bytes. Our first short hello came out with 12 extensions instead of 13 — and the fingerprint didn’t match. The fix:

&utls.UtlsPaddingExtension{GetPaddingLen: utls.AlwaysPadToLen(512)}

Force the hello to exactly 512 bytes so the padding extension (0x0015) is always present.

Result

handshake OK, proto: h2
HTTP/1.0 200 OK
FLAG{h4ndcr4ft3d_h3ll0_sl1ps_thr0ugh}

Why this worked (the real lesson)

  1. TLS fingerprints are not authentication. JA4 (and its predecessor JA3) describe how a client speaks TLS — they can be impersonated with a custom stack like uTLS. Using a fingerprint as an access-control gate is security by obscurity with a public spec.
  2. The server leaked the expected fingerprint in an error header. That turned the challenge into a hash-reproduction puzzle — fun, but a reminder that verbose 403s are free reconnaissance.
  3. The padding pitfall is generic: fingerprint-based rules that depend on extension presence are sensitive to ClientHello length heuristics in the forging library.

Defense takeaways:

  • Never use client fingerprints alone for authorization; pair with real credentials or mTLS.
  • Don’t echo expected fingerprint values in error responses.
  • If you must fingerprint, treat it as one low-trust signal among many.

Files

  • forge/main.go — the uTLS forger (working exploit)
  • ja4calc.py — pcap ClientHello parser / JA4 verifier

Writeup of a solved CyberTalents web/crypto challenge. All attack surface described here is part of the challenge’s intended vulnerable application.

en_US
Secure Steps
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.