Overview
Network analysis reveals how iOS apps communicate with servers, exposes API endpoints, and uncovers authentication mechanisms. This is crucial for understanding app functionality and identifying security vulnerabilities.
Most modern iOS apps use HTTPS with certificate pinning, requiring advanced techniques to intercept traffic.
Intercepting Network Traffic
Setting Up a Proxy
Burp Suite
mitmproxy
Charles Proxy
Configure Burp
Open Burp Suite
Proxy > Options > Proxy Listeners
Add listener on all interfaces (e.g., 0.0.0.0:8080)
Enable “Support invisible proxying”
Configure iOS Device
Settings > Wi-Fi > Select network
Scroll down to HTTP Proxy
Select “Manual”
Server: Your computer’s IP
Port: 8080
Install CA Certificate
On device, browse to http://burp
Download CA certificate
Settings > Profile Downloaded > Install
Settings > General > About > Certificate Trust Settings
Enable full trust for Burp CA
iOS 10.3+ requires explicit trust for user-installed certificates.
# Install mitmproxy
brew install mitmproxy
# Start proxy
mitmproxy --listen-host 0.0.0.0 --listen-port 8080
# Or use web interface
mitmweb --listen-host 0.0.0.0 --listen-port 8080
Install certificate:
Configure device proxy (same as Burp)
Visit http://mitm.it on device
Download iOS certificate
Install and trust the certificate
mitmweb provides a modern web UI for analyzing traffic.
Enable SSL Proxying
Proxy > SSL Proxying Settings > Enable SSL Proxying Add locations:
Configure Device
Help > SSL Proxying > Install Charles Root Certificate on Mobile Device Follow on-screen instructions
Trust Certificate
Settings > General > About > Certificate Trust Settings Enable full trust for Charles Proxy CA
VPN-Based Interception
ProxyMan creates a VPN tunnel for seamless interception:
Install ProxyMan on macOS
Install ProxyMan companion app from App Store
Pair device via QR code
Traffic automatically routed through proxy
Advantages:
No manual proxy configuration
Works with all apps
Captures localhost traffic
Automatic certificate installation
ProxyMan requires both devices on same network for initial pairing.
Packet Capture with tcpdump
For low-level packet analysis:
# SSH into jailbroken device
ssh root@ < device-i p >
# Install tcpdump via Cydia
apt-get install tcpdump
# Capture all traffic
tcpdump -i any -w /tmp/capture.pcap
# Capture only HTTP/HTTPS
tcpdump -i any -n 'tcp port 80 or tcp port 443' -w /tmp/http.pcap
# Copy to computer for analysis
scp root@ < device-i p > :/tmp/capture.pcap .
# Analyze with Wireshark
wireshark capture.pcap
Filter by Host
Filter by IP
Real-time monitoring
tcpdump -i any -n host api.example.com -w /tmp/api.pcap
SSL Pinning Bypass
Understanding SSL Pinning
App validates the server’s certificate against a bundled copy: // Certificate pinning implementation
- ( void )URLSession:(NSURLSession * )session
didReceiveChallenge:( NSURLAuthenticationChallenge * )challenge
completionHandler:( void ( ^ )(NSURLSessionAuthChallengeDisposition, NSURLCredential * ))completionHandler {
NSData * serverCert = challenge . protectionSpace . serverTrust . certificateAtIndex ( 0 );
NSData * pinnedCert = [ self loadPinnedCertificate ];
if ([serverCert isEqualToData: pinnedCert]) {
// Certificate matches
NSURLCredential * credential = [ NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
completionHandler (NSURLSessionAuthChallengeUseCredential, credential);
} else {
// Certificate mismatch - reject
completionHandler (NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil );
}
}
App validates the public key instead of entire certificate: // Extract and compare public keys
SecKeyRef serverKey = SecTrustCopyPublicKey (serverTrust);
SecKeyRef pinnedKey = [ self loadPinnedPublicKey ];
if ( SecKeyCompare (serverKey, pinnedKey) == 0 ) {
// Keys match
}
Harder to bypass than certificate pinning.
Many apps use TrustKit for pinning: TrustKit * trustKit = [[TrustKit alloc ] initWithConfiguration: @{
@"api.example.com" : @{
kTSKPublicKeyHashes : @[
@"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ,
@"sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="
]
}
}];
Bypass Techniques
SSL Kill Switch
Frida Script
objection
Binary Patching
Easiest method for jailbroken devices:
Enable in Settings
Settings > SSL Kill Switch 2
Enable “Disable Certificate Validation”
Optionally select specific apps
SSL Kill Switch doesn’t work with all pinning implementations. Have alternatives ready.
Universal bypass for most apps: // ssl-bypass.js
if ( ObjC . available ) {
// Bypass NSURLSession certificate validation
var NSURLSession = ObjC . classes . NSURLSession ;
var SessionDelegate = ObjC . classes . __NSURLSessionDelegate ;
Interceptor . attach (
SessionDelegate [ '- URLSession:didReceiveChallenge:completionHandler:' ]. implementation ,
{
onEnter : function ( args ) {
console . log ( '[+] Bypassing SSL pinning' );
var challenge = ObjC . Object ( args [ 3 ]);
var completionHandler = new ObjC . Block ( args [ 4 ]);
// Create credential that accepts all
var credential = ObjC . classes . NSURLCredential
. credentialForTrust_ ( challenge . protectionSpace (). serverTrust ());
// Call completion handler with credential
completionHandler . implementation ( 1 , credential );
}
}
);
// Bypass SecTrustEvaluate
var SecTrustEvaluate = Module . findExportByName ( 'Security' , 'SecTrustEvaluate' );
Interceptor . replace ( SecTrustEvaluate , new NativeCallback ( function ( trust , result ) {
console . log ( '[+] SecTrustEvaluate called - forcing success' );
Memory . writeU8 ( result , 1 ); // kSecTrustResultProceed
return 0 ; // errSecSuccess
}, 'int' , [ 'pointer' , 'pointer' ]));
// Bypass AFNetworking if present
if ( ObjC . classes . AFHTTPSessionManager ) {
Interceptor . attach (
ObjC . classes . AFSecurityPolicy [ '- setSSLPinningMode:' ]. implementation ,
{
onEnter : function ( args ) {
console . log ( '[+] Disabling AFNetworking SSL pinning' );
args [ 2 ] = ptr ( 0 ); // AFSSLPinningModeNone
}
}
);
Interceptor . attach (
ObjC . classes . AFSecurityPolicy [ '- setAllowInvalidCertificates:' ]. implementation ,
{
onEnter : function ( args ) {
args [ 2 ] = ptr ( 1 ); // Allow invalid certs
}
}
);
}
// Bypass TrustKit if present
if ( ObjC . classes . TrustKit ) {
Interceptor . attach (
ObjC . classes . TSKPinningValidator [ '- evaluateTrust:forHostname:' ]. implementation ,
{
onEnter : function ( args ) {
console . log ( '[+] Bypassing TrustKit' );
},
onLeave : function ( retval ) {
retval . replace ( ptr ( 1 )); // Return YES
}
}
);
}
console . log ( '[+] SSL pinning bypass loaded' );
}
Run it: frida -U -l ssl-bypass.js "Target App"
objection provides quick SSL bypass: # Install objection
pip install objection
# Launch app with objection
objection -g "Target App" explore
# Disable SSL pinning
ios sslpinning disable
# Additional checks
ios sslpinning disable --quiet
objection combines multiple bypass techniques for broad compatibility.
Permanent SSL bypass:
Find pinning code
Use Hopper/Ghidra to locate:
SecTrustEvaluate calls
Certificate validation methods
TrustKit/AFNetworking usage
Patch instructions
; Original:
bl SecTrustEvaluate
cbnz w0, loc_fail ; Branch if failed
; Patched:
bl SecTrustEvaluate
nop ; Remove conditional branch
Or simply return success: ; Replace entire validation method:
mov w0, # 1 ; Return YES/true
ret
Save and resign
# Save patched binary
# Resign with own certificate
codesign -f -s "iPhone Developer" MyApp.app
Advanced Pinning Bypass
Custom Pinning Logic Some apps implement custom validation: // Find and hook custom validator
var methods = ObjC . classes . CustomSSLValidator . $ownMethods ;
methods . forEach ( function ( method ) {
if ( method . includes ( 'validate' ) || method . includes ( 'verify' )) {
Interceptor . attach (
ObjC . classes . CustomSSLValidator [ method ]. implementation ,
{
onLeave : function ( retval ) {
console . log ( '[+] Hooked: ' + method );
retval . replace ( 1 );
}
}
);
}
});
Flutter Apps Flutter apps require different approach: # Use reFlutter for automatic bypass
pip install reflutter
# Patch Flutter engine
reflutter app.ipa
# Install patched IPA
# SSL pinning bypassed in Flutter layer
React Native Hook React Native networking: if ( ObjC . classes . RCTHTTPRequestHandler ) {
Interceptor . attach (
ObjC . classes . RCTHTTPRequestHandler [ '- sendRequest:withDelegate:' ]. implementation ,
{
onEnter : function ( args ) {
console . log ( '[+] RN request intercepted' );
// Modify request as needed
}
}
);
}
Xamarin Apps Xamarin uses Mono runtime: // Hook .NET ServicePointManager
var ServerCertificateValidationCallback = /* find callback */ ;
Interceptor . replace ( ServerCertificateValidationCallback ,
new NativeCallback ( function () {
return 1 ; // Always valid
}, 'int' , [])
);
Analyzing API Communication
Request Analysis
Identify how the app authenticates:
GET /api/user/profile HTTP / 1.1
Host : api.example.com
Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Decode JWT tokens at jwt.io
Check expiration and claims
Test token reuse and refresh
GET /api/data HTTP / 1.1
Host : api.example.com
X-API-Key : abc123def456ghi789
Extract from requests
Check if static or per-user
Test without key for validation
POST /oauth/token HTTP / 1.1
grant_type=authorization_code&
code=AUTH_CODE&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
Map OAuth flow
Extract client credentials
Test PKCE implementation
GET /api/data HTTP / 1.1
Cookie : sessionid=abc123; csrftoken=xyz789
Check secure/httponly flags
Test session fixation
Verify CSRF protection
Map all API endpoints: // Frida script to log all URLs
var NSURLSession = ObjC . classes . NSURLSession ;
var NSURLRequest = ObjC . classes . NSURLRequest ;
var endpoints = {};
Interceptor . attach (
ObjC . classes . NSURLSessionTask [ '- resume' ]. implementation ,
{
onEnter : function ( args ) {
var task = ObjC . Object ( args [ 0 ]);
var request = task . currentRequest ();
var url = request . URL (). absoluteString (). toString ();
var method = request . HTTPMethod (). toString ();
var key = method + ' ' + url ;
if ( ! endpoints [ key ]) {
endpoints [ key ] = 0 ;
console . log ( '[+] New endpoint: ' + key );
}
endpoints [ key ] ++ ;
}
}
);
// Periodically dump summary
setInterval ( function () {
console . log ( ' \n [+] Endpoint Summary:' );
Object . keys ( endpoints ). sort (). forEach ( function ( key ) {
console . log ( ' ' + endpoints [ key ] + 'x - ' + key );
});
}, 60000 );
Understand request parameters: # Burp Suite extension or script
# Analyze parameter patterns
import json
def analyze_request ( request ):
# Parse query params
# Parse JSON body
# Extract headers
params = {
'user_id' : 'integer' ,
'auth_token' : 'jwt' ,
'timestamp' : 'unix_epoch' ,
'signature' : 'hmac_sha256'
}
# Test for:
# - Required vs optional
# - Type validation
# - Authorization checks
# - Injection vulnerabilities
Response Analysis
Success Response
Error Response
Debugging Info
{
"status" : "success" ,
"data" : {
"user_id" : 12345 ,
"email" : "[email protected] " ,
"premium" : true ,
"subscription_expires" : "2025-12-31"
},
"meta" : {
"api_version" : "2.1" ,
"timestamp" : 1709481600
}
}
Man-in-the-Middle Techniques
Request Modification
Burp Suite Proxy
Automated Testing
Frida-based MITM
Intercept request
Proxy > Intercept > Intercept is on
Trigger request in app
Request appears in Burp
Modify parameters
# Original
POST /api/purchase
{ "item_id" : 123 , "price" : 9.99 }
# Modified
POST /api/purchase
{ "item_id" : 123 , "price" : 0.01 }
Forward request
Click “Forward” to send modified request
Use Match/Replace rules for automatic modification.
# mitmproxy script
from mitmproxy import http
def request ( flow : http.HTTPFlow) -> None :
# Modify authentication
if "Authorization" in flow.request.headers:
flow.request.headers[ "Authorization" ] = "Bearer ATTACKER_TOKEN"
# Change user ID in JSON
if flow.request.method == "POST" and "/api/" in flow.request.path:
try :
data = flow.request.json()
if "user_id" in data:
data[ "user_id" ] = 99999 # Different user
flow.request.text = json.dumps(data)
except :
pass
def response ( flow : http.HTTPFlow) -> None :
# Modify responses
if "/api/user/status" in flow.request.path:
try :
data = flow.response.json()
data[ "premium" ] = True # Force premium
flow.response.text = json.dumps(data)
except :
pass
Run with: // Modify requests before they're sent
Interceptor . attach (
ObjC . classes . NSMutableURLRequest [ '- setValue:forHTTPHeaderField:' ]. implementation ,
{
onEnter : function ( args ) {
var field = ObjC . Object ( args [ 3 ]). toString ();
if ( field === "Authorization" ) {
var newToken = ObjC . classes . NSString
. stringWithString_ ( "Bearer CUSTOM_TOKEN" );
args [ 2 ] = newToken ;
console . log ( '[+] Modified Authorization header' );
}
}
}
);
// Modify JSON bodies
Interceptor . attach (
ObjC . classes . NSMutableURLRequest [ '- setHTTPBody:' ]. implementation ,
{
onEnter : function ( args ) {
var bodyData = ObjC . Object ( args [ 2 ]);
var bodyStr = ObjC . classes . NSString . alloc ()
. initWithData_encoding_ ( bodyData , 4 ). toString ();
try {
var json = JSON . parse ( bodyStr );
json . premium = true ; // Modify field
var newStr = JSON . stringify ( json );
var newData = ObjC . classes . NSString
. stringWithString_ ( newStr )
. dataUsingEncoding_ ( 4 );
args [ 2 ] = newData ;
console . log ( '[+] Modified request body' );
} catch ( e ) {}
}
}
);
Attack Scenarios
Test if server validates prices: POST /api/purchase HTTP / 1.1
{
"item_id" : 123 ,
"quantity" : 1 ,
"price" : 0.01 , // Modified from 99.99
"currency" : "USD"
}
Only test on apps you’re authorized to test. This is often illegal otherwise.
Test Insecure Direct Object References (IDOR): # Access another user's data
GET /api/user/12345/private-data HTTP / 1.1
# Try different user ID
GET /api/user/99999/private-data HTTP / 1.1
Test for rate limiting: import requests
# Send 1000 requests
for i in range ( 1000 ):
resp = requests.post( 'https://api.example.com/login' ,
json = { 'username' : 'test' , 'password' : f 'pass { i } ' }
)
print ( f "Request { i } : { resp.status_code } " )
Test duplicate parameter handling: POST /api/transfer HTTP / 1.1
amount=1.00&amount=1000.00&recipient=attacker
Proxyman Modern macOS proxy with iOS pairing
Burp Suite Industry-standard security testing proxy
mitmproxy Scriptable MITM proxy
Wireshark Network protocol analyzer
Postman API testing and documentation
HTTPToolkit Modern HTTP debugging
Best Practices
Document Everything
Keep detailed logs of all requests/responses
Screenshot important findings
Export traffic captures for review
Map complete API surface
Test Systematically
Start with passive observation
Identify authentication mechanism
Map all endpoints
Test authorization boundaries
Check input validation
Stay Legal
Only test apps you’re authorized to test
Respect terms of service
Don’t access others’ data
Report vulnerabilities responsibly
Verify Findings
Reproduce issues multiple times
Test on different accounts
Confirm with direct API calls
Document exact reproduction steps
Network analysis complete when you have:
Successfully intercepted all traffic
Bypassed SSL pinning if present
Mapped complete API structure
Identified authentication mechanisms
Documented potential vulnerabilities
Tested key security controls
Resources
SSL Kill Switch 2 Jailbreak tweak for SSL bypass
objection Runtime mobile exploration toolkit
Frida CodeShare Community scripts including SSL bypass
OWASP Mobile Mobile security testing guide