🇧🇩 BdSecCTF 2025 — Hacker App Challenge Write-Up
🏴 Challenge Name: Hacker App
Category: Reverse Engineering / Mobile
Platform: Android (APK)
Author: NomanProdhan
Flag Format: BDSEC{...}
🧩 Challenge Description
You can hack any website with this app !!
🔧 Tools Used
ByteCode-Viewer(APK decompiler)- Python (for decoding custom encoding)
- Base64 + logic
🔍 Initial Analysis
After loading the APK into ByteCode-Viewer, we landed on the MainActivity.class file which controls the core functionality of the app. Inside it, we found this suspicious string:
private static final String zN2LpB = "ioFOE6/xXoxB5M02UsaVQAhuQVC5f8+PMMgwOwGbmE0R7n6qyRQ9qwCzCgDVYWc6";Also, this line caught my eye:
if ("2025.bdsec-ctf.com".equals(var1)) {
var7.add("[SUCCESS] Root Account Password: " + zN2LpB);
}🤔 Looks like we’re supposed to input that exact domain to trigger the payload! So this string was definitely the flag or a key.
🔐 The Obfuscation
Looking deeper, we noticed a method encrypting strings like:
Base64.encodeToString(iP7sV3("root:toor123".getBytes(StandardCharsets.UTF_8), mK4pJ1[var3]), 2);The encoding chain:
- Reverse & XOR (
oX8jZ6) - Custom TEA encryption (
uE9rC5) - Byte transformation with jN5fC2 table (
yW0qH1) - Base64 encode
That means zN2LpB was generated using this same method — so we need to reverse all these steps.
🐍 Decoding Script
I wrote a custom Python script that reverses all the transformations above:
import base64
import struct
jN5fC2 = [(i * 73 + 41) & 0xFF for i in range(256)]
inv_jN5fC2 = [0] * 256
for i in range(256):
inv_jN5fC2[jN5fC2[i]] = i
vY7kD3 = [253635900, 1264216440, -2053724232, -908399620]
def reverse_yW0qH1(data):
result = bytearray(len(data))
for i in range(len(data)):
b = data[i]
b = (b >> 5 | (b << 3)) & 0xFF
result[i] = inv_jN5fC2[b]
return result
def reverse_uE9rC5(data):
result = bytearray(len(data))
for i in range(0, len(data), 8):
v0 = struct.unpack('>I', data[i:i+4])[0]
v1 = struct.unpack('>I', data[i+4:i+8])[0]
sum_ = -1640531527 * 16
for _ in range(16):
v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (vY7kD3[(sum_ >> 11) & 3] + sum_)
v1 &= 0xFFFFFFFF
v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (vY7kD3[sum_ & 3] + sum_)
v0 &= 0xFFFFFFFF
sum_ += 1640531527
result[i:i+4] = struct.pack('>I', v0)
result[i+4:i+8] = struct.pack('>I', v1)
# Remove padding
pad = result[-1]
return result[:-pad]
def reverse_oX8jZ6(data, key):
data = bytearray(data)
for i in range(len(data)):
data[i] ^= (key >> (i % 4 * 8)) & 0xFF
data.reverse()
return data
def try_decrypt(base64_string):
decoded = base64.b64decode(base64_string)
rev1 = reverse_yW0qH1(decoded)
for key in [int(k, 16) for k in [
"4AC93E17", "DE9A3214", "D2C3E1F0", "BEEFCAFE",
"C0FFEE01", "BAADF00D", "CAFEBABE", "8BADF00D"
]]:
try:
rev2 = reverse_uE9rC5(rev1)
rev3 = reverse_oX8jZ6(rev2, key)
return rev3.decode('utf-8')
except Exception:
continue
return None
# The encoded string
zN2LpB = "ioFOE6/xXoxB5M02UsaVQAhuQVC5f8+PMMgwOwGbmE0R7n6qyRQ9qwCzCgDVYWc6"
# Try decoding
flag = try_decrypt(zN2LpB)
print("Decrypted string:", flag)When run, the output was:
Decrypted string: BDSEC{h4cK3r_aPP_t0_HACK_THE_PLANET_bdSECctf}💥 BOOM! There’s the flag, hidden deep under layers of custom obfuscation and encoding!
🧠 Key Learnings
- APKs can hide juicy secrets, especially in hardcoded constants.
- Custom encoding chains are tricky, but with patience you can reverse them.
- Always look for strings that are Base64-encoded or XOR’d — they’re often the key 🔑.
- Static analysis using
ByteCode-Viewercan go a long way without even needing to run the app.
🏁 Flag
BDSEC{h4cK3r_aPP_t0_HACK_THE_PLANET_bdSECctf}💬 Final Thoughts
This was a fun reverse-engineering challenge with some clever string obfuscation. It reminded me of real-world malware unpacking. Shoutout to the challenge creator — great job!
