Recently I had an opportunity to perform a penetration test of a mobile application created in Flutter - a relatively new development framework created by Google. Flutter mobile applications are written in Dart. In this post I'll share some notes on the first steps I took when testing this application on Android:
If you want to try it yourself, I created a sample Flutter app available here
Because setting network proxy is not sufficient in this case, instead I set my Mac (IP 192.168.1.4) as a gateway for Android phone (IP 192.168.1.8). On the Mac:
$ sudo sysctl -w net.inet.ip.forwarding=1
Image Not Showing Possible ReasonsLearn More โ
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
On the phone:
Image Not Showing Possible ReasonsLearn More โ
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Now all the traffic goes through the intercepting Mac and is ready to be analysed with Wireshark/tcpdump/whatever. Based on analysis of the traffic generated by the Flutter application, let's redirect some of it to Burp Proxy.
Once the communication between the application and its server-side API is passing through the intercepting machine, it's easy to redirect specific connections to Burp:
$ cat pfctl.txt
rdr pass inet proto tcp from 192.168.1.8 to runic.pl port 443 -> 127.0.0.1 port 8443$ sudo pfctl -f pfctl.txt
$ sudo pfctl -s nat
rdr pass inet proto tcp from 192.168.1.8 to 37.187.60.243 port = 443 -> 127.0.0.1 port 8443
This redirects https traffic sent to runic.pl to Burp Proxy listening on 127.0.0.1:8443. In Proxy options, let's create a listener on port 8443 supporting invisible proxying:
Image Not Showing Possible ReasonsLearn More โ
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Of course the application doesn't accept Burp Proxy SSL certificate as valid yet. There is a good post on fixing this in Android 7.0+ at blog.ropnop.com - I won't repeat it here. You have to use the first method (installing Burp Proxy certificate as a system-level CA), because http.dart in Flutter ignores Network Security Config. This means a rooted device is needed.
Once this is done, all is ready to start testing HTTP trafficโฆ unless there is root detection in place.
There are currently three root/jailbreak detection packages available for Flutter:
In the test app above I used the first one. After using dex2jar on the test app's APK and loading the generated jar file in JD-GUI, it's quite easy to find the class responsible for root detection on Android:
Image Not Showing Possible ReasonsLearn More โ
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
I used Frida to overload isRooted() method. The script is short:
console.log("Loading flutter_jailbreak_detection bypass...");
setImmediate(function() {
Java.perform(function () {
var root = Java.use("com.scottyab.rootbeer.RootBeer");
root.isRooted.overload().implementation = function() {
console.log("isRooted() called, returning false");
return false;
}
});
});
Once frida-server is running on Android device, let's run the script:
$ frida -U -f pl.runic.ip -l frida-flutter-jailbreak-detection-bypass.js
Image Not Showing Possible ReasonsLearn More โ
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
That's it - the app runs on a rooted device and the traffic passes through Burp - let's start testing!
If it helps you in any way, the Frida scripts for flutter_jailbreak_detection and root_checker bypasses are on Github:
I didn't check trust_fall plugin, but it uses RootBeer as the first one above, so I guess the script should work too.
2019-06-08 @runicpl