What the Transsion Telemetry Research Means for Mobile Security - NowSecure
-->
2026 Mobile App Risk Management Survey
See what senior security leaders across finance, healthcare, high tech and retail report, how their answers compared to AI model predictions, and the strategic recommendations you need to close the gap.
Download the Report
See What 485 Security Leaders Say About Mobile App Risk<br>See What 485 Security Leaders Say About Mobile App Risk
Download the Report
1-in-2 phones sold in Africa ship a hidden telemetry framework that encrypts whole-device activity to shalltry.com
Transsion is the world’s fourth-largest smartphone manufacturer. Its brands — TECNO, Infinix, and itel — dominate markets across Africa, South Asia, Southeast Asia, and Latin America. Every one of these devices ships with a first-party Android telemetry framework: Athena for event collection and oneID for cross-app tracking, both reporting to *.shalltry.com.
While users have long known these devices "call home" — shalltry.com frequently appears on ad and tracker blocklists, and the "com.hoffnung" package is notoriously impossible to remove — the content of these beacons has remained a mystery. The traffic is heavily encrypted, effectively turning the devices’ telemetry into a black box. Until now.
The encryption is real but it is not a barrier: the keys were embedded directly inside the client. This is a write-up of recovering it, decrypting the traffic, and reading what a Transsion phone actually sends, which turns out to include the network activity of every app on the device, the foreground app moment to moment, precise location, and which app opened the camera, all bound to permanent identifiers. From a mobile security perspective, the findings demonstrate how software outside an organization’s direct control can collect and transmit sensitive information without obvious visibility.
We extracted the SDK from a TECNO Spark 40 (KM5) firmware image and confirmed the decryption against live device traffic. Everything below is reproducible from the firmware and a single runtime hook.
The upload channel is AES, and the key is in the APK
The bulk telemetry endpoint is POST /athena/checkpoint/v3/upload. The body is base64 of an AES ciphertext; a handful of request headers describe how to decrypt it: encrypt-index, encrypt-level: 3, zip, ver, appids, dupid.
Pulling com.rlk.weathers (the Weather app, which embeds Athena 3.1.0.2-sys) out of the ROM and decompiling it, the cipher construction is a plain SecretKeySpec, with no native code and no white-box:
// g2/b.java : the per-message cipher<br>SecretKeySpec k = new SecretKeySpec(bArr, "AES");<br>Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");<br>c.init(1, k, new IvParameterSpec(i2.g.f4592o)); // f4592o = the IV
Two things need recovering: the IV i2.g.f4592o, and the key bArr.
The initial recovery step targeted the IV. Rather than being hardcoded in plain sight, the framework pulls the value from a config bundle hidden behind a layer of obfuscation.:
// i2/g.java<br>f4592o = jSONObject.getString("iv").getBytes();
That config bundle is itself hex-encoded and XOR-obfuscated in the binary, decoded by a one-line transform:
// e2/a.java<br>for (int i = 0; i
Running that transform over the embedded blob yields the framework’s bootstrap configuration in the clear:
"iv": "abcdefghijk1mnop",<br>"w": "3716E5CB1B982C035013E52167AAF54A",<br>"u": "/athena/checkpoint/v2/upload",<br>"g": "/logconf/v1/secret",<br>...
The IV is the 16-byte ASCII string abcdefghijk1mnop , constant for every message on every device. w is a fallback key, used before the device has provisioned a key table.
The key is selected by index, and the table is one global constant
The per-message key is chosen at send time:
// e2/a.java : key selection<br>List table = ...f4615l; // the "p" key table<br>int idx = (int)(System.currentTimeMillis() % table.size());<br>return new Pair<>(idx, table.get(idx));<br>// m2/d.java sets the header: encrypt-index = idx + 1
So key = KEY_TABLE[encrypt-index – 1]. The table has 64 entries; each key is a 32-character ASCII hex string used as raw bytes (getBytes()), which is a 32-byte AES-256 key. The keys are provisioned from the control plane (/logconf) rather than hardcoded, which raises the obvious question of whether the table differs per device or per SDK version.
It does not. Two different devices, on two different SDK versions, sending the same event at the same encrypt-index, produce an identical ciphertext prefix , the signature of the same key over the same plaintext under a fixed IV. Grouping traffic by (encrypt-index, ciphertext-prefix) shows single prefixes shared across up to seven devices and four SDK versions (2.3.6.4, 2.3.6.5, 3.0.0.8-sys, 3.0.1.3). The 64-key table is a global constant , stable across the fleet and across at least the 2.3.x to 3.1.x version range.
That means one capture of the table decrypts everything.
Recovering the table: one runtime hook
The table lives in...