Skip to main content
AnalysisPublished

Locating Flutter's TLS certificate verifier in a stripped libflutter.so without byte signatures

Source Reddit NetSec

0

Discussion

This report discusses a method to locate Flutter's TLS certificate verifier function within a stripped libflutter.so binary on ARM64 devices without relying on fragile byte signatures. Flutter bundles its own BoringSSL and does not use Android's network security config, requiring patching of the ssl_crypto_x509_session_verify_cert_chain function to modify trusted CAs. The function can be identified by cross-referencing unique strings and specific ARM64 prologue characteristics, enabling reliable patching across multiple Flutter engine versions. This technique was validated on apps using Flutter engine versions 2.19.2 through 3.11.1. The report also highlights a common pitfall in patching tools that fail to handle split APKs correctly. No active exploits or vendor patches are mentioned.

r/netsec (opens in a new tab)u/magixer0 points0 comments

Author, and I work on the tool this came out of.

Flutter bundles its own BoringSSL and never reads Android's network security config, so repackaging to trust a user CA does nothing. You have to patch ssl_crypto_x509_session_verify_cert_chain, and libflutter.so ships stripped.

The usual answer is a byte signature copied from a build where someone already found it. It breaks on a new compiler, and it breaks silently: search returns nothing, the patched app installs and launches and never decrypts, which looks exactly like the app winning.

Two properties come from the source rather than the compiler, so a rebuild can't move them. The function picks its role from the strings ssl_client and ssl_server, and it is the only one referencing both. Its third argument is an out-pointer for an alert code written on entry, so on arm64 the prologue stores a byte through x2. Cross-reference both strings, walk back to the nearest prologue, confirm the x2 store. At most two candidates every time.

13 apps off one handset, engines 2.19.2 to 3.11.1, found in all 13. The part that convinced me: the two apps on 3.11.1 have byte-identical prologues at different addresses, while three unrelated apps on 3.5.3/3.5.4 all land on 0x6dbef4.

It also caught a bug in our own patcher. 12 of the 13 ship libflutter.so in split_config.arm64_v8a.apk, one ships it in base.apk, and ours only opened the base. Silently did nothing on 12 of 13 real apps, because the one we validated against was the one that couldn't catch it.

arm64 only. Writeup with the disassembly:

References

Community reviews

Loading reviews…