About Blog Socials
Technical Android

Pwning Flutter Apps

08/03/26

It is assumed that you already have a bit of knowledge about Android app pentesting.

Flutter apps work quite differently from the usual Android apps, which means pentesting them often requires a different approach.

Background on Flutter

Flutter is an open-source SDK used to write cross-platform apps in Dart. Flutter itself is built on C, C++, Dart and Skia.

Instead of using native UI components, Flutter draws every pixel on the screen using its own rendering engine. This also means Flutter uses its own HTTP client

The architecture of a typical Flutter app looks like this:

Platform AgnosticPlatform SpecificDartC/C++EmbedderRunnerDart AppFrameworkdart:uiEnginePlatformChannelsPlatform-specificAPI Flutter Engine API Embedder API (embedder.h)

The Dart code of the app is compiled ahead-of-time as an AOT snapshot which is then run by the stripped-down Dart VM called precompiled runtime inside the Engine.

The AOT snapshot includes pre-compiled Machine code for most of the original Dart code

The AOT snapshot contains:

  • Dart VM Snapshot - Shared VM heap state (objects/strings)
  • Dart VM Instructions - Shared VM stubs
  • Isolate Snapshot - Per-isolate heap state
  • Isolate Instructions - Main AOT compiled Dart code

P.S The AOT snapshot has both the Dart code and Flutter’s framework code

The files you should be looking at are:

libapp.so     # AOT Snapshot
libflutter.so # Flutter Engine

Going forward it would be best to not use a bundled APK (.XAPK, .APKM) as they are a pain to patch and modify. If the app you are trying to pentest uses bundles, you can use AntiSplit-M to get a single merged APK.

Dynamic Analysis

This would be the easiest method and the one I would recommend you first go for.

Intercepting HTTP traffic

Android DeviceInternetAttacker MachineFlutter AppFrida / reFlutterVPN Based Proxy (TunProxy)Burpsuite(Invisible Proxy)http / dioSSL Pinning Disables SSL Pinning TrafficHTTPS Tunneled Traffic

Most Flutter apps use the http or dio package to make HTTP requests, which means they implement SSL pinning and do not respect system proxy settings.

There are two primary ways to bypass SSL pinning:

  1. Using Frida with the disable-flutter-tls-verification script.
    • The included patterns only work for x86 and x64 builds.
  2. Patching the application using reFlutter.
    • This should work across all architectures and can also print function offsets.

Once you have bypassed SSL pinning, you should proxy the traffic:

  1. If you are using the Android emulator, launch the emulator with the -http-proxy http://<proxy_host>:<proxy_port> flag, or configure it directly in the emulator settings.
  2. If not, you can just use TunProxy or any other VPN-based proxy app.

Invisible proxying is necessary, Flutter just ignores the system proxy settings.

If you are using BurpSuite, you should enable Support invisible proxying.

Static Analysis

All of the app’s logic that was written in Dart can be found in the libapp.so file. As of writing this post, most obfuscation techniques that are available for Flutter are quite primitive (renames class and function names).

As mentioned before, libapp.so is just an ELF binary so you could just run strings on it or use Ghidra, IDA Pro, etc. to analyze it.

Another good tool for analyzing libapp.so is Blutter. As of now it only supports arm64-v8a builds, but it does a solid job of extracting useful info from the binary.

python3 blutter.py path/to/app/lib/arm64-v8a out_dir

Running it will give you a handful of useful outputs:

  • asm/* - libapp assemblies with symbols
  • blutter_frida.js - a Frida script template for the target app
  • objs.txt - complete (nested) dump of Objects from the Object Pool
  • pp.txt - all Dart objects in the Object Pool

It also generates an IDA script that loads function and class names, assuming the app wasn’t obfuscated ofc.

You can also feed the assembly output to a decent LLM and it’ll do a pretty good job of decompiling the logic, though it could get quite expensive.


Credits