# K!ll Fl!utter: deriving a Flutter TLS-pinning bypass from the binary instead of a hash database

> K!ll Fl!utter is an MIT-licensed Flutter SSL-pinning bypass for authorized mobile testing. Instead of matching known engine hashes, it locates the verification function from BoringSSL string anchors, so it targets Flutter versions no database has seen.

**f3rb123/kill_flutter** — Flutter SSL Pinning Bypass Tool for Android & iOS — works on any Flutter version without pattern databases

- Repository: https://github.com/f3rb123/kill_flutter
- Stars: 313 · Forks: 52
- Language: Python
- License: MIT
- Published: 2026-09-18 · Updated: 2026-09-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/f3rb123-kill-flutter

## The specific problem: Flutter hides TLS below the layers testers hook

Intercepting a mobile app's HTTPS traffic during an authorized penetration test is routine until the app is built with Flutter. The README explains why. Flutter bundles its own network stack, BoringSSL, compiled into a native binary, `libflutter.so` on Android and `Flutter.framework/Flutter` on iOS. Android Network Security Config is ignored, Java-level SSL hooks such as OkHttp do not exist, iOS App Transport Security is bypassed, system proxy settings are not respected, and standard tools like Objection and SSL Kill Switch hook a layer Flutter never uses.

The intended user is narrow and clearly stated: an authorized penetration tester who needs to see the traffic of a Flutter app they are permitted to assess. The banner reads "For authorized penetration testing only," and the tool's whole shape, patching a specific certificate-verification function, only makes sense inside that permission.

The README is also honest that this is a hard problem even the incumbents handle poorly. reFlutter, which it calls the most popular Flutter-specific bypass, relies on a hardcoded database of known Flutter engine hashes, so an app built on a Flutter version outside that database is not patched correctly. That gap is the reason this project exists.

## String anchors and prologue walkback

The technique replaces a lookup table with analysis of the binary in front of you. The README's insight is that whatever the Flutter version or compiler, BoringSSL's verification function always references two fixed strings, `ssl_client` and `ssl_server`, hardcoded in BoringSSL's open source and never changing. Those strings are permanent landmarks inside any Flutter binary.

From there the tool detects the architecture from the ELF or Mach-O header and routes to the matching decoder, because different CPUs load a string's address differently. The README's own summary of the pipeline is compact:

```text
APK/IPA
  Pick ABI (--arch, or auto-select arm64-v8a)
    Extract Flutter binary
      Detect arch from ELF/Mach-O header, route to engine
        Find ssl_client + ssl_server string anchors
          Scan arch-specific string references
            Walk back to the function prologue
              Offset found, Frida script generated
                iptables DNAT sends traffic to Burp
```

On arm64 it reads the `ADRP + ADD` instruction pair; on x86_64 it decodes a RIP-relative `LEA` via Capstone; on armeabi-v7a it handles Thumb-2 `MOVW/MOVT`. Having pinned the function body by finding the code that references both landmarks, it walks back to the architecture-defined prologue (`STP x29,x30` on arm64, `endbr64` or `push rbp` on x86_64) to find the function start. The generated Frida script then intercepts `ssl_crypto_x509_session_verify_cert_chain` and forces it to return success.

## Why the ABI you run against has to match the offset

A subtlety the README stresses is that Android ships native libraries per ABI but maps only the one matching the device at runtime. So the derived offset must come from the same ABI you will actually run Frida against, or the hook lands at the wrong address.

Installation is a clone plus two pip packages, the second optional:

```bash
git clone https://github.com/f3rb/kill_flutter
cd kill_flutter
pip install frida-tools
pip install capstone
```

The README notes `capstone` is only needed for `x86_64` and `armeabi-v7a` targets, because the `arm64-v8a` and iOS paths are dependency-free. A first run starts by listing what an APK actually contains, then targets the right ABI:

```bash
python3 kill_flutter.py app.apk --list-arch
python3 kill_flutter.py app.apk -i 192.168.1.10 -p 8080
```

The first command reports the bundled ABIs; the second defaults to `arm64-v8a` and points the app at Burp at the given IP and port. For an Intel or AMD emulator you pass `--arch x86_64`, and for iOS you run against an `app.ipa` with `--device-ip` set. Because Flutter ignores system proxy settings, the README explains the tool installs iptables DNAT rules to redirect TCP 443 and 80 traffic to Burp at the kernel level rather than relying on a proxy the app would ignore.

## What it explicitly cannot bypass

The README is unusually clear about the tool's boundary, which is the most useful thing a security tool can document. It bypasses default Flutter `HttpClient` certificate validation, `dio` package SSL pinning and custom validators, anything that ultimately calls `ssl_crypto_x509_session_verify_cert_chain`. It does not bypass mutual TLS where the server requires a client certificate, native Android or iOS certificate pinning implemented outside Flutter, or root and jailbreak detection, which the README calls a separate problem.

That list is a real limitation and a design consequence. The whole method hinges on one BoringSSL function, so a pinning scheme that never routes through that function is invisible to it. An app that adds mTLS, or pins outside the Flutter stack, will defeat this tool completely, and the README does not pretend otherwise.

The operational requirements are their own constraint. The README requires a rooted Android or jailbroken iOS device for Frida and iptables, plus `aapt` from the Android SDK build tools for package-name detection and Burp Suite. Very old `x86` images are detected but not statically resolved, so those fall back to `x86_64` or dynamic Frida.

## Against reFlutter, the database-driven incumbent

reFlutter is the alternative the README picks by name, and the difference in approach is the whole argument. reFlutter patches an APK using a database of known Flutter engine hashes. When the app's engine is in the database, that works. When it is a newer or unusual build, the database misses and the patch is wrong.

K!ll Fl!utter derives the offset from the binary every time, so a Flutter version nobody has catalogued is handled the same as a common one, provided the ABI is one of the supported three. The trade is in failure mode. A hash-database tool either recognises a build or cleanly does not. A derivation tool depends on the binary matching its structural assumptions, the two landmark strings present, a decodable reference, a standard prologue, so it can misfire on an unusually compiled or obfuscated binary in ways that are harder to predict than a simple database miss. For the common case of an ordinary Flutter release outside a hash list, the derivation approach is the one that actually returns interceptable traffic, which is why it is worth having alongside reFlutter rather than instead of it.

## A single-script tool: what that means for trust and upgrades

The repository is deliberately small. Its tree is `kill_flutter.py`, a `LICENSE`, a banner and two help images. There is no package to install, no plugin system and no server, just one Python script you read before you run. For a tool that generates code to disable certificate verification and rewrites kernel routing tables, that readability is a feature: you can audit the entire behaviour in one file before pointing it at anything.

The flip side is that maintenance rests on one script and one author. There is no release cadence to track and no dependency surface beyond `frida-tools` and the optional `capstone`, so upgrades are just pulling a newer script. But the technique is coupled to facts that could change: the README leans on the two BoringSSL strings being permanent and on the per-ABI prologues being fixed. Those are stable today. If a future Flutter engine changed how BoringSSL is compiled in, the derivation would need updating, and a single-maintainer script is only as current as its last commit.

The MIT license makes it fully reusable and adaptable, which suits a tool that testers will want to fork for their own harnesses. The README frames v3.0.0 as the point where the architecture-aware, multi-ABI engine arrived, so anyone relying on x86_64 or armeabi-v7a support should confirm they are on that version or later.

## Conclusion

Reach for K!ll Fl!utter during an authorized Flutter assessment when reFlutter's hash database misses the app's engine version and you need traffic on arm64, x86_64 or armeabi-v7a: deriving the offset from BoringSSL string anchors is what gets you past a build nobody has catalogued. Do not expect it to defeat mutual TLS, native pinning outside Flutter, or root and jailbreak detection, which the README lists as out of scope. Before running, confirm you are on v3.0.0 or later for multi-ABI support, use --list-arch to match the ABI you will run Frida against, and remember it needs a rooted or jailbroken device plus Burp.

## FAQ

### Which SSL pinning does K!ll Fl!utter bypass?

The README says it handles default Flutter HttpClient validation, dio package pinning and custom validators, anything that calls ssl_crypto_x509_session_verify_cert_chain. It does not bypass mutual TLS, native pinning outside Flutter, or root and jailbreak detection.

### Why does K!ll Fl!utter work on Flutter versions reFlutter cannot?

reFlutter relies on a database of known Flutter engine hashes and misses builds outside it. K!ll Fl!utter derives the hook offset from BoringSSL string anchors in the binary itself, so it does not depend on a known-version list.

### What does K!ll Fl!utter require to run?

Python 3 with frida-tools, plus capstone only for x86_64 and armeabi-v7a targets. It also needs a rooted Android or jailbroken iOS device for Frida and iptables, aapt for package detection, and Burp Suite.

## Sources

- [f3rb123/kill_flutter on GitHub](https://github.com/f3rb123/kill_flutter)
- [Issues](https://github.com/f3rb123/kill_flutter/issues)
- [License: MIT](https://github.com/f3rb123/kill_flutter/blob/main/LICENSE)
- [README](https://github.com/f3rb123/kill_flutter/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/f3rb123-kill-flutter
