Mobile Security

Mobile Traffic Capture and Certificate Pinning Bypass

Why mobile apps need a different workflow

The first time I tested a mobile application, I opened the app, looked at the screen, and had no idea what to do next. The traffic was invisible, the app did not use the normal browser, and the server seemed to work fine. I needed a way to see the requests. That was the moment I started building the workflow in this post.

This is my personal study note on mobile traffic capture and certificate pinning bypass. The goal is not just to see the traffic. The goal is to map the API, understand the client-side logic, and find the security issues that the mobile client cannot hide.

Set up the proxy

The first step is a local proxy that can capture HTTP and HTTPS traffic. I use a tool that listens on a port and records every request. The proxy runs on my computer, and the mobile device or emulator sends its traffic to that proxy.

The device and the computer need to be on the same network, or the device needs a way to reach the computer's address. I configure the device's proxy settings to use the computer's IP and the proxy port. Once the device is configured, the proxy should show the traffic.

Some applications ignore the system proxy. They use their own HTTP stack, or they only accept cleartext traffic when the application is configured for debugging. I check the application behavior and adjust the environment when necessary.

Install the proxy certificate

HTTPS traffic is encrypted, so the proxy needs to decrypt it. The proxy creates a certificate authority and uses it to sign certificates for the domains that the app visits. The device must trust that certificate authority.

On iOS, I install the certificate and then enable full trust for it in the certificate settings. On Android, I install the certificate as a user certificate. The process is different depending on the Android version and the application's network security configuration.

Android 7 and later do not trust user-installed certificates by default. Applications that target a recent Android version may ignore the user certificate entirely. To capture their traffic, I need to install the certificate in the system store, which usually requires a rooted device or a custom build.

Use an emulator when possible

An emulator is often easier to work with than a physical device. I can control the Android version, install the certificate in the system store, and take snapshots. The emulator also makes it easier to run scripts and automate the testing.

The downside is that some applications detect emulators and behave differently. They may refuse to run, show a warning, or use a different API endpoint. I compare the emulator behavior with the physical device behavior before I trust the results.

For iOS, the simulator has its own set of tools and limitations. Some features, such as certain hardware sensors and biometrics, are not available. I choose the environment that matches the application's target platform and testing needs.

Identify the API surface

Once the traffic is visible, the next step is building the API map. I record every request that the app makes during a normal workflow. The map includes the host, the path, the method, the headers, the parameters, and the response.

The API map is the foundation for the security testing. It shows me the endpoints that the UI uses, but the app may also have endpoints that are not used in the normal flow. I search the application package, the JavaScript, and the API documentation for additional endpoints.

The mobile app is a client, not a security boundary. The server must validate every request. I test the API the same way I test a web API, even if the app never calls a particular endpoint.

Look for hardcoded secrets

Mobile applications are distributed as binary packages, which means an attacker can extract and inspect them. Hardcoded secrets are a common finding. I search the application package for API keys, database passwords, cloud credentials, and cryptographic keys.

The search can be done with static analysis tools and with simple string searches. I look for high-entropy strings, base64 values, and files with names like config, keys, or credentials. The presence of a hardcoded secret is a finding even if the secret is not used in the traffic.

The encryption keys are especially important. If the app encrypts data with a key that is embedded in the package, an attacker can decrypt the data. The encryption provides no real protection against a determined attacker.

Inspect the cryptographic logic

The app may use encryption to hide the request or response data. I need to understand the cryptographic logic to test the API. The first step is identifying the encryption library and the key management. The second step is finding where the key is stored.

The key can be stored in the application code, in a configuration file, in the keychain, or in a remote server. If the key is in the code, I can extract it and decrypt the traffic. If the key is stored securely, I may need to hook the app at runtime.

The purpose of the encryption matters. Client-side encryption is not a substitute for server-side security. The server must still validate the data, the session, and the authorization.

Bypass certificate pinning

Certificate pinning is a technique that binds the app to a specific certificate or public key. The app refuses to trust other certificates, which blocks the proxy's certificate. To capture the traffic, I need to bypass the pinning.

The first step is identifying how the pinning is implemented. Some apps pin in the HTTP library, some in the native code, and some in a separate security library. Static analysis can reveal the relevant classes and functions.

Dynamic instrumentation is the next step. Tools like Frida can hook the certificate validation functions at runtime and force them to accept the proxy certificate. The hook needs to match the exact function and signature used by the app.

Pinning bypass is a testing technique, not the vulnerability itself. The finding should focus on the API or the business logic that is exposed after the bypass. I do not report the pinning bypass alone.

Replay and modify requests

Once I can see and modify the traffic, I test the API for common issues. I replay requests, change parameters, and compare responses. The mobile client may validate input, but the server must validate it too.

The API tests include authorization, input validation, rate limiting, and business logic. I use the same approach as web API testing. The difference is that the mobile app may have a different session model, a different token format, or a different way of handling errors.

I also test the client-side controls. If the app hides a button or a feature, the server may still expose the endpoint. I look for hidden endpoints in the package and test them directly.

Android specifics

Android adds a few extra steps to the traffic capture workflow. The application may declare a network security configuration that controls which certificates are trusted. If the configuration only trusts the system store, the user-installed proxy certificate will not work.

I read the network security configuration from the application package. The file is usually called network_security_config.xml. It can allow cleartext traffic for specific domains, trust user certificates, or pin a set of certificates.

The application can also use its own HTTP client that bypasses the system proxy. Some clients use native networking code that does not read the proxy settings. I need to configure the proxy at the network level or use an iptables redirect on a rooted device.

Android debug builds are easier to test because they often allow cleartext traffic and user certificates. A release build is more restrictive. I compare the two builds to understand the difference between the development configuration and the production configuration.

iOS specifics

iOS has a similar set of controls. The application can use App Transport Security, or ATS, to require HTTPS. The proxy certificate must be installed and fully trusted in the certificate settings.

The iOS certificate trust is per certificate. I need to install the proxy certificate and then enable the full trust switch for that certificate. The switch is in the certificate details screen.

The application can also use certificate pinning in the URLSession delegate, in a third-party library, or in the native code. The pinning may be based on the certificate, the public key, or the certificate chain.

iOS simulators are useful for traffic capture, but they have limitations. Some features, such as secure enclave operations, are not available in the simulator. I use a physical device for the final verification.

Runtime instrumentation

When static analysis is not enough, I use runtime instrumentation. Frida is the tool I use most often. It can hook functions, modify arguments, and call methods inside the running application.

The hook targets the certificate validation functions. I find the function name in the static analysis and then use Frida to override its return value. The override makes the application trust the proxy certificate.

Runtime instrumentation can also be used for other tasks. I can bypass anti-debugging checks, dump the decrypted traffic, or call internal methods that are not exposed in the UI. The possibilities are broad, but each hook needs to be tested carefully.

The instrumentation should be reversible. A hook that remains active after the test can change the behavior of the application. I remove the hook and restart the application after the test.

Documenting the environment

The environment is part of the evidence. A finding that only reproduces on a rooted Android device is different from a finding that works on a stock device. I record the device model, the operating system version, the application version, and the proxy setup.

The document should also include the certificate configuration. Did I use a user certificate or a system certificate? Did I disable certificate pinning? The answers tell the developer which environment needs to be secured.

The environment documentation is also useful for the retest. When the developer fixes the issue, I need to reproduce the same environment to verify the fix. A missing detail can make the retest impossible.

The documentation should be part of the report, not a separate artifact. The reader should be able to understand the test without asking me for the missing details.

When the app detects the proxy

Some applications detect the proxy and refuse to work. The app may show a warning, use a different endpoint, or exit immediately. The detection can be based on the proxy settings, the certificate, or the network environment. I do not disable the detection and assume the test is over. I record the behavior, disable the detection with a hook when the engagement allows it, and compare the traffic with and without the proxy. The detection itself is a control, and the way the app responds tells me something about its security posture.

Common mistakes I make

My biggest mistake is skipping the certificate installation and trying to capture traffic without it. The proxy shows an error, and I waste time. My second mistake is using a physical device without understanding the Android certificate rules. The user certificate is not enough for many apps. My third mistake is treating the mobile client as the security boundary.

The fourth mistake is ignoring the application package. The package contains the API surface, the hardcoded secrets, and the cryptographic logic. The fifth mistake is forgetting to document the environment, including the device model, the OS version, and the app version.

My quick checklist

  1. Configure the proxy and the device or emulator.
  2. Install the proxy certificate in the correct trust store.
  3. Map every API request and response.
  4. Search the application package for hardcoded secrets.
  5. Inspect the cryptographic logic and key storage.
  6. Identify and bypass certificate pinning if necessary.
  7. Test the API as an untrusted client.
  8. Document the device, OS, app version, and environment.

What I would do next time

Next time I want to build the API map before I write any test. The map shows me the endpoints, the parameters, and the data flow. I also want to spend more time on the application package, because that is where the hidden functionality lives. Mobile traffic capture is not just about seeing the requests. It is about understanding what the client and the server each trust.