Hunting mobile TLS pinning and trust gaps: when the app trusts a network it should not
A mobile app talks to its backend over connections it treats as private, and that privacy depends entirely on
the app rejecting any network position that would let someone read or alter the traffic. The app does that by
validating the server's certificate against trusted anchors, refusing cleartext, and, for its most sensitive
connections, pinning to a known certificate or key so even a trusted-but-wrong authority is rejected. The gap
is any place the app accepts a network position it should reject: it trusts certificate authorities the user
or a device profile added, it allows a cleartext or mixed connection, it disables or loosens validation for
convenience, or it pins some connections but leaves an unpinned or fallback path a network attacker can take.
The bug is not the absence of pinning everywhere; it is a sensitive connection the app treats as secure that
an intercepting network position can actually read or alter. You hunt these by listing every connection and
checking whether each resists a network attacker.
When to use
- A mobile app makes network connections it treats as private and you can observe its trust behavior.
- The app may trust user-added anchors, allow cleartext, or loosen certificate validation.
- Some sensitive connections may be unpinned, or pinning may have a bypassable fallback.
Scope check
Hunt transport trust gaps only on apps and networks you own or are authorized to assess, on test devices and
accounts, interposing only on a network you control and never on real users' traffic. A confirmed gap exposes
supposedly secure traffic, so keep every probe within scope. If you can't name the authorization, stop.
The loop
Establish whether each sensitive connection rejects an intercepting position first. For each connection
the app treats as private, determine whether it validates the certificate against trusted anchors, refuses
cleartext, and, where required, pins to a known certificate or key, so an intercepting network position is
rejected, or whether some trust gap lets that position succeed. This is the false-positive killer: a
connection that validates strictly and pins where it matters cannot be read by a network attacker, so its
presence is not a finding. Name the accepted network position before claiming interception.
Enumerate the app's connections and their sensitivity. List the connections the app makes, the data
each carries, and which are sensitive enough that reading or altering them would matter. Note which the app
treats as secure and relies on for authentication or private data.
Check the trust anchors and validation. Determine whether the app trusts only the system anchors it
should, or also user-added authorities or a device profile's anchors, and whether it validates the
certificate fully, hostname included, or disables or loosens validation. A user-trusted anchor or disabled
validation lets an interposed certificate be accepted.
Check for cleartext and mixed connections. Determine whether any sensitive connection uses cleartext or
falls back to it, or mixes secure and insecure content, so part of the traffic is readable regardless of
the certificate handling.
Check the pinning coverage and fallback. For connections that pin, determine whether every sensitive
connection is pinned or only some, and whether pinning has a fallback, a catch-all path, an unpinned
subdomain, or a bypass, that a network attacker can steer the app onto. Partial or bypassable pinning
leaves the unpinned path open.
Confirm and record. Confirm by interposing on a network you control and reading or altering a sensitive
connection the app treats as secure, on a test device with a test account. Kill the lead if every sensitive
connection validates strictly, refuses cleartext, and pins where required with no bypassable fallback, or if
the interception requires a trust the attacker cannot obtain. Record the connection, the accepted network
position, the trust gap, and the interception observed, or set a kill_reason.
Where transport trust leaks
- The accepted network position is the finding. A secure connection is expected; the bug is one the app
treats as secure that an intercepting position can read or alter. Name the position and how it is accepted.
- User-added anchors defeat validation. An app that trusts user-added or profile-added authorities accepts
a certificate an intercepting proxy presents, so validation passes against the wrong anchor.
- Cleartext and fallback expose traffic directly. A sensitive connection that uses or falls back to
cleartext is readable regardless of certificate handling, and mixed content leaks the insecure part.
- Partial pinning leaves an open path. Pinning some connections but not others, or an unpinned subdomain
or catch-all, lets a network attacker steer the app onto the unpinned path.
- Bypassable pinning is not pinning. A pin with a fallback to system validation, or one that can be
disabled by a setting or a downgrade, does not resist an interposed trusted-but-wrong certificate.
Worked example (a confirm and a kill)
Confirm. The app pins its primary API connection but makes a sensitive secondary connection to an
unpinned host that validates only against system and user-added anchors. Interposing on a controlled network
with a user-added anchor lets that secondary connection be read and altered, exposing data the app treats as
secure, on a test device. Confirmed transport trust gap through partial pinning and user-anchor trust,
high, remediation = pin every sensitive connection to a known certificate or key, trust only system
anchors for those connections, refuse cleartext and fallback, and remove any unpinned sensitive path.
Kill. Every sensitive connection validates fully against system anchors only, ignores user-added
authorities, refuses cleartext with no fallback, and pins to a known key with no bypassable path. An
interposed certificate is rejected on every sensitive connection and no traffic is readable. Killed,
kill_reason = "all sensitive connections validate strictly against system anchors, refuse cleartext, and
pin without a fallback; the intercepting position is rejected everywhere it matters."
Rationalizations to reject
- "We use HTTPS everywhere." -> HTTPS validates against whatever anchors the app trusts; if it trusts
user-added authorities or loosens validation, an interposed certificate still passes.
- "The main connection is pinned." -> Coverage is the point; a sensitive secondary or fallback connection
left unpinned is the path a network attacker takes, so check every connection, not the primary.
- "Only a rooted device trusts extra anchors." -> A device profile or a user can add an anchor without root;
confirm the app ignores user-added anchors for sensitive connections rather than assuming they cannot exist.
- "Cleartext is only for non-sensitive calls." -> Confirm which data each cleartext connection carries and
that none falls back to cleartext; a fallback exposes the traffic the pin was meant to protect.
- "Pinning can be turned off for debugging." -> A pin with a switch or a fallback is bypassable; confirm the
production path cannot be downgraded onto system validation.
Executing this in practice
You need every connection the app makes with its sensitivity, the trust anchors and validation each uses,
whether any use or fall back to cleartext, and which are pinned with what fallback. For each sensitive
connection, decide whether an intercepting network position is rejected. Reading the trust configuration and
observing the app's behavior settles most leads; interposing on a controlled network and reading or altering a
sensitive connection on a test device settles the rest.
Related
auditing-tls-and-certificate-validation - the general certificate-validation audit, of which mobile
pinning and anchor trust are the app-side application.
auditing-mobile-webview-bridge-exposure - content loaded over a connection an attacker can intercept
reaches the WebView bridge, so a transport gap feeds that exposure.
hunting-mobile-secret-and-storage-exposure - a token read from intercepted traffic joins the secret
exposure that skill pursues from the storage side.
auditing-mobile-backend-and-firebase-exposure - an intercepted or unpinned backend connection reveals the
backend endpoints and keys that skill audits.
- FINDING-SCHEMA.md - source = the intercepting network position the app fails to
reject, sink = the connection the app treats as trusted, evidence = reading or altering a sensitive
connection on a controlled network with a test device.
1---2name: hunting-mobile-tls-pinning-and-trust-gaps3description: Hunt transport trust gaps in a mobile app, where the app accepts a network position it should reject, because it trusts user-added certificate authorities, allows cleartext or mixed connections, disables or misapplies certificate validation, or pins only some connections so an unpinned or fallback path lets a network attacker read or alter traffic the app treats as secure. Use when reviewing how a mobile app establishes and validates its network connections and whether every sensitive connection resists an intercepting network position. Covers user-trusted anchors, cleartext and mixed connections, disabled or permissive validation, and partial or bypassable pinning. The intercepting network position the app fails to reject is the source, the connection the app treats as trusted is the sink, and reading or altering supposedly secure traffic is the bug.4license: MIT5---67# Hunting mobile TLS pinning and trust gaps: when the app trusts a network it should not89A mobile app talks to its backend over connections it treats as private, and that privacy depends entirely on10the app rejecting any network position that would let someone read or alter the traffic. The app does that by11validating the server's certificate against trusted anchors, refusing cleartext, and, for its most sensitive12connections, pinning to a known certificate or key so even a trusted-but-wrong authority is rejected. The gap13is any place the app accepts a network position it should reject: it trusts certificate authorities the user14or a device profile added, it allows a cleartext or mixed connection, it disables or loosens validation for15convenience, or it pins some connections but leaves an unpinned or fallback path a network attacker can take.16The bug is not the absence of pinning everywhere; it is a sensitive connection the app treats as secure that17an intercepting network position can actually read or alter. You hunt these by listing every connection and18checking whether each resists a network attacker.1920## When to use2122- A mobile app makes network connections it treats as private and you can observe its trust behavior.23- The app may trust user-added anchors, allow cleartext, or loosen certificate validation.24- Some sensitive connections may be unpinned, or pinning may have a bypassable fallback.2526## Scope check2728Hunt transport trust gaps only on apps and networks you own or are authorized to assess, on test devices and29accounts, interposing only on a network you control and never on real users' traffic. A confirmed gap exposes30supposedly secure traffic, so keep every probe within scope. If you can't name the authorization, stop.3132## The loop33341. **Establish whether each sensitive connection rejects an intercepting position first.** For each connection35 the app treats as private, determine whether it validates the certificate against trusted anchors, refuses36 cleartext, and, where required, pins to a known certificate or key, so an intercepting network position is37 rejected, or whether some trust gap lets that position succeed. This is the false-positive killer: a38 connection that validates strictly and pins where it matters cannot be read by a network attacker, so its39 presence is not a finding. Name the accepted network position before claiming interception.40412. **Enumerate the app's connections and their sensitivity.** List the connections the app makes, the data42 each carries, and which are sensitive enough that reading or altering them would matter. Note which the app43 treats as secure and relies on for authentication or private data.44453. **Check the trust anchors and validation.** Determine whether the app trusts only the system anchors it46 should, or also user-added authorities or a device profile's anchors, and whether it validates the47 certificate fully, hostname included, or disables or loosens validation. A user-trusted anchor or disabled48 validation lets an interposed certificate be accepted.49504. **Check for cleartext and mixed connections.** Determine whether any sensitive connection uses cleartext or51 falls back to it, or mixes secure and insecure content, so part of the traffic is readable regardless of52 the certificate handling.53545. **Check the pinning coverage and fallback.** For connections that pin, determine whether every sensitive55 connection is pinned or only some, and whether pinning has a fallback, a catch-all path, an unpinned56 subdomain, or a bypass, that a network attacker can steer the app onto. Partial or bypassable pinning57 leaves the unpinned path open.58596. **Confirm and record.** Confirm by interposing on a network you control and reading or altering a sensitive60 connection the app treats as secure, on a test device with a test account. Kill the lead if every sensitive61 connection validates strictly, refuses cleartext, and pins where required with no bypassable fallback, or if62 the interception requires a trust the attacker cannot obtain. Record the connection, the accepted network63 position, the trust gap, and the interception observed, or set a `kill_reason`.6465## Where transport trust leaks6667- **The accepted network position is the finding.** A secure connection is expected; the bug is one the app68 treats as secure that an intercepting position can read or alter. Name the position and how it is accepted.69- **User-added anchors defeat validation.** An app that trusts user-added or profile-added authorities accepts70 a certificate an intercepting proxy presents, so validation passes against the wrong anchor.71- **Cleartext and fallback expose traffic directly.** A sensitive connection that uses or falls back to72 cleartext is readable regardless of certificate handling, and mixed content leaks the insecure part.73- **Partial pinning leaves an open path.** Pinning some connections but not others, or an unpinned subdomain74 or catch-all, lets a network attacker steer the app onto the unpinned path.75- **Bypassable pinning is not pinning.** A pin with a fallback to system validation, or one that can be76 disabled by a setting or a downgrade, does not resist an interposed trusted-but-wrong certificate.7778## Worked example (a confirm and a kill)7980> **Confirm.** The app pins its primary API connection but makes a sensitive secondary connection to an81> unpinned host that validates only against system and user-added anchors. Interposing on a controlled network82> with a user-added anchor lets that secondary connection be read and altered, exposing data the app treats as83> secure, on a test device. **Confirmed** transport trust gap through partial pinning and user-anchor trust,84> `high`, remediation = pin every sensitive connection to a known certificate or key, trust only system85> anchors for those connections, refuse cleartext and fallback, and remove any unpinned sensitive path.86>87> **Kill.** Every sensitive connection validates fully against system anchors only, ignores user-added88> authorities, refuses cleartext with no fallback, and pins to a known key with no bypassable path. An89> interposed certificate is rejected on every sensitive connection and no traffic is readable. **Killed**,90> `kill_reason` = "all sensitive connections validate strictly against system anchors, refuse cleartext, and91> pin without a fallback; the intercepting position is rejected everywhere it matters."9293## Rationalizations to reject9495- *"We use HTTPS everywhere."* -> HTTPS validates against whatever anchors the app trusts; if it trusts96 user-added authorities or loosens validation, an interposed certificate still passes.97- *"The main connection is pinned."* -> Coverage is the point; a sensitive secondary or fallback connection98 left unpinned is the path a network attacker takes, so check every connection, not the primary.99- *"Only a rooted device trusts extra anchors."* -> A device profile or a user can add an anchor without root;100 confirm the app ignores user-added anchors for sensitive connections rather than assuming they cannot exist.101- *"Cleartext is only for non-sensitive calls."* -> Confirm which data each cleartext connection carries and102 that none falls back to cleartext; a fallback exposes the traffic the pin was meant to protect.103- *"Pinning can be turned off for debugging."* -> A pin with a switch or a fallback is bypassable; confirm the104 production path cannot be downgraded onto system validation.105106## Executing this in practice107108You need every connection the app makes with its sensitivity, the trust anchors and validation each uses,109whether any use or fall back to cleartext, and which are pinned with what fallback. For each sensitive110connection, decide whether an intercepting network position is rejected. Reading the trust configuration and111observing the app's behavior settles most leads; interposing on a controlled network and reading or altering a112sensitive connection on a test device settles the rest.113114## Related115116- `auditing-tls-and-certificate-validation` - the general certificate-validation audit, of which mobile117 pinning and anchor trust are the app-side application.118- `auditing-mobile-webview-bridge-exposure` - content loaded over a connection an attacker can intercept119 reaches the WebView bridge, so a transport gap feeds that exposure.120- `hunting-mobile-secret-and-storage-exposure` - a token read from intercepted traffic joins the secret121 exposure that skill pursues from the storage side.122- `auditing-mobile-backend-and-firebase-exposure` - an intercepted or unpinned backend connection reveals the123 backend endpoints and keys that skill audits.124- [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the intercepting network position the app fails to125 reject, sink = the connection the app treats as trusted, evidence = reading or altering a sensitive126 connection on a controlled network with a test device.