Post

Man-in-the-Middle (MITM)

Man-in-the-Middle (MITM)

Man-in-the-Middle (MITM)

A Man-in-the-Middle (MITM) attack is where an attacker intercepts and potentially manipulates the communication between a client (Android app) and a server. In Android pentesting, MITM is commonly used to analyze the app’s network traffic and discover vulnerabilities such as:

  • Insecure data transmission
  • Lack of SSL/TLS certificate validation
  • Sensitive data exposed in plain text

Why MITM is Important in Android Pentesting

Most Android applications communicate with backend APIs over HTTP(S). If an app doesn’t properly implement SSL/TLS validation or pins certificates incorrectly, a pentester can intercept and analyze traffic to find sensitive information, such as:

  • API keys
  • User credentials
  • Personally identifiable information (PII)
  • Session tokens

Setting Up MITM Proxy

Make sure your Linux system is online. This can be via:

  • Ethernet (preferred): leaves your Wi-Fi card free for hotspot.

Create a Wi-Fi Hotspot

You’ll share your Linux machine’s internet with the Android.

Option A: Use hostapd + dnsmasq (more control)

Install tools:

1
sudo apt install hostapd dnsmasq iptables

Configure hostapd.conf:

1
2
3
4
5
6
7
8
9
10
11
interface=wlan0
driver=nl80211
ssid=PentestHotspot
hw_mode=g
channel=6
auth_algs=1
wpa=2
wpa_passphrase=YourStrongPassword
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

Configure dnsmasq.conf:

1
2
interface=wlan0
dhcp-range=192.168.150.10,192.168.150.100,12h

Enable IP forwarding:

1
echo 1 > /proc/sys/net/ipv4/ip_forward

Set up NAT:

1
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Start the hotspot:

1
2
sudo hostapd /etc/hostapd/hostapd.conf
sudo dnsmasq -C /etc/dnsmasq.conf

Your Android can now connect to PentestHotspot.

Option B: Use NetworkManager GUI (easier, less control)

  • Open Settings > Wi-Fi > Wi-Fi Hotspot.
  • Configure SSID, password, etc.
  • Enable Internet Sharing.
  • Skip hostapd, dnsmasq — NetworkManager handles it all.

Intercept the Traffic

Once your Android is connected:

For HTTP/HTTPS traffic:

  • Run Burp Suite or mitmproxy on Linux.
  • Configure your proxy (e.g., Burp at 192.168.150.1:8080).
  • Install Burp’s CA cert on Android (as a user CA).
  • Use Frida/Objection or Magisk to bypass certificate pinning.

For all traffic (TCP/UDP):

  • Use Wireshark or tcpdump on wlan0 to sniff packets.
  • For redirection: use iptables or bettercap for full MITM.

Example for redirecting HTTP:

1
iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 80 -j REDIRECT --to-port 8080

I created Hotspot in my Ubuntu Machine. It is working and Android device is connected through Linux attacker machine. I’m acting as a gateway (IP: 10.42.0.1), and Android device is on 10.42.0.68. Now, I can capture, inspect, and forward all traffic from the Android to the internet, then relay responses back.

Step 1: Enable IP Forwarding (allows routing)

1
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward

To make it persistent:

1
sudo sysctl -w net.ipv4.ip_forward=1

Step 2: Set Up NAT with iptables

This lets your machine masquerade as the Android device when sending packets to the internet:

Assuming wlp0s20f3 is the hotspot interface and eth0 or wlan0 is your internet interface:

1
2
# Replace eth0 with your actual internet-facing interface
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Verify the interface used for internet with:

1
ip route

Step 3: Set Up Interception (Choose One)

Intercept traffic on wlp0s20f3 with a proxy tool.

For HTTP/HTTPS traffic (using Burp or mitmproxy)

Option 1: Configure Proxy on Android

Run Burp or mitmproxy on your attacker machine:

1
mitmproxy -i wlp0s20f3 -p 8080

On Android:

  • Go to Wi-Fi > Modify network > Proxy: Manual
  • Set proxy IP: 10.42.0.1, port: 8080

Install mitmproxy or Burp CA certificate on Android.

Option 2: Transparent Proxy (No proxy setting on Android)

This is more advanced, but automatic and powerful.

Redirect all HTTP/HTTPS from Android to proxy:

1
2
3
4
5
6
7
8
9
# Redirect HTTP (port 80)
sudo iptables -t nat -A PREROUTING -i wlp0s20f3 -s 10.42.0.68 -p tcp --dport 80 -j REDIRECT --to-port 8080

# Redirect HTTPS (port 443) if you're using mitmproxy/Burp with SSL support
sudo iptables -t nat -A PREROUTING -i wlp0s20f3 -s 10.42.0.68 -p tcp --dport 443 -j REDIRECT --to-port 8080


# OR you can use the following simple command
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.42.0.1:8080

Use the following command to display all rules in the nat table, including your REDIRECT rules:

1
2
REDIRECT   tcp  --  10.42.0.68  0.0.0.0/0  tcp dpt:80  redir ports 8080
REDIRECT   tcp  --  10.42.0.68  0.0.0.0/0  tcp dpt:443 redir ports 8080

You can verify by listening on 8080 in nc.

Once verified, then run mitmproxy or Burp in transparent mode.

1
sudo mitmproxy --mode transparent --showhost -p 8080

⚠️ Burp Suite requires configuration for transparent proxying. Select Invisible Mode into its Proxy Configuration and set proper interface. Mitmproxy works directly.

Add a New Proxy Listener in Burp

  1. Open Burp Suite
  2. Go to Proxy > Options > Proxy Listeners
  3. Click Add
  4. Set:
    • Bind to address: 10.42.0.1 (or All interfaces)
    • Port: 8080 (or any free port)
  5. Click Request Handling
  6. Check: “Support invisible proxying”

Install Burp CA Certificate on Android

  1. Export the Burp certificate: Proxy > Options > Import/Export CA Cert
  2. Copy it to your Android and install it:
    • Settings > Security > Install from storage
    • CA will only be used for user-installed apps unless rooted.

For apps with SSL pinning, you’ll need Frida, Objection, or Magisk module like TrustMeAlready.

When generating CA Certificate you’ll now see several options:

OptionUse Case
Certificate in DER format (recommended)Best for Android devices
Certificate in PEM formatFor tools like curl, Python, Linux CLI
Certificate and private key in PKCS#12 (.p12) formatFor browsers or some OS installs
Java Key StoreFor Java-based environments or JDK

Pick: Certificate in DER format (.cer)

Save it as:

burp-ca-cert.cer
  • Copy burp-ca-cert.cer to your phone via USB, email, or any cloud method
  • On Android:
    • Go to Settings > Security > Encryption & Credentials
    • Tap Install from storage
    • Choose burp-ca-cert.cer
    • Name it something like: Burp Suite CA

Done! Burp can now intercept and decrypt HTTPS traffic for user-installed apps.

To capture Server Response also in Burp Suite:

Go to Proxy -> Proxy Settings -> Scroll Down -> Response Interception Rules -> Check Intercept responses based...

Then Add a new rule:

Boolean Operator - OR
Match Type - Any Header
Match relationship - In response Matches
Match Condition - .*

What Is SSL Pinning?

SSL/TLS pinning is a technique where an app trusts only a specific certificate or public key, ignoring the system’s trusted CA store — including your Burp or mitmproxy CA.

So even if you install your CA certificate:

  • The app won’t trust it
  • SSL connections will fail
  • You’ll see errors like:

    • SSLHandshakeException
    • Connection reset
    • Peer not authenticated
    • Or nothing at all (silent failure)

Why is it used?

  • Prevents interception and modification of HTTPS traffic using tools like Burp Suite or mitmproxy.
  • Protects sensitive data like credentials, tokens, etc., from being leaked via MITM.
  • Common in banking, fintech, or health apps.

How SSL Pinning Works:

  • During HTTPS connection, the app compares the server certificate (or public key) with a hardcoded copy.
  • If it matches → connection is established.
  • If it doesn’t match → connection is dropped (even if the cert is valid via OS).

Types of Pinning:

  1. Certificate Pinning: App stores the full server certificate.
  2. Hash Pinning: App stores a hash (SHA-256) of the certificate or public key.
  3. Public Key Pinning: App stores only the public key (more robust across cert renewals).

Bypassing SSL Pinning (for Pentesting):

  1. Frida (Dynamic Instrumentation)
  2. Objection (Frida Wrapper)
  3. Xposed Modules
  4. Patching APK
    • Use apktool to decompile the app.
    • Modify or hook the SSL functions in Smali code.
    • Recompile and resign the APK.

Notes:

  • SSL pinning bypass is legally restricted to security assessments with permission.
  • Android 7+ enforces network security configuration, check network_security_config.xml.

Let’s bypass Certificate Hash Pinning. For this demo use certificatePinning_hash.apk

First find out in Wireshark which port the App is using to send the traffic.

The app is using port 443.

Certificate Pinning - Patching Fingerprint

SSL Pinning is a security mechanism where the client (app) validates the server’s certificate against a known copy (or fingerprint) stored within the app. This prevents Man-in-the-Middle (MITM) attacks.

We can use frida for Runtime Patching:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Bypass certificate pinning for common libraries
Java.perform(function() {
    // OkHttp
    var OkHttpClient = Java.use('okhttp3.OkHttpClient$Builder');
    OkHttpClient.build.overload().implementation = function() {
        return this.build();
    };
    
    // Android WebViewClient
    var WebViewClient = Java.use('android.webkit.WebViewClient');
    WebViewClient.onReceivedSslError.implementation = function(view, handler, error) {
        handler.proceed();
    };
});

Also, Objection Framework

1
2
objection --gadget [package.name] explore
android sslpinning disable

Xposed Modules

Modules like:

  • JustTrustMe
  • SSLUnpinning
  • TrustMeAlready

Manual APK Modification

  1. Decompile APK using apktool/jadx
  2. Locate certificate checks (look for .pem, .cer files or hash strings)
  3. Modify smali/Java code to bypass checks
  4. Rebuild and sign APK

Just a Hack! You can modify your getprop properties:

1
2
3
4
5
6
Java.perform(function() {
    var Build = Java.use('android.os.Build');
    Build.FINGERPRINT.value = "generic/generic/generic:10/QQ1A.200105.002/1234567:user/release-keys";
    Build.MODEL.value = "Pixel 3";
    Build.BRAND.value = "google";
});

Convert Burp cert to PEM if needed:

1
2
3
4
# Convert .der to PEM
openssl x509 -inform DER -in burp.der -out burp.pem
# Extract the public key to .pub
openssl x509 -pubkey -noout -in burp.pem > burp.pub

Now burp.pub contains just the RSA public key in PEM format.

Get the public key’s SHA-256 hash (in pinning format):

1
2
openssl pkey -pubin -inform PEM -in burp.pub -outform DER |
openssl dgst -sha256 -binary | base64

This outputs something like:

abcde12345...=   ← use this as the new pin

Let’s say it gives:

In your NetworkAPI Smali file (e.g., MainActivity$NetworkAPI.smali), find lines like:

1
const-string v1, "sha256/mM294xslEgmvDADAxWWH2DeH4/bNgPBpgZvd7SfciuA="

Replace with:

1
const-string v1, "sha256/AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcd="

Do this for each add() call (you can use the same Burp hash multiple times, or just keep one and remove others).

Optional: You can also only add Burp’s hash and remove all others. But I generally don’t recommend this.

.pem → full certificate

A .pem file contains the whole X.509 certificate, including the public key and metadata (issuer, validity, etc.).

.pub → only public key

Some guides extract just the public key to a .pub file and then hash that, mimicking what apps do internally when calculating the cert pin.

On Android 7+ (Nougat), user-installed CAs are not trusted by apps using network_security_config.xml. You need:

  • Root the phone OR
  • Use Frida to bypass at runtime OR
  • Patch the app to remove SSL pinning

Decompile the APK

1
apktool d your-app.apk -o myapp/

Look for Pinning Code

  • Search for typical SSL pinning mechanisms in the decompiled Smali or Java (use jadx):
  • TrustManager
  • HostnameVerifier
  • checkServerTrusted
  • verify methods
  • Libraries like OkHttp, TrustKit, CertificatePinner

We need to remove Pins from network_security_config.xml

res/xml/network_security_config.xml Replace with a non-pinning configuration that allows user-installed certs:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" /> <!-- allows Burp cert -->
        </trust-anchors>
    </base-config>
</network-security-config>

Rebuild the APK

1
apktool b app_unpacked -o app_patched.apk

Sign the APK

Generate a debug keystore if you don’t already have one:

1
keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 10000

Sign it:

1
2
3
4
5
6
7
jarsigner -verbose -keystore debug.keystore -storepass android -keypass android app_patched.apk androiddebugkey

# OR
apksigner sign --ks debug.keystore --ks-pass pass:android --key-pass pass:android --ks-key-alias androiddebugkey app_patched.apk

# Verify
apksigner verify app_patched_signed.apk

zipalign is an archive alignment tool for Android APK files that ensures all uncompressed data in the APK, such as images and raw files, starts at a specific byte alignment relative to the start of the file—typically on 4-byte boundaries. This alignment allows the Android runtime to access these files directly with mmap(), reducing RAM usage and improving performance.

1
2
3
4
5
# Align
zipalign -f -v 4 infile.apk outfile.apk
# (Use `-P 16` if your APK contains `.so` files.)
# Check Alignment
zipalign -c -v 4 existing.apk

Certificate Pinning - Patching Certificate

In this method, the app doesn’t use a certificate hash, but instead loads a bundled certificate file and compares the server’s certificate chain against it. So rather than patching Smali or replacing pins, you:

Bypass SSL Pinning by Replacing the App’s Pinned .der File

Apps often load a pinned cert like this:

1
2
3
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(context.getAssets().open("cert.der"));
Certificate ca = cf.generateCertificate(caInput);

Then build a TrustManager or SSLSocketFactory using it.

Or it can store cert.der in assets/raw/cert.der and store it in network_security_config.xml as @raw/cert.der. The Java code will refer to this file using R.raw.cert so search for this string in Java code.

If the app loads cert.der from assets (or res/raw), then:

Replace that file with your own cert (Burp’s .der file), so the app will validate the proxy (Burp) instead of the real server.

How to Know If This Approach Is Used

Look for code like this in Java (jadx is helpful):

1
2
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getAssets().open("cert.der");

OR

1
2
Resources res = context.getResources();
InputStream in = res.openRawResource(R.raw.cert);
This post is licensed under CC BY 4.0 by the author.