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.javaroutes between encryption versionsPglCryptUtils.javahandles the cypher:4 path usinglibpglarmor.soPangleEncryptUtils.javahandles cypher:3 vialibtobEmbedPagEncrypt.soaT.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 initializes the Pangle SDK, the following data is sent to ByteDance:
Device Hardware Profile
- Exact device model (e.g., iPhone 16 Pro Max, iPhone 14)
- CPU core count
- Total RAM
- Total and free storage capacity
- Screen dimensions and pixel density
Device State
- Battery level and charging status
- Screen brightness
- Volume level
- Dark mode setting
- Airplane mode
- Whether headphones are connected
- Whether the screen is on or off
- Low power mode status
Network and Location Signals
- Internal IP address
- Connection type (WiFi/cellular)
- Timezone (e.g., America/New_York)
- Carrier name and MCC/MNC codes
Identifiers
- IDFV (Identifier for Vendor, a persistent per-app device ID)
- IDFA (Advertising ID, when available)
- Boot timestamp
- System build version
Privacy and Consent
- GDPR status
- CCPA flag
- App Tracking Transparency (ATT) status
- Personalized advertising preference
This data was observed across 23 Pangle publisher apps, spanning 20 different device models and four US time zones.
The Encryption Double Standard
ByteDance's Pangle SDK actually uses two encryption schemes:
- Cypher:3 uses self-keyed AES for device fingerprinting and SDK configuration (the data I decrypted)
- Cypher:4 uses ECIES (Elliptic Curve Integrated Encryption Scheme) for ad statistics and behavioral events
Cypher:4 uses proper ephemeral key exchange. Each message generates a fresh elliptic curve key pair, and the shared secret is derived via ECDH. This is genuinely strong encryption that cannot be broken from passive traffic observation.
The contrast is telling. ByteDance applies real cryptographic protection to the data valuable to their business: ad impressions, click attribution, revenue tracking. But the device fingerprints they harvest from users? Those get the key-taped-to-the-doorframe treatment.
The Server Response
I also decrypted 685 server responses, which revealed ByteDance's SDK configuration pushed to devices. Among the findings:
- A universal AES key sent identically to all devices, likely for encrypting cached resources
- Behavioral tracking configurations including video play counting, ad impression tracking, landing page dwell time measurement, and video completion monitoring
- Backup server URLs at
tiktokpangle-b.usdomains - Dynamic code delivery via ByteDance's "Gecko" system at
gecko16-normal-useast5.tiktokv.us
The Broader Picture
The Pangle SDK runs inside apps that collectively have hundreds of millions of users. These users downloaded a language learning app or a movie tracker, not TikTok. Yet through the ad SDK embedded by app developers, ByteDance receives a detailed hardware and behavioral fingerprint from each device.
The "encryption" protecting this data collection is trivially breakable by anyone who downloads the publicly available SDK and reads the code. Every message carries its own decryption key.
This does not expose the data to network attackers, because HTTPS already protects the transport layer. But the application-layer encryption that ByteDance adds on top, the layer they control and presumably designed to provide additional protection, serves only as obfuscation against casual inspection. It would not withstand scrutiny from any party with the motivation to look, whether that's a security researcher, a regulator, or a competitor.
Disclosure
Due to the design nature of the SDK's encryption scheme rather than an exploitable vulnerability affecting users directly, this research is published without coordinated disclosure. ByteDance may update the SDK's encryption approach in future versions.
Methodology
SDK analysis was conducted using publicly available binaries downloaded from ByteDance's Maven repository. No systems were accessed without authorization, no vulnerabilities were exploited in live services, and no individual user data is disclosed in this publication.
The Pangle SDK analyzed (version 6.5.1.2) was downloaded from artifact.bytedance.com/repository/pangle/.