I discovered a non documented anti-censorship feature in WhatsApp that allows users to bypass censorship in certain countries. When I started analyzing the app my initial goal wasn’t to find anything like this, but I stumbled across the package It’s interesting that apparently this has never been documented or talked about online, as I couldn’t find any articles, tweets, reddit posts, nothing. Not even writeups with the endpoints. I immediately opened its package and inside its content found a reference to The configuration file is a JSON file that contains various parameters for the Psiphon tunnel, like: I have truncated some of it’s values for convenience: The regional allowlist is a feature that allows WhatsApp to enable the Psiphon tunnel only for certain countries. This is done using two feature flags: Said countries are just an example, as of now we can’t disclose said values. However, funnily enough there’s one that comes hardcoded in the decompiled code, which is Russia (RU): here we also noticed this other url which i’ve never seen before There’s this other method that checks if the user has a current VPN connection, and if so, it disables the Psiphon tunnel. This is likely to avoid conflicts between the two VPNs. First of all, this binary is not part of the APK, it is downloaded dynamically using Google Play’s. Here’s a small TLDR of what we know so far: lib proxy is downloaded with Voltron, which i will later explain, and contains the Psiphon tunnel implementation, which is responsible for establishing the connection to the embedded servers. In the base APK the packages are not included but we can still find references to it. Voltron is Meta’s internal codename for their dynamic module delivery system, built on top of Google Play’s App Bundle / Dynamic Feature Modules. It’s not exclusive to proxyservice, other modules like i18n use the same infrastructure to ship code and assets post installation instead of bundling everything into the base APK. This appears to be non documented, as I couldn’t find any information about it online. The only thing I could find were some github repos with decompiled code. Not every module is downloaded immediately. OxygenScheduledInstallerJobService (an Android JobService) handles deferred/retry installs for modules that failed to download or weren’t needed right away: Using static analysis I discovered an undocumented WhatsApp anticensorship feature that can route traffic through a Psiphon tunnel for users in certain regions. It’s gated by regional allowlists and feature flags, and implemented inside a dynamically downloaded module—requiring dynamic testing to fully evaluate its behavior and limits. Diving into a non documented WhatsApp anti-censorship feature
I discovered a non documented anticensorship feature in WhatsApp
ca.psiphon in the decompiled code. I was confused at first, thinking my sample apk was infected with malware or part of a p2p network, but after meticulously analyzing the code it turned out to be much more interesting than that: it was a full Psiphon tunnel implementation, which is a well known anticensorship tool.The config
/assets/proxyservice_config.json which is a configuration file for the Psiphon tunnel. I was surprised to find that there was no information about this feature online, no articles, tweets, reddit posts, nothing. The only thing I could find were some github repos with decompiled code.proxyservice_config.json
SponsorId, PropagationChannelId, ClientPlatform, ClientVersion, EmitClientAddress, EmitDiagnosticNotices, EmitDiagnosticNetworkParameters, ServerEntrySignaturePublicKey, PushPayloadSignaturePublicKey, RemoteServerListSignaturePublicKey, AdditionalParameters, DisableServerEntriesReporter, ConnectionWorkerPoolSize, TransformHostNameProbability, TLSFragmentClientHelloProbability and EstablishTunnelTimeoutSeconds.{
"SponsorId" : "80...",
"PropagationChannelId" : "89...",
"ClientPlatform" : "meta",
"ClientVersion" : "%d",
"EmitClientAddress": true,
"EmitDiagnosticNotices": true,
"EmitDiagnosticNetworkParameters": true,
"ServerEntrySignaturePublicKey": "sHu...",
"PushPayloadSignaturePublicKey": "9/X8...",
"RemoteServerListSignaturePublicKey": "MII...",
"AdditionalParameters": "ZFZE7E...",
"DisableServerEntriesReporter": true,
"ConnectionWorkerPoolSize": 30,
"TransformHostNameProbability": 0.9,
"TLSFragmentClientHelloProbability": 0.5,
"EstablishTunnelTimeoutSeconds": 0
}Regional allowlist
18724 and 18725. The values of these flags are CSV strings that contain the ISO country codes of the allowed countries.builder4.put(18725, "RU");
builder4.put(18724, "RU");https://mmg.whatsapp.net/proxygen/health which replies with PROXY-IS-ALIVE, this is likely a ping endpoint for the Psiphon tunnel, to check if it’s alive and working.package p000X;
import android.app.Application;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/* JADX INFO: renamed from: X.0gv, reason: invalid class name and case insensitive filesystem */
/* JADX INFO: loaded from: classes.dex */
public final class C15130gv {
public final Application A00 = C00I.A00();
public final C05Z A01 = C05V.A00(59);
public final C05Z A02 = C05V.A00(53);
public final C05Z A04 = C05V.A00(191);
public final C05Z A03 = C05V.A00(58);
// Pick config
public static final boolean A00(C15130gv c15130gv) {
return !((C08T) c15130gv.A04.A00.get()).A0O() || ((C0CA) c15130gv.A03.A00.get()).A01() == 0;
}
// See if a VPN is in use, if so, disable the Psiphon tunnel to avoid conflicts
public final C047507k A01() {
C00D c00d;
int i;
String str;
Application application = this.A00;
if (A00(this)) {
c00d = (C00D) this.A01.A00.get();
i = 24068;
} else {
c00d = (C00D) this.A02.A00.get();
i = 24067;
}
boolean z = false;
if ((!c00d.A0r(i)) && AbstractC37631fy.A00(application)) {
str = "vpn_in_use";
} else {
z = true;
str = "";
}
return new C047507k(z, str);
}
// Alternative feature flags
public final boolean A02() {
C00D c00d;
int i;
if (A00(this)) {
c00d = (C00D) this.A01.A00.get();
i = 28186;
} else {
c00d = (C00D) this.A02.A00.get();
i = 28189;
}
return c00d.A0r(i);
}
// Check if the user is in an allowed country, if so, enable the Psiphon tunnel
public final boolean A03(String str) {
C00D c00d;
int i;
if (A00(this)) {
c00d = (C00D) this.A01.A00.get();
i = 18724;
} else {
c00d = (C00D) this.A02.A00.get();
i = 18725;
}
String strA0Y = c00d.A0Y(i);
C00m.A0A(strA0Y, 0);
C06N.A0B(str, strA0Y, "proxy_service", "User Region / Allowed Region: %s / %s");
Locale locale = Locale.ROOT;
C00m.A07(locale);
String lowerCase = strA0Y.toLowerCase(locale);
C00m.A06(lowerCase);
List listA0m = AbstractC05720Bs.A0m(lowerCase, new String[]{","}, 0);
ArrayList arrayList = new ArrayList();
for (Object obj : listA0m) {
if (!((String) obj).equals("")) {
arrayList.add(obj);
}
}
String lowerCase2 = str.toLowerCase(locale);
C00m.A06(lowerCase2);
return arrayList.contains(lowerCase2);
}
}libproxyservice.so
proxyservice modulelibproxyservice.so and proxy_service embedded server entries{
"assets/proxyservice/libs.txt": "37c3eb9fe437d9392cc9b174da3876bb1b2c76e51b13b12bfe5c2f5458ac0afa",
"assets/proxyservice/voltron_meta_embedded_server_entries": "0dd547935296c17697270c4cd4a56d648bab1a5b31c75e1589f2de7214523762"
}```
```json
{
"lib/arm64-v8a/libgojni.so": "d6e86fcdf8a899fcde997c45ad89992ba0dc141d8afdc9d994c50fb100a81717"
}Voltron
public boolean onStartJob(JobParameters jobParameters) {
// Runs on a background job, checks for missing modules, retries install
}
public final List A00(C12350bO c12350bO) {
// Reads a map of "AppModules::ScheduledInstallRequestTimestamp"
// Cross-checks which modules are still missing via ModuleApkUtil$ModuleResolver
// Returns list of modules that still need installing
}
public boolean onStopJob(JobParameters jobParameters) {
// If there are still missing modules, request retry (return true)
// Otherwise, don't reschedule (return false)
}conclusion
WhatsApp Anti-Censorship Networking psiphon