/ Adam's Pile of Hacks / blog

Android, Frida and a very strange bug

December 28, 2019

Skill Trees

I’m very good at getting side-tracked, but learning quite a lot while down a particular side track. This time I wanted to investigate how a particular app was working. The rise of HTTPS is fantastic for security and privacy, but terrible if you want to know what’s actually going out of your network.

Instead of starting with the target app itself, I thought I’d build myself a simple Android app that made a connection to a HTTPS-enabled website, then see what I could do to modify it. This is where the idea of skill trees comes in - instead of going straight to learning the skill I need, I end up grabbing a stack of prerequisites, like the Skill Trees found in Civilisation or other RPGs.

Step 1: Hello World / Android Studio

Every time I start Android Studio I’m reminded how much we lost when Visual Basic 6 was discontinued. Getting a reference to a TextView took far longer than it needed to - I couldn’t work out why findViewById couldn’t see the control, until I realised that Android Studio’s template hadn’t added an ID to it, so it was invisible to code.

Putting aside my dislike of Android Studio and the complexity of Fragments and ConstraintLayouts, I got a simple app going. You can grab the whole project at https://gitlab.com/voltagex-experiments/android/httpstest if you want (and I really need other people to test this out - I cannot work out the bug I will describe below).

The code is as simple as I could make it. When a button is tapped, a request is made to https://example.org. The result is shown in a TextView.

fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
                queue.getCache().clear();
                String url ="https://example.org";

                statusText.setText("Button hit, but will it actually do anything?");

                // Request a string response from the provided URL.
                StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                        new Response.Listener<String>() {
                            @Override
                            public void onResponse(String response) {

                                statusText.setText(response);
                            }
                        }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        statusText.setText(error.getLocalizedMessage());
                    }
                });

                queue.add(stringRequest);
            }
        });

Not shown: way too many lines of boilerplate.

Step 2: Who do you trust?

Running normally, I got output like this:

A cropped screenshot of an Android app, displaying some HTML code from example.org

When I then set up the Fiddler proxy to intercept the connection, I got the following error:

HandshakeException: CertPathValidatorException: Trust anchor for certification path not found

This is expected - Fiddler is presenting a different, untrusted certificate to the app. With recent versions of Android, you can’t just grab the Fiddler certificate and install it - Android still won’t trust a “user” certificate. See this Google blog post for more info. The summary is that unless your app has specific configuration, Android Nougat and above won’t trust any user supplied certificates for TLS connections.

Now, because I’m building an app specifically to test this out, I could just add that configuration and be on my way, but what if I’m researching an app that I don’t control?

My options are:

  1. Use an older Android device or operating system with user certs - good until I’m working with an app that only targets newer Android versions

  2. Unpack and repack the app - worth a shot, probably won’t bypass any certificate pinning code

  3. Root my phone and use Magisk, plus https://github.com/NVISO-BE/MagiskTrustUserCerts/releases to turn the user certificates into trusted system certificates

  4. Use Frida and something like the universal SSL pinning bypass script

A quick note on having full control over hardware you bought

I ended up using option 3 and 4, but I can no longer recommend that anyone root phones (at least without an official method). I followed Sony’s instructions and now my Xperia XA 2 a) can never go back to a locked bootloader and b) will never receive updates over-the-air from Sony again. Reading Sony’s information on this, it seems like running fastboot oem unlock will blow a fuse on the motherboard and the only way back is replacing the entire board with a new one. I won’t even go into the potential downgrade in camera quality and features that happens (if you want to look this up - the TA partition is wiped). You gain a lot of points for being one of the very few manufacturers with instructions on how to find the UART on your phones but lose all the points for doing silly things like this.

Step 3: Frida - it should have been simple

Frida is a pretty cool set of tools to hook applications in various ways and observe or modify their behaviour. You run a server alongside the application you want to hook into, and then the Frida client provides a REPL or a scripting interface (in Javascript). One day I’ll forgive the help command responding with Help: #TODO :), but after stumbling around the website a bit and reading some other code examples I had a way forward.

If there’s an easy way or a hard way, I’ll choose the hard way. I spent way too long modifying the script mentioned above to automatically grab Fiddler’s certificate and trust it. My version can be found via [https://codeshare.frida.re/@voltagex/fiddler-4-ssl-pinning-bypass/](Frida Codeshare). There are two problems I’ve been unable to solve so far - one is that SSLContext doesn’t seem to get called by my app and two is that even though it doesn’t get called, something Frida is doing is causing my proxy settings to be ignored. As in, the connection works with Frida attached and the script running. I thought I’d succeeded, until I realised I couldn’t see any traffic from the phone in Fiddler. Luckily, a rooted phone meant I could run tcpdump and work out that the request was happening, just not via my proxy.

Without digging further into the Android operating system, the Volley source code or even how Java’s TLS support works, I won’t be able to make further progress on this. My “hacking” phone is stuck on Android Oreo forever so I can’t check if it’s an OS bug for the moment.

Future research and options

Experience points gained