Here are several reasons why I think you should keep reading this post:
- The Android application is one of the most installed applications in Iran and belonged to a Irancell, a telecommunications company with roughly 148 million subscribers
- The application had been tested by many well-known penetration testing companies before I began working on it. Honestly, they were shocked when I reported the vulnerability with critical severity
- To achieve RCE, I conducted a bit of related research, which I believe every skilled hunter should be able to do while actively hunting their target
- This was an unusual RCE case which I'd never seen before; I managed to trick a headless browser into running arbitrary JavaScript code server-side
-
In order to intercept network traffic, I should have opened two encryption layers:
- a normal TLS layer, which is used in widespread applications
- an extra AES implementation with a random key for each user
- The RCE was blind, and the remote server didn't have an internet connection, so I couldn't send data back by HTTP. Surprisingly, I could make a DNS tunnel to exfiltrate the data
Capturing the Traffic
One of the most bold topics in mobile application penetration tests is figuring out how to capture traffic. This is not a big deal in web applications (sometimes it is; for example, the user panel on amazon.com is heavily protected against MITM with BurpSuite), but overall, 99% of websites allow MITM when you install your own certificate as RCA, so it can sign other websites' certificates, making MITM not an issue. However, in mobile applications, the story is totally different.
Before going through it, let me clarify that I conducted all tests on this APK, which was the latest version at the time of hunting. There are many methods to disable an SSL pinning mechanism in mobile applications. I personally prefer the Frida tool, which is a handy tool to hook classes and functions at runtime. I've been using the following code to bypass SSL pinning:
var array_list = Java.use("java.util.ArrayList");
var ApiClient = Java.use('com.android.org.conscrypt.TrustManagerImpl');
ApiClient.checkTrustedRecursive.implementation = function(a1,a2,a3,a4,a5,a6) {
console.log('Bypassing SSL Pinning');
var k = array_list.$new();
return k;
}
It's universal and works in every application. The bypass works by intercepting and hooking the certificate validation process. Since the method returns an empty or "trusted" list without performing any actual certificate checks, it tricks the application into believing the SSL/TLS connection is secure. Before hooking, I ran Genymotion, installed MyIrancell, set up the BurpSuite proxy in the virtual Android machine (I cannot remember the version; it was API 31 or something), and I could easily capture the traffic. Surprisingly, the MyIrancell application wasn't applying an SSL pinning mechanism. Why did the application behave like this? The answer is in the traffic:
POST /webaxn/webaxn?group=selfcare HTTP/1.1
Accept-Encoding: gzip, deflate
Content-Type: application/vnd.wap.wbxml
IMEI: 000000000000000
os_version: 21
User-Agent: 2.2.1.10995/android
x-user-agent: 2.2.1.10995/android
x-device-ip: 10.0.3.15
X-Cookie: 1.5961535400767.32.8a42e69d7c531b0f.18c7a3256fbe09d4.72505fdbef3b508b0a899e3ce6f44f6883fb7767
DENSITY: 4.0;640
Host: myirancell.irancell.ir
Connection: close
Content-Length: 240
<binary WBXML body — Jl¹H¾ö ¿ÛÇ …>
— More coming Rest of the writeup (the AES layer, the headless-browser RCE, and the DNS-tunnel exfil) will be transferred shortly.