Intercepting SSL on iOS
There are multiple ways to intercept SSL traffic on iOS. In this post, I will summarize a couple of ways to intercept SSL traffic. Some methods would be easy to execute but some of them would require a jailbroken device.
Basic SSL
SSL or TLS connection requires a bunch of steps to initiate a secure connection. I am not going to explain the details of those protocols but the important thing to remember is this. For a secure connection to happen, the client and the server must use public and private keys issued by the certificate provider. This is why it is hard to sniff those connections. Unless you’re a certificate provider, you can’t issue a forged certificate to listen to traffic. To listen to traffic, we must either create a self-signed certificate and force clients to accept this certificate or intercept the encryption keys in a jailbroken device and use those keys to decrypt the packets.
Proxy
There are multiple https proxies to intercept traffic. You can either use mitmproxy, Charles, or Proxyman. All those programs can issue a self-signed certificate and will allow you to sniff SSL traffic. You can check the documentation of these apps to see how you can create your certificate, add this certificate to your device, and finally trusting this certificate.
Pros:
- Easy to use.
- No Jailbreak required
Cons:
- SSL pinning can be an issue.
- Only HTTPS traffic.
- Custom certificate must be installed
Sniffing with SSLKEYLOGFILE
Pros:
- Can sniff SSL traffic without key installation.
- SSL pinning can be bypassed
Cons:
- Jailbreak only.
- More steps.
SSLKEYLOGFILE is an environment variable that is being used by popular SSL libraries like OpenSSL or BoringSSL. When this variable is set, those libraries log pre-master secret keys to a file. So you can use those keys to decrypt encrypted packages. However, there is no easy way to set up this environment variable on iOS. We need Frida to hook into the process and set this parameter ourselves.
- Jailbreak your device
- Install Frida Server to your device and frida tools to your computer
- Connect your iOS device to your computer with USB
- Run the following command on your terminal
frida -U -f BUNDLE.ID.OF.APP --codeshare andydavies/ios-tls-keylogger -o app.keylog
You can check the source code to see how this script is working. This script works for iOS 12.x but you may need to update it for other iOS versions due to its fragile hooking mechanism. If you need to update for future iOS versions, you need to update 0x2A8
constant in the script. Let’s see how we can find that constant for future iOS versions.
- Get
dyld_shared_cache_arm64
from your iOS ipsw file. Tutorial - Disassemble
libboringssl.dylib
- Search for
CLIENT_RANDOM
You will get some disassembly like below
libboringssl:__text:0000000181D8835C LDRSW X3, [X20,#0xC]
libboringssl:__text:0000000181D88360 ADRP X1, #aClientRandom@PAGE ; "CLIENT_RANDOM"
libboringssl:__text:0000000181D88364 ADD X1, X1, #aClientRandom@PAGEOFF ; "CLIENT_RANDOM"
libboringssl:__text:0000000181D88368 MOV X0, X19
libboringssl:__text:0000000181D8836C BL sub_181D4CF90
sub_181D4CF90
represents our log function. Go inside that function and you’ll see something similar
libboringssl:__text:0000000181D4CF90 SUB SP, SP, #0x60
libboringssl:__text:0000000181D4CF94 STP X22, X21, [SP,#0x50+var_20]
libboringssl:__text:0000000181D4CF98 STP X20, X19, [SP,#0x50+var_10]
libboringssl:__text:0000000181D4CF9C STP X29, X30, [SP,#0x50+var_s0]
libboringssl:__text:0000000181D4CFA0 ADD X29, SP, #0x50
libboringssl:__text:0000000181D4CFA4 LDR X8, [X0,#0x68]
libboringssl:__text:0000000181D4CFA8 LDR X8, [X8,#0x2A8]
libboringssl:__text:0000000181D4CFAC CBZ X8, loc_181D4D0B4
As you see, if the logging function is defined which is at offset 0x2A8
then the library logs the key, otherwise, the function returns without logging anything. Our Frida script changes that pointer so we can log master keys to a file. Okay, now we learned how to dump master keys. How can we use this key file to decrypt packets?
We need to capture the network packages of your device with tcpdump. There are multiple ways of doing this.
- Dump from the device by using tcpdump if you’re jailbroken
- Share your Mac internet with Internet Sharing with USB or WiFi
- Create a virtual network with rvictl
ideviceinfo | grep UniqueDeviceID
rvictl -s YOUR_UNIQUE_DEVICE_ID
tcpdump -i rvi0 -w capture.pcap -P
- Open your captured pcap file with Wireshark,
- Go to
Preferences > Protocols > TLS > (Pre)-Master-Secret log filename
and select the key file we created in the previous step.
Sniffing with SSLSPLIT
Pros:
- Raw TLS traffic can be sniffed besides HTTPS
- SSL pinning can be bypassed
- No Jailbreak required
Cons:
- Custom certificate must be installed.
- SSL pinning can be an issue.
This is my favorite mode of sniffing because it is easier to configure and solves lots of issues. Instead of https, we can also sniff non-HTTP(s) protocols. It also allows us to sniff Flutter apps. Because by default Flutter, doesn’t respect system proxy settings.
- Install Sslsplit with
brew install sslsplit
- Connect your device to Mac with USB and share your internet with USB only. Disable WiFi of your device.
-
Edit pf rules
sudo vi /etc/pf.conf
- Add following line just after
rdr-anchor "com.apple/*"
rdr pass on bridge100 inet proto tcp from any to any port 443 -> 127.0.0.1 port 8000
- Reload rules
sudo pfctl -F all -f /etc/pf.conf
- Create key files and trust them on your device. I used Charles Proxy’s certificate. You can export p12 file by navigating to
Help/SSL Proxying/Export Charles Root Certificate and Private Key
menu. Use below commands to export private and public keys fromcharles-ssl-proxying.p12
file
openssl pkcs12 -in charles-ssl-proxying.p12 -nokeys -out ca.crt
openssl pkcs12 -in charles-ssl-proxying.p12 -nocerts -out ca.key
I prefer using an existing certificate so I can just go back to Charles for a light workload. If you want to create your keys, you can use the below commands
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 1826 -key ca.key -out ca.crt
-
Run SSLSPLIT
sudo sslsplit -X capture.pcap -M keyfile.log -S logdir/ -k ca.key -c ca.crt -D https 127.0.0.1 8000
If you have done it everything correctly, you should see packages flying around and everything is logged to the logdir
directory. If you don’t see any packages, it means you either didn’t configure sharing well or your certificate isn’t created or trusted properly.