Android 7 Cellular MiTM
Brad Dixon - Tue, 24 Oct 2017

Performing security assessments of complex systems sometimes requires some technical gymnastics to "man-in-the-middle" (MITM) communications between components. MITM techniques are essential for observing and manipulating communications in ways that a developer may not have anticipated. As system defenses improve the task of setting up a MITM environment for a system becomes more difficult.

A recent assessment was a bit more challenging to MITM. This system required the critical authentication steps to be sent over the cellular network. Usually we will MITM cellular devices over WiFi but enabling WiFi resulted in Android sending _all_ communications over WiFi including the critical authentication step.

The solution is based on:

  • BURP for inspecting and manipulating traffic. Eventually I wrote a functioning exploit using mitmproxy which is perfect for Python scripted traffic manipulation.
  • SSH (shown in orange) to function as a tunnel to BURP and a SOCKS proxy for traffic leaving BURP.
  • ADB (shown in blue) to provide a non-WiFi connection between my Mac and the target device. ADB carried SSH traffic over a "forward" tunnel to the device.
  • iptables scripts on the target device to send packets into this contraption as required.

Create a Shorter Duration BURP CA Certificate

The first task was convincing Android 7 to trust the BURP CA Certificate. The standard CA Certificate has an expiration date so far in the future that Android simple rejects the certificate. You must make a new certificate, add it to Burp, and then add it to Android.

Use this script to make the cert:

#!/usr/bin/env sh
export KEYNAME=burp_short

# Make private key
openssl genrsa -out $KEYNAME.key 2048

# Make certificate
openssl req -x509 -new -nodes -key $KEYNAME.key -sha256 -days 180 -out $KEYNAME.pem

# Combine both for mitmproxy
cat $KEYNAME.pem $KEYNAME.key > mitmproxy-ca.pem    

# Make der for import to Mac
openssl x509 -outform der -in $KEYNAME.pem -out $KEYNAME.der

# Make PKCS#12 file for burp import.
echo ""
echo "You must have a password or else burp will not import it."
echo ""
openssl pkcs12 -export -out $KEYNAME.pkcs12 -inkey $KEYNAME.key -in $KEYNAME.pem 

# openssl version` < 1.0:
export CERTFP=`openssl x509 -inform PEM -subject_hash -in $KEYNAME.pem | head -1`
# else
# openssl x509 -inform PEM -subject_hash_old -in $KEYNAME.pem | head -1

cp $KEYNAME.pem $CERTFP.0

echo "Install this certificate to Android: $CERTFP.0"
echo "Import this certificate+private key to Burp: $KEYNAME.pkcs12"

Configure BURP

Setup BURP (or whatever attack proxy you prefer) and then:

  1. Enable transparent proxying. This is required because our iptables rules rudely shove packets at BURP without establishing a proxy connection. A proxy connection would explicitly inform BURP where the connection is intended to go. Without this hint BURP has to infer this from parsing the HTTP request.
  2. Import your new shorter duration CA certificate.
  3. In Project Options -> Connections configure BURP use localhost:37760 as the upstream SOCKS proxy. This sends traffic from BURP back over ADB+SSH and then out over the cellular network.

Add New CA Certificate to Device

You'll need root access on your target device.

  1. adb push $KEYNAME.0 /sdcard/
  2. On device, as root:
  3. mount -o rw,remount /system
  4. mv /sdcard/$KEYNAME.0 /system/etc/security/cacerts/
  5. chown root:root $KEYNAME.0 (from dir)
  6. chmod 644 $KEYNAME.0
  7. ls -lZ to make sure SELinux context is correct
  8. Reboot device

Install Termux and SSH on Device

You'll need a real SSH daemon on the device so that you have a reliable SOCKS proxy. There are various apps that provide proxy services on Android but I've found good old SSH to be the most reliable. You can easily install it using Termux.

  1. Install Termux
  2. Start Termux and then install ssh: pkg install sshd
  3. Start SSH: sshd in the Termux shell
  4. You need to install your SSH key in the Termux user directory:
    • adb shell
    • cd /data/data/com.termux/files/home/
    • Make a .ssh directory
    • Copy your public ssh key to your clipboard
    • cat > .ssh/authorized_keys then paste your clipboard. Now Control-D to terminate cat.
    • Fix permissions on the .ssh/authorized_keys file so SSH is happy.

Check:

drwx------ 2 u0_a116 u0_a116 4096 2017-10-10 10:35 .ssh
-rw------- 1 u0_a116 u0_a116 404 2017-10-10 10:45 authorized_keys

At this point if your device is on WiFi you can test the SSH connection:

ssh -i $SSH_PRIVATE_KEY -p 8022 $DEVICE_IP

It does not matter what user account. You'll always end up the Termux user.

$ ssh -p 8022 root@localhost 
Enter passphrase for key '/Users/rbdixon/.ssh/id_rsa': 
Welcome to Termux!

Wiki: https://wiki.termux.com
Community forum: https://termux.com/community
IRC channel: #termux on freenode
Gitter chat: https://gitter.im/termux/termux
Mailing list: termux+subscribe@groups.io

Search packages: pkg search <query>
Install a package: pkg install <package>
Upgrade packages: pkg upgrade
Learn more: pkg help
bash-4.4$

Setup ADB Tunnel

Disable WiFi on the target device. When you turn off WiFi you'll lose the ability to SSH into your device unless you can tunnel over ADB.

You must have a recent version of ADB for this stuff to work right.

$ adb --version
Android Debug Bridge version 1.0.39
Revision 3db08f2c6889-android

Establish the ADB tunnel from your development machine to the target device:

adb forward tcp:8022 tcp:8022

Check:

adb forward --list

This should let your SSH into the device with WiFi disabled. Try it out:

ssh -i $SSH_PRIVATE_KEY -p 8022 localhost

Configure SSH

Add something like this to your ~.ssh/config file:

Host device

# localhost:8022 is the tunnel provided by ADB
Hostname localhost
Port 8022

# Tunnel from the device to reach BURP
RemoteForward :8080 localhost:8080

# SOCKS proxy to send traffic from BURP out the device cellular connection
DynamicForward localhost:37760

Now ssh device to establish the connection. As long as this ssh connection is open you can:

  1. Tunnel from the device to BURP over port 8080.
  2. Proxy requests over SOCKS from the Mac to the device so that you can use the device's cellular connection.

Use the Tunnel

Just because you have these tunnels does not mean that any traffic will be sent over them. You've got to mangle iptables rules to redirect traffic over the tunnel.

The example below is only for IPv4 because ADB seems to only be able to forward IPv4 traffic. The systems I've tested have been dual stack so I fixed this by adding the IPv4 address for the server to /etc/hosts on the device. This forced the usage of IPv4.

You could also handle this by using socat to tunnel IPv6 to IPv4 but I did not try that.

Anyways, adapt the script below and run it on the device as root to start using the tunnels.

#!/bin/sh

# On your Mac make sure burp is listening on this port
# with transparent proxying enabled
BURP=127.0.0.1:8080

# Android username for the application that you are hacking.
# All traffic originated by processes run by this user will    
# be sent to burp.
APP_USER=u0_a111

# Android username for the sshd process. Run `ps|grep ssh` to find it.
SSH_USER=u0_a116

# Create table
iptables -t nat -N burp

# Flush table
iptables -t nat -F burp

# Setup forwarding rules

# General HTTP/HTTPS capture
#iptables -t nat -A burp -p tcp --dport 80 -j DNAT --to-destination $BURP
#iptables -t nat -A burp -p tcp --dport 443 -j DNAT --to-destination $BURP

# If you want to capture _any_ traffic to a target IP address
# use a rule like this. This is useful for running Chrome through
# burp.
iptables -t nat -A burp -p tcp -d 93.184.216.34 -j DNAT --to-destination $BURP

# This rule sends any traffic originated by your target application
# to burp
iptables -t nat -A burp -p tcp -m owner --uid-owner $APP_USER -j DNAT --to-destination $BURP

# The rules above are added to a special "burp" chain. Packets will
# not use that chain unless they are "jumped" onto that chain. We
# want to send all packets to that chain _unless_ they are coming
# from SSH. Why? That's the return path back from Burp. If we don't 
# filter those out your packets will just travel in a little boring
# loop.
# The rule below jumps all packets routed out of the device that are
# not owned by SSH to the burp chain.
iptables -t nat -A OUTPUT -m owner ! --uid-owner $SSH_USER -j burp

# Print
iptables -t nat -L

Testing

Check by running these as root on the device:

  1. curl -v http://google.com. Check in Burp for traffic.
  2. curl -v https://google.com. Check Burp. TLS cert should be the one you created.
  3. curl -v http://ip-api.com/json. You should be on the cellular network.

Next run your target app on the device and make sure you've got Burp traffic as expected.

Use Chrome Inspector for Mobile Chrome

You can inspect Chrome running on your Android device over ADB.

Other Carve articles about MITM techniques: