Skip to main content
  1. Posts/

Android - Intercepting Network Communications

·7 mins
mobile android
Table of Contents

The Flow Chart
#

We need a bit more than just a proxy and a neat interception tool like Burp Suite in order to inspect the traffic of some android applications. Let’s examine this flow chart from hextree.io:

hextree-flowchart

First you want to examine if the AndroidManifest.xml file contains the android:networkSecurityConfig.

For background, the android manifest file for your application is an XML file that describes essential information about your app to the Android operating system. In this case the network security configuration feature lets you customize, as you might guess, the network security settings for the app.

All the granular details are here in the documentation, but we are most concerned with seeing if the application is using a custom certificate, if the config trusts user certificates, if the config trusts system certificates, or if no certificates or trust anchors are being used.

When the setting is missing in the manifest or no certificate/trust anchor is used, check which API version is in use. Anything before API level 23 can be treated the same as if the config trusts user certificates - anything at or above API level 24 will be treated the same as if the config trusts the system certificates.

For the edge cases - if you don’t have root access and/or the application is using a custom cert (or pinning the cert) you can still use dynamic instrumentation to get around the cert and intercept the traffic.

When the app trusts user certificates (<certificates src="user" />) - you should be able to install your own certificate in the Android trust store and then proxy the traffic. Sometimes the app will ignore the proxy being used and you can try using a VPN to intercept the traffic.

When the app trusts system certificates (<certificates src="system" />) - and you have root access, you want to check if the Emulator/Device you are using is above Android 14 or before Android 13. Anything before and including Android 13, you would want to use mount -t tmpfs tmpfs /system/etc/security/cacerts to overlay the tmpfs directory over the system certificates. Then you can copy your user certificate from /data/misc/user/0/cacerts-added/<cert-id> to the system certificate store. This should allow the app to trust your imported cert, then follow the proxy and VPN steps from earlier. If you are using Android 14 or above you can try using HTTP toolkit to install certs.

If you don’t have root access or if the app is using certificate pinning - you will probably need to use dynamic instrumentation to intercept the traffic.

Networking with Android Apps
#

We can practice seeing how networking and making requests might be implemented in an android app by doing it ourselves. First we add the internet permission to our AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET" />

    <application ---SNIP---

Then, we can implement some basic logic in an empty views activity where a button will trigger a web request:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);

        TextView homeText = findViewById(R.id.home_text);
        homeText.setText("Changing Text");

        Button homeButton = findViewById(R.id.home_button);

        homeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ExecutorService executor = Executors.newSingleThreadExecutor();
                executor.execute(() -> {
                    try {
                        URL url = new URL("http://www.android.com/");
                        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                        InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                        StringBuilder sb = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            sb.append(line).append('\n');
                        }
                        String result = sb.toString();
                        runOnUiThread(() -> homeText.setText(result));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        });
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.MainActivity), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
    }
}

But when we run this application and try to send the request we will get an error:

androidnet-1

Android will try and prevent you from making the mistake of allowing unencrypted HTTP traffic - no big deal, we can switch the code to say https and it should work:

androidnet-2

Now sometimes you might have a legitimately good reason to allow HTTP traffic - so you can add the following permission to your manifest under the application section:

android:usesCleartextTraffic="true"

androidnet-3

You might also want to go further and add another file res/xml/network_security_config.xml to separately configure some of these things:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example.com</domain>
        <trust-anchors>
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

You as a developer could also just use TCP sockets directly to effectively bypass the built-in protections, but it still needs the basic internet permissions.

Demonstration with HTB Pinned
#

Let’s walk through the process we might go through when testing a mobile app by practicing on this challenge. First you want to download the APK and look at the included README:

Install this application in an API Level 29 or earlier (i.e. Android 10.0 (Google APIs)).

The emulator I am using through android studio (and don’t want to change at the moment) is using API level 34 and doesn’t have the google APIs in use. So before we can even install this, we need to re-align the APK, make a key, and sign it:

zipalign -p -f -v 4 pinned.apk align-pinned.apk
keytool -genkey -v -keystore research.keystore -alias research_key -keyalg RSA -keysize 2048 -validity 10000
apksigner sign --ks research.keystore align-pinned.apk

Now we should be able to install it:

adb install .\align-pinned.apk

androidnet-4

Now we can begin trying to intercept network traffic. Following the flow chart, let’s begin by using jadx to decompile the app and look at it’s network security configuration.

androidnet-5

We now know that the application has internet access and if we investigate the MainActivity we will see how a certificate is used. It checks if the provided certificate is the same as the pinned certificate:

androidnet-6

Then we can see the certificate being loaded and use this to actually track it down in the code:

androidnet-7

The cert at: /res/raw/certificate.der:

androidnet-8

So we still need to just intercept the traffic - looking at the flow chart we are using a newer version of android and it is using pinning so we will likely need to jump to the last resort of utilizing dynamic instrumentation in combination with HTTP toolkit.

We can use frida to see running processes and list if they are ones we can interact with using Dynamic Instrumentation.

frida-ps -Uia

  PID  Name            Identifier
-----  --------------  ------------------------------
 9732  Calendar        com.android.calendar
11811  Files           com.android.documentsui
11603  HT2048          io.hextree.privacyfriendly2048
12503  HTTP Toolkit    tech.httptoolkit.android.v1
12258  Hidden Secrets  io.hextree.reversingexample
12379  Pinned          com.example.pinned
 9817  Rethink         com.celzero.bravedns
 1199  SIM Toolkit     com.android.stk
11471  Settings        com.android.settings
    -  Camera          com.android.camera2
    -  Clock           com.android.deskclock
    -  Contacts        com.android.contacts
    -  FridaTarget     io.hextree.fridatarget
    -  Gallery         com.android.gallery3d
    -  Messaging       com.android.messaging
    -  Phone           com.android.dialer
    -  PocketHexMaps   io.hextree.pocketmaps
    -  Search          com.android.quicksearchbox
    -  WebView Shell   org.chromium.webview_shell

The above command lists the processes running on our emulator, using -i to only list instrumentable processes and -a to show additional application data.

We see that com.example.pinned is our target here. To bypass the SSL pinning we can use objection which is a part of frida that can make this part of the process easier. In order to do this make sure you have the right version of the frida-server running on the android device or emulator.

#I opted to push it to the tmp directory
adb push frida-server /data/local/tmp

#then get a shell and make it excecutable and run it
adb root
adb shell
cd /data/local/tmp
chmod +x frida-server
./frida.server

Then we can inject objection into the running app:

objection -g com.example.pinned explore

Using USB device `Android Emulator 5554`
Agent injected and responds ok!

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions
com.example.pinned on (Android: 14) [usb] #

We can use android sslpinning disable to, as you may have guessed, disable SSL pinning.

com.example.pinned on (Android: 14) [usb] # android sslpinning disable
(agent) Custom TrustManager ready, overriding SSLContext.init()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.verifyChain()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.checkTrustedRecursive()
(agent) Registering job 443476. Type: android-sslpinning-disable
com.example.pinned on (Android: 14) [usb] # (agent) [443476] Called SSLContext.init(), overriding TrustManager with empty one.

Then on the app we will go ahead and log in and in your traffic inspector (Burp or HTTP Toolkit) we will see the flag:

androidnet-9

So, we kinda jumped past all the would-be conditions to show how this could work so this isn’t a great example. I highly recommend the courses from Hextree.io as they cover each case in detail.

Related

Mobile Application Security Considerations
·3 mins
mobile
I think that because mobile application testing is somewhat of a niche, the security considerations for mobile devices are also less understood.
Android CTF - HTB Pinned
·4 mins
htb
This is an easy-difficulty mobile challenge - and here is the description: This app has stored my credentials and I can only login automatically.
Command Injection - More Techniques
·10 mins
web
Introduction # We&rsquo;ve already learned a decent amount about of introductory information about OS command injection when we were studying for the Burp Suite Certified Practitioner Exam.