A Technical Walkthrough from Binary Analysis to Runtime Query Manipulation
Welcome to this detailed walkthrough of the Flipcoin Wallet iOS challenge, part of the Mobile Hacking Lab training platform.
Our mission?
✅ Unpack and reverse the application.
✅ Identify insecure SQL query construction.
✅ Confirm the presence of a SQL Injection vulnerability.
✅ Bypass UI limitations using Frida instrumentation.
✅ Inject a custom SQL payload and extract the hidden flag — documenting every step with clarity.
🎯 Objective
This project aims to identify and exploit a SQL Injection vulnerability in the Flipcoin Wallet iOS application. We will demonstrate how a poorly constructed SQL query can be manipulated to extract sensitive data, even when there is no visible input from the user.
🛠 Tools Used
Tool | Purpose |
---|---|
Frida | Dynamic instrumentation and runtime memory manipulation |
Radare2 / rabin2 | Static analysis of the binary |
TrollStore | Installing unsigned apps on a jailbroken iPhone |
iPhone SE (jailbroken) | Target device used for dynamic testing |
Terminal | Execution of analysis commands and scripts |
🧩 Analysis Steps (In Detail)
1. IPA Extraction and Binary Inspection
We started by extracting the .ipa
file:
unzip com.mobilehackinglab.Flipcoin-Wallet6.ipa -d flipcoin_wallet_lab
Inside the Payload/Flipcoin Wallet.app
folder, we located the app binary. We used rabin2
to inspect embedded strings:
rabin2 -zqq Payload/Flipcoin\ Wallet.app/Flipcoin\ Wallet | grep -iE 'select|insert|update|delete|sqlite'
Key findings:
- A SQLite file:
your_database_name.sqlite
- Table:
wallet
- SQL strings including
INSERT OR IGNORE INTO wallet
andSELECT * FROM wallet
- The recovery key column contains the flag:
FLAG{fl1p_d4_c01nz}}
This confirmed that the app interacts with a local SQLite database.
2. Function Discovery via Imports and Cross-references
We searched for imported SQLite functions:
rabin2 -i Payload/Flipcoin\ Wallet.app/Flipcoin\ Wallet | grep sqlite3
We found the app uses functions like sqlite3_prepare
, sqlite3_exec
, and sqlite3_column_text
.
To locate where these were used in the binary:
r2 -A Payload/Flipcoin\ Wallet.app/Flipcoin\ Wallet
> axt sym.imp.sqlite3_prepare
Two key functions were identified: createWallets
and getWallets
. We disassembled getWallets
:
s 0x10000f98c
pdf
We confirmed that it performs:
SELECT * FROM wallet
without using parameter binding — indicating potential for SQL Injection.
🔧 Why We Switched to Frida Instead of Manual Input
After installing the app using TrollStore on a jailbroken iPhone SE, the interface was completely broken: the layout was oversized and unusable.
We attempted several UI fix strategies:
- Applying view scaling using Frida scripts
- Iterating subviews to locate the root UIView
All failed due to the app likely being developed in Flutter or SwiftUI, where traditional UIKit methods no longer apply.
As we could not interact via the UI, we used Frida to simulate SQL Injection directly at the memory level.
⚔️ Simulating SQL Injection with Frida
We built a script to intercept and replace the vulnerable query at runtime.
Script: extract_flag_sqlite.js
// Intercept and replace vulnerable query
Interceptor.attach(Module.getExportByName(null, "sqlite3_prepare"), {
onEnter: function (args) {
const originalQuery = args[1].readUtf8String();
if (originalQuery.includes("SELECT * FROM wallet")) {
const newQuery = "SELECT 1, 2, 3, 4, recovery_key FROM wallet";
Memory.writeUtf8String(args[1], newQuery);
console.log("[+] SQL query replaced:", newQuery);
}
}
});
// Monitor data extraction from each column
Interceptor.attach(Module.getExportByName(null, "sqlite3_column_text"), {
onEnter: function (args) {
this.columnIndex = args[1].toInt32();
},
onLeave: function (retval) {
const value = Memory.readUtf8String(retval);
if (value && value.includes("FLAG{")) {
console.warn("🚩 FLAG FOUND:", value);
}
}
});
How This Works (Line by Line)
-
sqlite3_prepare
is called when SQL queries are constructed. - We check if the query contains
SELECT * FROM wallet
- If so, we replace the query string in memory with a malicious one that extracts the
recovery_key
column - When
sqlite3_column_text
is called, we read each column's content - If the result contains
FLAG{
, we print it
Note: The app must still execute the original function. We manually triggered it by interacting with whatever broken UI was visible, until
getWallets
was called.
Execution:
frida -U -n "Flipcoin Wallet" -l extract_flag_sqlite.js
Output:
[+] SQL query replaced: SELECT 1, 2, 3, 4, recovery_key FROM wallet
🚩 FLAG FOUND: FLAG{fl1p_d4_c01nz}}
🧠 Why This is SQL Injection
SQL Injection is not defined by typing a payload — it’s about injecting or modifying a SQL query due to poor query construction.
In this case:
- The query is built as a static string without input sanitization
- No parameter binding is used
- Although we couldn’t inject via UI, we demonstrated the flaw by changing the query directly, proving it is vulnerable and exploitable
This is a valid example of SQL Injection — only the delivery method changed (from input field to in-memory injection).
📫 About the Author
[Júnior Carreiro]
🔐 Mobile AppSec | iOS Security | Reverse Engineering
📍 Let's connect: [GitHub] · [Linkedin]
🏷️ Tags: #ios
#reverseengineering
#frida
#infosec
#mobile