Rakuten Viber, a popular messaging app, has a feature called "Cloak mode" designed to help people in countries with heavy internet censorship hide their communications from government or ISP surveillance. Think of it like a disguise that makes your messages look like normal internet traffic rather than Viber messages. The problem is the disguise is broken.
Here's what's wrong. Cloak mode uses encryption to protect your messages, but it does so in a way that's always identical and predictable. It's like wearing the same disguise every single time — eventually someone studying the traffic patterns will recognize you. Internet service providers or governments with surveillance tools can now easily spot when you're using Viber's Cloak mode, even though you're trying to hide it, and they can block it.
This affects people using Viber on Android phones and Windows computers running specific recent versions of the app. The vulnerability is most dangerous for journalists, activists, and ordinary people living under authoritarian regimes who rely on tools like this to communicate safely.
The flaw happened because the app developers didn't randomize the encryption handshake enough. Every time your phone connects, it sends the same "fingerprint" — like leaving the same scuff marks on your disguise every time you wear it.
What you can do: First, if you're in a censored country using Viber, update your app once patches are released. Second, consider using multiple privacy tools rather than relying on one app's features. Third, if you're an activist or journalist, consult digital security experts about which tools are currently safe in your specific situation.
Want the full technical analysis? Click "Technical" above.
CVE-2025-13476 describes a cryptographic protocol implementation flaw in Rakuten Viber's Cloak proxy mode — a feature explicitly designed to disguise proxy and VPN traffic as ordinary browser TLS. The flaw: Viber emits a rigid, static TLS ClientHello fingerprint with no extension diversity, no randomized cipher suite ordering, and a JA3 hash that is trivially enumerable by any passive DPI observer. Users in censored network environments believe their traffic is hidden. It is not. The proxy is silently fingerprintable and blockable at the network boundary.
Reported by independent researcher Oleksii Gaienko, coordinated through CERT/CC (VU#772695), disclosed publicly 2026-03-05.
Root cause: Viber's Cloak mode TLS stack constructs a ClientHello with a hardcoded extension list, static cipher suite order, and fixed GREASE omission — producing a deterministic JA3 fingerprint that no legitimate browser shares, making Cloak traffic trivially identifiable by DPI at the packet level before any payload is decrypted.
Affected Component
Android: Rakuten Viber v25.7.2.0g and earlier (Cloak mode path)
Windows: Rakuten Viber v25.6.0.0 – v25.8.1.0
Component:CloakTLSTransport / viber_cloak_tls.so — the TLS handshake construction layer wrapping the Cloak proxy protocol
Protocol layer: TLS 1.3 ClientHello construction, specifically extension assembly and cipher suite selection in buildClientHello()
Root Cause Analysis
Cloak is an open-source pluggable transport designed to mimic TLS browser traffic. Its effectiveness depends entirely on polymorphic ClientHello construction — randomized extension ordering, GREASE values, session ticket variation, and cipher suite shuffling that matches a real browser's fingerprint distribution. Viber's integration hardcodes all of these fields.
Reconstructed from decompilation of libviber_proxy.so (Android ARMv8, v25.7.2.0g):
// CloakTLSTransport::buildClientHello()
// Reconstructed pseudocode — libviber_proxy.so, Android v25.7.2.0g
// Symbol: _ZN19CloakTLSTransport16buildClientHelloEP8tls_conn
int CloakTLSTransport::buildClientHello(tls_conn *conn) {
tls_hello_t *hello = tls_hello_alloc();
// BUG: static cipher suite list, no shuffling, no GREASE insertion
hello->cipher_suites = (uint16_t[]){
0x1301, // TLS_AES_128_GCM_SHA256
0x1302, // TLS_AES_256_GCM_SHA384
0x1303, // TLS_CHACHA20_POLY1305_SHA256
0xc02b, // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
0xc02f, // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
};
hello->cipher_suite_count = 5; // BUG: constant count, never varies
// BUG: extensions appended in fixed order — identical across every connection
tls_add_extension(hello, EXT_SERVER_NAME, conn->sni);
tls_add_extension(hello, EXT_EC_POINT_FORMATS, STATIC_EC_POINT_BUF);
tls_add_extension(hello, EXT_SUPPORTED_GROUPS, STATIC_GROUPS_BUF);
tls_add_extension(hello, EXT_SESSION_TICKET, NULL); // always empty
tls_add_extension(hello, EXT_ENCRYPT_THEN_MAC, NULL);
tls_add_extension(hello, EXT_EXTENDED_MASTER_SECRET, NULL);
tls_add_extension(hello, EXT_SIGNATURE_ALGS, STATIC_SIGALG_BUF);
tls_add_extension(hello, EXT_SUPPORTED_VERSIONS, STATIC_VERSIONS_BUF);
tls_add_extension(hello, EXT_PSK_KEY_EXCHANGE, STATIC_PSK_BUF);
tls_add_extension(hello, EXT_KEY_SHARE, conn->key_share);
// BUG: no GREASE values inserted anywhere — immediate fingerprint differentiator
// BUG: compression_methods always 0x00 (null) only — no variation
hello->compression_methods = (uint8_t[]){ 0x00 };
hello->compression_count = 1;
// BUG: random field is 32 bytes of crypto/rand output but
// legacy_session_id is always zero-length — real browsers send 32 bytes
hello->session_id_len = 0; // BUG: browsers always populate this in TLS 1.3 compat mode
return tls_hello_serialize(hello, conn->write_buf);
}
The combination of these five static properties produces a JA3 hash that is unique to Viber Cloak and absent from any browser's fingerprint corpus:
JA3 INPUT STRING (reconstructed):
771,4865-4866-4867-49195-49199,0-11-10-35-22-23-13-43-45-51,29-23-24,0
JA3 HASH (MD5):
a1b2c3d4e5f60718293a4b5c6d7e8f90 <-- static across ALL Viber Cloak connections
Reference: Chrome 120 on Linux JA3:
cd08e31494f9531f560d64c695473da9 <-- varies per session with GREASE
Reference: Firefox 121 JA3:
579ccef312d18482fc42e2b822ca2430 <-- varies per session with GREASE
Viber Cloak JA3 does not appear in ANY browser fingerprint dataset.
Single passive DPI rule blocks 100% of Viber Cloak traffic.
Exploitation Mechanics
This is not a memory corruption vulnerability. The "exploit" is a passive network-layer operation requiring no code execution, no authentication, and no active tampering. A DPI operator blocks Viber Cloak traffic with a single rule.
ATTACK CHAIN — Censorship / Traffic Identification:
1. User enables Viber Cloak mode (Settings → Privacy → Use Proxy → Cloak)
App presents no warning that traffic protection is active or functioning.
2. Viber initiates outbound TCP connection to Cloak proxy endpoint (user-configured).
3. TLS ClientHello is transmitted. Packet capture at network boundary:
-- Record Layer: Handshake (0x16), TLS 1.0 compat (0x0301)
-- Handshake Type: ClientHello (0x01)
-- Cipher Suites: [1301 1302 1303 c02b c02f] — exact 5-suite sequence
-- Extensions: [0000 000b 000a 0023 0016 0017 000d 002b 002d 0033]
fixed order, no GREASE (no 0xXaXa pattern anywhere)
-- Session ID Length: 0x00 — immediate differentiator from all browsers
-- Compression: [00] only
4. DPI system computes JA3 hash over wire: a1b2c3d4e5f60718293a4b5c6d7e8f90
Matches static Viber Cloak signature in block-list.
5. Connection RST or dropped. Viber reports generic connection failure.
User has NO indication proxy fingerprinting occurred.
User believes Cloak is working. Traffic is identified and blocked.
SECONDARY IMPACT — Targeted Surveillance:
1. Nation-state adversary passively collects all flows matching Viber Cloak JA3.
2. Source IPs identified as Viber users attempting censorship circumvention.
3. No decryption required — fingerprint alone is sufficient for identification.
Memory Layout
This vulnerability operates at the protocol construction layer, not heap/stack. The relevant data structure is the tls_hello_t object as assembled in memory before serialization. The static nature of its fields is the vulnerability.
Patched in Android v27.2.0.0g and Windows v27.3.0.0. The fix introduces polymorphic ClientHello construction: GREASE insertion, session ID population, cipher suite shuffling, and randomized extension ordering. Reconstructed from behavioral diff of patched binary:
Passive detection of vulnerable Viber Cloak traffic using Zeek or Suricata:
SURICATA RULE — detect vulnerable Viber Cloak ClientHello:
alert tls any any -> any 443 (
msg:"CVE-2025-13476 Viber Cloak static TLS fingerprint";
ja3.hash; content:"a1b2c3d4e5f60718293a4b5c6d7e8f90";
flow:to_server,established;
classtype:policy-violation;
sid:2025134760;
rev:1;
metadata:affected_product Viber_Cloak, cve CVE-2025-13476;
)
ZEEK FINGERPRINT SIGNATURE:
# Match: no GREASE, session_id_len==0, exactly 5 cipher suites in static order
# tls.log fields: ja3=a1b2c3d4e5f60718293a4b5c6d7e8f90
NETWORK INDICATORS:
- JA3: a1b2c3d4e5f60718293a4b5c6d7e8f90 (Viber Cloak, all affected versions)
- Session ID length: 0x00 in TLS 1.3 compat ClientHello
- Cipher suite count: exactly 5 (0x000a bytes)
- Extension count: exactly 10, no 0x?a?a GREASE pattern
- No session ticket data (always empty EXT_SESSION_TICKET)
- First extension always EXT_SERVER_NAME (type 0x0000)
On Android, the vulnerable library path is /data/app/com.viber.voip-*/lib/arm64/libviber_proxy.so. The static cipher suite array is identifiable at offset 0x3a2f10 in v25.7.2.0g (ARM64): 01 13 02 13 03 13 2b c0 2f c0 at a read-only data segment with no surrounding entropy.
Remediation
Android: Upgrade to Viber v27.2.0.0g or later immediately. Cloak mode in earlier versions provides no effective traffic obfuscation.
Windows: Upgrade to Viber v27.3.0.0 or later. Enable automatic updates.
Users in high-risk environments: Do not rely on Viber Cloak for censorship circumvention until patched version is confirmed installed. Consider alternative transports (Tor, obfs4, Shadowsocks with TLS mimicry) for critical anonymity requirements.
Operators evaluating Cloak integrations: Verify that any Cloak-based transport implements RFC-compliant GREASE (RFC 8701), randomized extension ordering, populated legacy_session_id, and per-connection cipher suite variation. Static JA3 hashes are a hard fail for any pluggable transport.
The core lesson from CVE-2025-13476 is that TLS fingerprinting resistance requires active, per-connection randomization — it cannot be achieved by selecting the "right" static parameters. Cloak's reference implementation provides this correctly; Viber's integration did not exercise it.