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.
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.
- A rooted Android device or emulator (the agent itself works on any device
Frida can instrument; root is required to run
frida-serverand to trust the proxy CA). - Frida on the host and a matching
frida-serveron the device.- Verified on Frida 16.7.x and 17.x (e.g. 16.7.19 and 17.13.0),
loaded through the
fridaCLI. 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-serverversions must match exactly.
- Verified on Frida 16.7.x and 17.x (e.g. 16.7.19 and 17.13.0),
loaded through the
- An intercepting proxy (examples below use mitmproxy).
- A supported Instagram build (see the table below).
| Instagram version | Status | APK |
|---|---|---|
| 433.0.0.47.68 | ✅ Verified | APKMirror |
Tested on Android 15 (arm64).
# 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 &'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.
# 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>:8080Instagram 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).
Spawn the app under Frida with the agent loaded:
frida -U -f com.instagram.android -l scripts/instagram-unpin.jsRun 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.)
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 insteadIt 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 proxyscripts/instagram-unpin.js installs three patches:
- Conscrypt —
TrustManagerImpl.checkTrustedRecursiveis replaced so it returns an empty chain (i.e. "trusted") instead of throwing. - SSLContext —
SSLContext.init(...)is wrapped to substitute a no-opX509TrustManager, so every context the app creates accepts any certificate. - Tigon —
TigonMNSServiceHolder.initHybrid(...)is intercepted and theTigonMNSConfigargument 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.
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 rawdevice.create_script()on Frida 17 (which no longer auto-injects theJavabridge). Load it with thefridaCLI (-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 notigon: matched ...line, the app build may use an overload this script does not yet match — open an issue with the printedunmatched overloadlines. *.googleapis.comhandshake failures — those are Google Play Services, not Instagram; they pin separately and can be ignored.
Released under the MIT License.