Security News

Cybersecurity news aggregator

🔒
MEDIUM Vulnerabilities Reddit r/netsec

Your Duolingo Is Talking to ByteDance: Cracking the Pangle SDK's Encryption

  • What: Pangle SDK in popular apps leaks device data to ByteDance
  • Impact: Users of apps using Pangle SDK may have personal data exposed
Read Full Article →

When you open Duolingo to practice Spanish, BeReal to share a photo, or Character.AI to chat with a bot, you probably don't expect your battery level, storage capacity, and internal IP address to be sent to ByteDance, the company behind TikTok. But that's exactly what's happening. And the encryption "protecting" this data carries its own key in every message. The Discovery Through analysis of mobile network traffic, I identified over 38,000 network requests from 40+ popular apps to ByteDance's Pangle ad SDK infrastructure at api16-access-ttp.tiktokpangle.us . The requests came from apps across every category: language learning, social media, AI chatbots, reading platforms, fitness trackers, and dozens of casual games. Among the apps embedding the Pangle advertising SDK that I observed communicating with ByteDance servers: Duolingo (language learning) BeReal (social photo sharing) Character.AI (AI chatbot) Wattpad (reading platform) Letterboxd (movie tracking) HelloTalk (language exchange) SmartNews (news aggregator) Sweatcoin (fitness rewards) CamScanner (document scanning) Plus over 30 casual gaming apps including Pixel Flow, Idle Soap ASMR, Ludo Star, and Eatventure. All of these apps embed ByteDance's Pangle SDK, an advertising framework that serves ads and collects device telemetry. The data is transmitted under an encryption scheme that ByteDance labels "cypher:3" in the request payloads. Breaking the Encryption The encrypted payloads looked like this in transit: {"cypher": 3, "message": "3eT2HZkAvtEl4Z1cgNmwq7mfXKhJdLTi..."} I set out to understand what was inside. Reverse-Engineering the SDK The Pangle SDK is publicly distributed through ByteDance's Maven repository at artifact.bytedance.com . I downloaded version 6.5.1.2 and began analyzing the native libraries and Java classes. The SDK ships four native libraries: libtobEmbedPagEncrypt.so (8KB encryption library) libpglarmor.so (61KB cryptographic wrapper) libnms.so (281KB networking library) libtt_ugen_layout.so (ad layout engine) I decompiled the Java classes using jadx and traced the encryption pipeline through four key files: PangleEncryptManager.java routes between encryption versions PglCryptUtils.java handles the cypher:4 path using libpglarmor.so PangleEncryptUtils.java handles cypher:3 via libtobEmbedPagEncrypt.so aT.java (obfuscated name) contains the actual encryption implementation The Self-Decrypting Message What I found in aT.java was remarkable. The cypher:3 encryption works as follows: Generate a random 32-character key and 16-character initialization vector (IV) Encrypt the plaintext using AES-256-CBC with PKCS5 padding Concatenate: version byte + shuffled key + IV + Base64(ciphertext) The resulting message format: "3" + [32 chars: shuffled AES key] + [16 chars: IV] + [Base64 ciphertext] +---------+-------------------+--------------+----------------------+ | Byte 0 | Bytes 1-32 | Bytes 33-48 | Bytes 49+ | | Version | AES key (shuffled)| IV | Base64(ciphertext) | +---------+-------------------+--------------+----------------------+ The encryption key and IV are embedded in every single message. To decrypt any cypher:3 payload, you simply read the key from character positions 1-32 (swapping the two halves), the IV from positions 33-48, and the ciphertext from position 49 onward. This is the cryptographic equivalent of locking your front door and taping the key to the doorframe. The scheme functions more as obfuscation of SDK traffic than as cryptographic protection, since anyone who reads the code, which is publicly distributed, can decrypt every message. A Hardcoded Key Too The SDK also contains a second encryption path through a native library called libtobEmbedPagEncrypt.so . Disassembling this 8KB binary revealed a hardcoded AES key stored in plaintext in the .rodata section: UK*@3oKpFlVVnads . This key is identical across SDK versions 5.3.1.0 and 6.5.1.2, meaning every app using the Pangle SDK ships the same static key in its binary. Confirming the Algorithm The underlying cipher in Cb/aT.java is standard Java cryptography: Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(1, secretKeySpec, new IvParameterSpec(str2.getBytes())); AES-256-CBC is a strong algorithm when the key is kept secret. Here, the key travels with the ciphertext. I built a decryptor and tested it against 694 captured cypher:3 payloads. The result: 694 out of 694 requests decrypted successfully. A 100% success rate. A typical decrypted payload (redacted): { "device": { "device_model": "iPhone15,3", "os_version": "18.7.2", "battery_remaining_pct": 83, "total_space": "255413800960", "free_space": 66881117072, "screen_bright": 0.71, "ip": "192.168.x.x", "device_city": "America/New_York", "idfv": "[redacted]", "darkmode": 1, "rooted": 0 }, "app_id": "8139319", "sdk_version": "7.2.0.4", "mediation": "google" } What ByteDance Collects The decrypted payloads revealed comprehensive device fingerprinting. Every time one of these 40+ apps initializ...

Share this article