Skip to content

Instantly share code, notes, and snippets.

@unoexperto
Last active March 28, 2024 08:49
Show Gist options
  • Save unoexperto/80694ccaed6dadc304ad5b8196cbbd2c to your computer and use it in GitHub Desktop.
Save unoexperto/80694ccaed6dadc304ad5b8196cbbd2c to your computer and use it in GitHub Desktop.
How to patch Android app to sniff its HTTPS traffic with self-signed certificate

How to patch Android app to sniff its HTTPS traffic with self-signed certificate

  • Download apktool from https://ibotpeaches.github.io/Apktool/
  • Unpack apk file: java -jar /home/expert/work/tools/apktool.jar d net.flixster.android-9.1.3@APK4Fun.com.apk
  • Modify AndroidManifest.xml by adding android:networkSecurityConfig="@xml/network_security_config" attribute to application element.
  • Create file /res/xml/network_security_config.xml with following content:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>
  • Build patched apk: java -jar /home/expert/work/tools/apktool.jar b flixster -o flixster_patched.apk
  • If you see error 'android:localeConfig in Manifest' see iBotPeaches/Apktool#2756 (comment)
  • If you see followint error try running java -jar /home/expert/work/tools/apktool.jar empty-framework-dir --force or run b command with parameter --use-aapt2
W: invalid resource directory name: /home/expert/Downloads/Zzzzzz/Zzzzzz_v0.0.0/res navigation
brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1): [/tmp/brut_util_Jar_5815054990385134498.tmp, p, --forced-package-id, 127, --min-sdk-version, 23, --target-sdk-version, 29, --version-code, 226000400, --version-name, 226.000.0, --no-version-vectors, -F, /tmp/APKTOOL14466004687895005947.tmp, -e, /tmp/APKTOOL4388243966604401097.tmp, -0, arsc, -I, /home/expert/.local/share/apktool/framework/1.apk, -S, /home/expert/Downloads/Zzzzzz/Zzzzzz_v0.0.0/res, -M, /home/expert/Downloads/Zzzzzz/Zzzzzz_v0.0.0/AndroidManifest.xml]
  • Generate keys to sign apk: keytool -genkey -alias keys -keystore keys -keyalg RSA -keysize 2048 -validity 10000 # password
  • Sign apk file: jarsigner -verbose -keystore keys /home/expert/Downloads/lancet/flixster_patched.apk keys
  • Old method of signing with jarsigner produces apk that new version of Android refuses to install. Please use: java -jar uber-apk-signer.jar --apks /path/to/apks from here.
  • If necessary convert apk to jar for further analysis: d2j-dex2jar.sh net.flixster.android-9.1.3@APK4Fun.com.apk
  • To find what cyphers suites are supported by remote server calls: nmap --script ssl-enum-ciphers -p 443 youtubei.googleapis.com or sslscan youtubei.googleapis.com
  • To check what cypher suites your client supports query https://www.howsmyssl.com/a/check

Problem with Flutter apps

Problem with Flutter app is: it won’t trust a user installed root cert. This a problem for pentesting, and someone made a note on how to patch the binary (either directly or using Frida) to workaround this problem. Quoting TLDR of this blog post:

  • Flutter uses Dart, which doesn’t use the system CA store
  • Dart uses a list of CA’s that’s compiled into the application
  • Dart is not proxy aware on Android, so use ProxyDroid with iptables
  • Hook the session_verify_cert_chain function in x509.cc to disable chain validation

By recompiling the Flutter engine, this can be done easily. We just modify the source code as-is (third_party/boringssl/src/ssl/handshake.cc), without needing to find assembly byte patterns in the compiled code.

Luckily we don't have to do it manually. You can make Flutter app trust any certificate by doing the following:

  • install adb
/home/expert/work/tools/android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=/home/expert/work/tools/android-sdk/ --update        
/home/expert/work/tools/android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=/home/expert/work/tools/android-sdk/ --list          
/home/expert/work/tools/android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=/home/expert/work/tools/android-sdk/ "platform-tools"
export ANDROID_HOME=/home/expert/work/tools/android-sdk
export PATH="$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$PATH"
  • connect your android device and check its platform by calling adb shell getprop ro.product.cpu.abi. For me, it's arm64-v8a;
  • download Frida gadget frida-gadget-VERSION-android-ARCH.so.xz for your platform from here;
  • unpack your apk. you should see /lib/arm64-v8a/libapp.so and /lib/arm64-v8a/libflutter.so;
  • unpack frida gadget file under name /lib/arm64-v8a/libfrida-gadget.so;
  • now you need to force Android app to initialize this library on start. Find entry point activity. It's usually somewhere like /smali/com/company/product/app/MainActivity.smali;
  • typically you'll see constructor like this
.method public constructor <init>()V
    .locals 0

    invoke-direct {p0}, Lio/flutter/embedding/android/e;-><init>()V

    return-void
.end method
  • add two new strings at the end of the method
.method public constructor <init>()V
    .locals 0

    invoke-direct {p0}, Lio/flutter/embedding/android/e;-><init>()V

    const-string v0, "frida-gadget"
    
    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

    return-void
.end method
  • Repack and sign the .apk as always;
  • Install it via adb by calling adb install ./apks/patched-aligned-debugSigned.apk;
  • Launch the app on the phone and make sure it's working;
  • Install Frida tools via pip install frida-tools. In my case it was installed into /home/USERNAME/.local/bin/;
  • Make sure frida is working by calling frida-ps -Uai. Note name of your app in the output;
  • Test that frida gadget is initialized by your Android app by trying to trace it via frida-trace -U -i open APP_NAME;
  • Checkout sources of https://github.com/NVISOsecurity/disable-flutter-tls-verification
  • Launch TLS patcher via frida -U -l disable-flutter-tls.js APP_NAME;
  • From this moment you should be able to see HTTPS traffic in your sniffer app (Charles or mitm).

Decompiling Dart/Flutter apps is pretty hard right now. So if Android app uses tricky signature generation code for its API calls your only option is decompiling libapp.so using tools like IDA Pro. I tried using Doldrums but looks like it needs update for every Dart release (version hashes are here).

Random articles about reverse engineering Flutter apps:

@txtsd
Copy link

txtsd commented Jun 9, 2021

Thanks for these instructions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment