In android there are several ways to make HTTP requests. For
example using HttpURLConnection (low-level API built
into Java), OkHttp (A popular third-party library)
etc.
Starting from Android 9 (API level 28), HTTP clients like
URLConnection, Cronet, and
OkHttp enforce the use of HTTPS, thus disabling
cleartext traffic by default. However, it’s important to note that
other HTTP client libraries, such as Ktor, may not
enforce these restrictions [↗].
However, if developers explicitly set
usesCleartextTraffic=true [↗]
in the manifest or network security configuration [↗],
cleartext traffic is permitted.
To intercept TLS/SSL traffic, the proxy certificate must be trusted by the device. Android recognizes two types of certificates: user certificates and system certificates. Applications can explicitly configure which certificate types they trust using network security config.
Example network_security_config.xml:
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>Source [↗].
Android 7.0 (API level 24) and higher.
<base-config>
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>Android 6.0 (API level 23) and lower.
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>If the application doesn’t accept user certificates you need to install system certificate or patching network security config.
Install it in the user CA store via Android settings. In general apps trust user certificates if it targets Android 6 (API 23) or lower, or network security config allows it.
http://<burp_proxy_listener>.Install on older Android ≤ 11
If you try to install this certificate, it’ll be grayed out and you’ll not be able to install it. To install it you need to change its extension.
mv cacert.der cacert.crtNote: Keep in mind that Android accepts both DER and PEM formats. When you install a certificate as a user (regardless of the format), Android automatically converts it to DER format.
Requirement: rooted device.
This method use a temporary RAM-based filesystem (tmpfs) to override the system certificate directory in memory without actually modifying the read-only system image.
Export certificate in DER format from Burp Suite.
By default, all Android system certificates are in PEM format. While Android can handle certificates in DER format, I recommend converting them to PEM to ensure broader compatibility. Some libraries may behave inconsistently with DER certificates. For example, I’ve observed that Flutter applications fail to work properly with DER-formatted certificates. In this step, you’ll convert the certificate from DER to PEM format and rename it using its subject hash.
openssl x509 -inform DER -in cacert.der -out cacert.pem
mv cacert.pem $(openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1).0Create a folder on the device.
adb shell
mkdir /data/local/tmp/cacerts-added/Push the certificate in the created folder.
adb push <subject_hash.0> /data/local/tmp/cacerts-added/Add your custom cert to the same folder.
cp /system/etc/security/cacerts/* /data/local/tmp/cacerts-added/Switch to root user.
suMount tmpfs over system certs.
mount -t tmpfs tmpfs /system/etc/security/cacertsCopy combined certs into the tmpfs mount.
cp /data/local/tmp/cacerts-added/* /system/etc/security/cacerts/Update the perms & SELinux context labels.
chown root:root /system/etc/security/cacerts/*
chmod 644 /system/etc/security/cacerts/*
chcon u:object_r:system_file:s0 /system/etc/security/cacerts/*Install the proxy certificate as a regular user certificate.
adb shell.
Run the following script by Tim Perry.
# Create a separate temp directory, to hold the current certificates
# Otherwise, when we add the mount we can't read the current certs anymore.
mkdir -p -m 700 /data/local/tmp/tmp-ca-copy
# Copy out the existing certificates
cp /apex/com.android.conscrypt/cacerts/* /data/local/tmp/tmp-ca-copy/
# Create the in-memory mount on top of the system certs folder
mount -t tmpfs tmpfs /system/etc/security/cacerts
# Copy the existing certs back into the tmpfs, so we keep trusting them
mv /data/local/tmp/tmp-ca-copy/* /system/etc/security/cacerts/
# Copy our new cert in, so we trust that too
cp /data/misc/user/0/cacerts-added/* /system/etc/security/cacerts/
# Update the perms & selinux context labels
chown root:root /system/etc/security/cacerts/*
chmod 644 /system/etc/security/cacerts/*
chcon u:object_r:system_file:s0 /system/etc/security/cacerts/*
# Deal with the APEX overrides, which need injecting into each namespace:
# First we get the Zygote process(es), which launch each app
ZYGOTE_PID=$(pidof zygote || true)
ZYGOTE64_PID=$(pidof zygote64 || true)
# N.b. some devices appear to have both!
# Apps inherit the Zygote's mounts at startup, so we inject here to ensure
# all newly started apps will see these certs straight away:
for Z_PID in "$ZYGOTE_PID" "$ZYGOTE64_PID"; do
if [ -n "$Z_PID" ]; then
nsenter --mount=/proc/$Z_PID/ns/mnt -- \
/bin/mount --bind /system/etc/security/cacerts /apex/com.android.conscrypt/cacerts
fi
done
# Then we inject the mount into all already running apps, so they
# too see these CA certs immediately:
# Get the PID of every process whose parent is one of the Zygotes:
APP_PIDS=$(
echo "$ZYGOTE_PID $ZYGOTE64_PID" | \
xargs -n1 ps -o 'PID' -P | \
grep -v PID
)
# Inject into the mount namespace of each of those apps:
for PID in $APP_PIDS; do
nsenter --mount=/proc/$PID/ns/mnt -- \
/bin/mount --bind /system/etc/security/cacerts /apex/com.android.conscrypt/cacerts &
done
wait # Launched in parallel - wait for completion here
echo "System certificate injected"Unpack the apk.
apktool d target.apkModify the AndroidManifest.xml to add a
networkSecurityConfig
(xml/network_security_config.xml). If it’s already
present edit the file.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config" ... >
...
</application>
</manifest>network_security_config.xml
<!-- Example -->
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>Repack & sign the apk.
# Repack
apktool b
# Sign
java -jar uber-apk-signer.jar -apk <app_name>.apkNote: Unpacking and repacking an app can break stuff.
If you configure an HTTP proxy in Android settings, you can intercept network traffic. However;
Connections made directly via TCP sockets bypass the proxy and cannot be intercepted.
Applications may bypass the HTTP proxy settings if the developer configures them to disallow proxy usage. E.g. with OkHttp:
OkHttpClient client = new OkHttpClient.Builder()
.proxy(java.net.Proxy.NO_PROXY) // Disables proxy usage
.build();Also framework like Flutter and Xamarin application does not respect system proxy.
Requirement: the proxy certificate must be installed in the system certificate store.
In Android Studio, you can configure a global proxy for an
emulated device by going to the device’s Settings > Proxy
-> Manual proxy configuration. In the host name field
set burp suite proxy with http protocol:
e.g. http://192.168.1.90 and port number.
Warning: If your proxy is unreachable, try changing the emulator version. You can find other versions here: https://developer.android.com/studio/emulator_archive.
Warning: This method is not recommended when using the Android Studio emulator. Strange things could happen…
Requirement: the proxy certificate must be installed in the system certificate store.
If the proxy settings are ignored, use an Android VPN service app to intercept app traffic. You can use the open-source RethinkDNS app [↗].
Steps:
http://burpsuiteip:port).Warning: This method is not recommended when using the Android Studio emulator. Strange things could happen.
Requirement: The proxy certificate must be installed in the system certificate store.
Before starting, you need to bind Burp to a privileged port.
Source: [↗].
sudo touch /etc/authbind/byport/443
sudo chown $USER:$USER /etc/authbind/byport/443
sudo chmod 755 /etc/authbind/byport/443
authbind --deep java -Djava.net.preferIPv4Stack=true -jar burpsuite.jarWe need some kind of DNS server where we can control the IP.
Example dnsmasq.conf:
address=/target.com/192.168.1.50
log-queriesRun dnsmasq with docker:
docker pull andyshinn/dnsmasq
docker run --name my-dnsmasq --rm -it -p 0.0.0.0:53:53/udp -v /tmp/dnsmasq.conf:/etc/dnsmasq.conf andyshinn/dnsmasqEnforce DNS usage using Android’s VPN feature with tools like RethinkDNS.
Finally, configure your proxy tool for invisible proxying.
Burp will act as an HTTP(S) server, parse the HOST
header, and forward requests. Ensure an invisible proxy listener is
set on ports 443 and 80.
Normal Proxy
In a normal proxy, the client (e.g., a browser or app) is explicitly
configured to use the proxy. This means the client intentionally
routes traffic through the proxy. Thus:
GET http://www.example.com/path HTTP/1.1)Invisible Proxy
An invisible
proxy operates without the client being explicitly configured to
use it. This is useful when the client does not support proxy
configurations. Therefore, the client remains unaware of the proxy.
However:
With plain HTTP, a proxy-style request looks like this:
GET http://example.org/foo.php HTTP/1.1
Host: example.org
A non-proxy-style request looks like this:
GET /foo.php HTTP/1.1
Host: example.org
Proxies usually use the full URL in the first line to determine
the destination, ignoring the Host header. In invisible
proxying, Burp parses the Host header from
non-proxy-style requests to determine the destination.