8ksec ClearRoute iOS App CTF Challenge
ClearRoute is an iOS app designed to test your ability to intercept sensitive data without getting caught. The app attempts to send a POST request containing a hidden flag.
Your objective: Intercept the outgoing request to retrieve the flag. Modify, patch, or instrument the app to disable or evade any checks, allowing the request to go through. Intercept the POST data to extract the flag from the constructed key.
Intercept smartly — this route’s under surveillance. 🛰️
Solving the CTF
Considering the challenge description, I decided to start the assessment by running my networking.js Frida script, one of a handful of core scripts I use to evaluate iOS apps. With the exception of teleporter.js, the rest of these scripts have a common purpose. Depending on the choice of mobile app development language and framework, there are many places you may need to hook an app with Frida for any given purpose. However, as your code path makes it's way down into lower levels, everything eventually goes through "funnel points" where you can trace things. Imagine that you're reversing some random app and you don't know anything about the internals. These scripts trace low level Objective-C or Swift bridge API's and print backtraces to see what called them. And in many cases these scripts can print out useful information to help guide next steps or flag weaknesses.
| Script | Purpose | Hooks |
|---|---|---|
utils.js |
Shared utilities (hexdump, backtrace, colors) | — |
crypto.js |
Encryption, hashing, key generation | CCCrypt, PBKDF, MD5/SHA, SecKey, Keychain |
networking.js |
Network connections, TLS, certificates | SSL*, SecTrust*, connect, send/recv, NSURLSession |
anti-debug.js |
Debugger detection/prevention | ptrace, sysctl, getppid, isatty, syscall |
jailbreak-detect.js |
Environment integrity checks | stat, fopen, dlopen, NSFileManager, canOpenURL |
hook_UIControl.js |
Trace UI elements like button clicks | Hooks UIControl sendAction:to:forEvent |
teleporter.js |
Spoof location and mov(lat, long) |
Hooks CLLocation["- coordinate"] |
First, let's get the app name.
ipsw idev apps ls | awk '{print $1, $2, $3, $4}' | grep -i 8ksec
ClearRoute 1.0 (com.8ksec.ClearRoute.WBC6955H89) /private/var/containers/Bundle/Application/7533AF39-7327-4EB0-8045-3E66F4137900/ClearRoute.appNow I'm going to start up ClearRoute while tracing network connection details through the Frida script.
frida -U -l utils.js -l networking.js -f com.8ksec.ClearRoute.WBC6955H89
____
/ _ | Frida 17.5.1 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to iPad (id=)
Spawning `com.8ksec.ClearRoute.WBC6955H89`...
[*] utils.js loaded - Frida iOS Security Analysis Utilities
[*] Functions available: formatHexdump, backtrace, symbolicate, safeReadString, timestamp
[*] Constants: Colors, CCAlgorithm, CCOperation, CCMode, CCPadding
[*] Helpers: callerInfo, formatArgs, ccAlgorithmName, ccOperationName, ccModeName, ccPaddingName
[*] ========================================
[*] networking.js - iOS Network/TLS Analysis
[*] ========================================
[*] Installing hooks...
On the app screen I see a button that says "Send Secure Data". Here's a sample of the output from this script after pressing the button:
[16:33:18.137] NSURLSession - dataTaskWithRequest:completionHandler:
├─ URL: https://8ksec.io/blog
[*] HTTPS request (encrypted)
├─ Runtime addr: 0x10294d76c
├─ Module: ClearRoute.debug.dylib
├─ Module base: 0x102948000
├─ Offset: 0x0000576c ← USE THIS IN BINARY NINJA
├─ Static addr: 0x10000576c ← Or this (assuming 0x100000000 base)
└─ Caller: ClearRoute.debug.dylib!ContentView.checkForProxyAndSend()
[16:33:18.954] SecCertificateCopyData
├─ SecCertificateRef: 0xc1b0d4200
[*] CERT PINNING: Extracting certificate data
├─ Runtime addr: 0x191ffaa9c
├─ Module: CoreFoundation
├─ Module base: 0x191fde000
├─ Offset: 0x0001ca9c ← USE THIS IN BINARY NINJA
├─ Static addr: 0x10001ca9c ← Or this (assuming 0x100000000 base)
└─ Caller: CoreFoundation!CFArrayApplyFunction
└─ Certificate data: 1290 bytes
[16:33:18.969] SecTrustCopyPublicKey
├─ Trust: 0x3036086e0
[!!!] PUBLIC KEY PINNING: Extracting public key from certificate!
├─ Runtime addr: 0x193579820
├─ Module: CFNetwork
├─ Module base: 0x1934ee000
├─ Offset: 0x0008b820 ← USE THIS IN BINARY NINJA
├─ Static addr: 0x10008b820 ← Or this (assuming 0x100000000 base)
└─ Caller: CFNetwork!invocation function for block in CFNetworkTrust::strictEvaluateAsync(StrictSecurity::SessionStrengthPolicy, CFNetworkTrust::SignatureAlgorithmStrength, NSObject*, void (int) block_pointer)
└─ SecKeyRef: 0x300754000 (public key extracted) On the app screen I see "Status: Data sent. Response code: 200. Were you able to Intercept the Request ?". I checked the Burp Suite proxy and found the flag in the proxy history.
POST /blog HTTP/1.1
Host: 8ksec.io
Content-Type: application/json
Connection: keep-alive
Accept: */*
User-Agent: ClearRoute/1 CFNetwork/3826.600.41 Darwin/24.6.0
Content-Length: 65
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
{"8ksec_intercepted":"CTF{no_proxies_allowed}","user":"john_doe"}That was too easy. I didn't have to try to bypass any detections, despite the earlier script output that detected certificate pinning. I believe that this may have been easy because I didn't set a proxy on the device. Instead I used internet connection sharing with transparent proxying between the device and my Macbook. You can find details on that in my blog post. I took a look at the disassembly in Binary Ninja and spotted a method that looks like it's designed to detect if a proxy is enabled. Here I de-mangle the name to give you a better look at it.
swift demangle '_$s10ClearRoute11ContentViewV14isProxyEnabledSbyF'
_$s10ClearRoute11ContentViewV14isProxyEnabledSbyF ---> ClearRoute.ContentView.isProxyEnabled() -> Swift.BoolI'm going to turn off internet connection sharing and enable the proxy on the device and see what happens next. With the IP address and port of the Burp Proxy enabled on the device, I clicked the "Send Secure Data" button in the app again and see "Status: ❌ Failed to send data.". So it seems that the app isn't properly checking certificates and is simply checking for proxy settings to prevent capturing the flag.
Conclusion
I like these 8ksec CTF challenges a lot. The iOS CTF challenges are bite-sized and I can usually solve them in an hour or two, sometimes longer. But I usually learn something from them. If you're interested in learning mobile application hacking, check out the 8ksec "Practical Mobile Application Exploitation" course.