Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

instagram-ssl-pinning-bypass

A Frida agent that disables TLS certificate validation and pinning in the Instagram Android app, so its HTTPS traffic can be inspected with an intercepting proxy such as mitmproxy or Burp Suite.

Instagram defends its traffic on three layers, and this agent neutralises all of them:

Layer What it covers
Conscrypt TrustManagerImpl The platform trust check used by Java / OkHttp request paths
javax.net.ssl.SSLContext Any TLS context the app builds — a permissive trust manager is injected
Meta Tigon (TigonMNSServiceHolder) The native networking stack that carries most of the API traffic (i.instagram.com, graph.instagram.com) and pins independently of the JVM trust store

⚠️ For authorised security research and testing only. Use this exclusively on devices and accounts that you own or have explicit written permission to test. You are responsible for complying with all applicable laws and with the service's Terms of Service.


Why a Frida agent (and not just a proxy)

Setting a system HTTP proxy is not enough on its own:

  • The app rejects the proxy's certificate because of pinning.
  • The main API traffic runs through Tigon, a native stack that pins separately from the Android trust store, so unpinning only the Java layer leaves it encrypted.

This agent hooks the Tigon configuration (initHybrid) before it initialises and relaxes its certificate handling, in addition to the standard Java-layer unpinning. With it loaded, i.instagram.com / graph.instagram.com requests (GraphQL, Bloks, attestation, logging, …) decrypt cleanly in the proxy.


Requirements

  • A rooted Android device or emulator (the agent itself works on any device Frida can instrument; root is required to run frida-server and to trust the proxy CA).
  • Frida on the host and a matching frida-server on the device.
    • Verified on Frida 16.7.x and 17.x (e.g. 16.7.19 and 17.13.0), loaded through the frida CLI. On Android 14/15 use one of these lines; releases older than ~16.4 do not spawn reliably on recent Android.
    • The host Frida and the device frida-server versions must match exactly.
  • An intercepting proxy (examples below use mitmproxy).
  • A supported Instagram build (see the table below).

Supported Instagram versions

Instagram version Status APK
433.0.0.47.68 ✅ Verified APKMirror

Tested on Android 15 (arm64).


Setup

1. frida-server on the device

# match this to your host Frida version (example: 16.7.19, arm64)
adb push frida-server-16.7.19-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c '/data/local/tmp/frida-server &'"   # or: adb root; adb shell '/data/local/tmp/frida-server &'

2. Trust the proxy CA

Pinning bypass and trust are separate concerns. The Java layer trusts any certificate once this agent is loaded, but the native stack still validates against the system store, so install your proxy's CA as a system certificate (the Tigon hook then accepts it). On Android 14+ the runtime store lives in the Conscrypt APEX (/apex/com.android.conscrypt/cacerts); a rooted device can place the CA there. Alternatively, run the app under a network where the proxy is the gateway.

3. Point the device at your proxy

# mitmproxy on the host, reachable from the device (same Wi-Fi)
mitmdump --listen-host 0.0.0.0 -p 8080 -w capture.mitm
adb shell settings put global http_proxy <HOST-IP>:8080

Instagram honours the system HTTP proxy once pinning is bypassed, including the Tigon paths (the agent forces HTTP/2 over TCP so the proxy can read them).


Usage

Spawn the app under Frida with the agent loaded:

frida -U -f com.instagram.android -l scripts/instagram-unpin.js

Run it in a normal terminal and quit with quit / Ctrl-D. (Don't keep it alive with tail -f /dev/null | frida ... — that pipe makes the Frida CLI abort at shutdown with _enter_buffered_busy, the "Python quit unexpectedly" dialog.)

Or use the runner (run.py)

run.py loads the agent the right way and exits cleanly (no shutdown crash). It spawn-gates the load so the Tigon hook lands before initHybrid, checks the host/device Frida versions, and can set/clear the device proxy for you:

python3 run.py                              # spawn + load
python3 run.py --proxy 192.168.1.17:8080    # also set the device proxy (cleared on exit)
python3 run.py --attach                     # attach to a running app instead

It uses Frida's raw scripting API, which needs the global Java bridge, so run it on Frida 16.x. On 17.x use the frida CLI above (it bundles the bridge).

Expected output on a clean launch:

[unpin] starting Instagram TLS unpinning
[unpin] conscrypt: TrustManagerImpl.checkTrustedRecursive neutralised
[unpin] SSLContext.init wrapped with permissive trust manager
[unpin] tigon: matched initHybrid overload #0 (...)
[unpin] tigon: initHybrid intercepted, certificate handling relaxed

Drive the app and the requests will appear, decrypted, in your proxy.

When you are done:

adb shell settings put global http_proxy :0   # clear the proxy

How it works

scripts/instagram-unpin.js installs three patches:

  1. ConscryptTrustManagerImpl.checkTrustedRecursive is replaced so it returns an empty chain (i.e. "trusted") instead of throwing.
  2. SSLContextSSLContext.init(...) is wrapped to substitute a no-op X509TrustManager, so every context the app creates accepts any certificate.
  3. TigonTigonMNSServiceHolder.initHybrid(...) is intercepted and the TigonMNSConfig argument is relaxed (certificate verification disabled, sandbox certificates trusted, HTTP/2 forced) before the real call runs.

The Tigon class only loads after its native library is mapped, and initHybrid must be hooked before it runs. The agent uses two complementary triggers — a SoLoader load callback and a safe poll over the already-loaded class set (enumerateLoadedClassesSync, which never force-resolves the class) — and a guard flag ensures the hook is installed exactly once.


Troubleshooting

  • Failed to spawn: timeout — host Frida is too old for your Android version. Use 16.7.x or 17.x.
  • ReferenceError: 'Java' is not defined — you loaded the agent through a raw device.create_script() on Frida 17 (which no longer auto-injects the Java bridge). Load it with the frida CLI (-l), which bundles the bridge.
  • API hosts still fail the TLS handshake — the Tigon hook did not land before initHybrid. Make sure you spawned with -f (not attached late) and relaunch; if you see no tigon: matched ... line, the app build may use an overload this script does not yet match — open an issue with the printed unmatched overload lines.
  • *.googleapis.com handshake failures — those are Google Play Services, not Instagram; they pin separately and can be ignored.

License

Released under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages