NahamCon 2023 Writeup - Mobile
during the 16th - 18th june 2023 we participated in NahamCon ctf 2023 i managed to solve 4 out of 5 mobile challenges available, this is my writeups for the challenges i managed to solve, i mainly use genymotion as emulator to these challenge.
JNINJASPEAK
a application named jninjaspeak.apk was given, the first thing i do is quickly try to decompile the app using apktool and then analyze the smali files using jadx-gui, i understood that this app will do somekind of "translation" to the text we give, but i can't find the "translation" scheme so i try opening the app in genymotion.
i tried translating a few things and i notice something, when i translate the word "flag" the translation result is empty, so i thought that i had to find the right string to make the translation result empty and the string will be the flag, but after getting the string and submitting it into the NahamCon web it's not the flag :X.
after that i noticed something again, if i filled it with a character for example "b" it will be translated into "0" and if i try to translate "0" it will be "b" and then i wonder, what will happen if i translate spaces " ", and after giving a few space i found this
i noticed that it will produce the flag, and i try to give it more spaces so it will produce the flag
flag{1f539e4a706e6181dae9db3fad6a78f1}
Fortune Teller
in this chall a apk file fortune_teller.apk was given, after decompiling it with apktool and opening the smali with jadx i understood that i have to give the correct string which is equal to the value of correct_guess to be able to produce the decrypted image which was encrypted using AES CBC mode before, i tried opening the R class file in jadx and found something interesting
so the value of the correct string and encrypted are actually obtainable, first i try to find the correct_guess string in the /res/values/strings.xml and i actually found it
and i also found the encrypted file in the /res/raw folder, but i don't really want to construct a manual CBC encryption so i try feeding the correct_guess into the application and the image of the flag was given
flag{d7687f4af1a9c75c1811488a12cb54a6n}
Where's Waldo
a apk file wheres_waldo.apk was given, i decompiled the apk and then open it using jadx but the jadx gives error after trying to opening it :D, so i decided to see throught the painful smali files manually, the first thing i noticed is this app is trying to get the location of the android device based on the latitude and the longitude.In the MainActivity class onCreate function i found something interesting.
there's a request url with the parameter "lat" and "long", so i copied the url and try it in the web using random value of "long" and "lat" which i assumed as longitude and latitude , and it's actually a api endpoint.
so we know how far we're off by, i actually tried brute forcing the value of the latitude and longitude using this script
Script
import requests
import json
lat=5.1735064
lon=90.898947
payload=f"http://challenge.nahamcon.com:30001/location?lat={lat}&long={lon}"
temp=requests.get(payload).text
temp=json.loads(temp)
verdict=temp['message']
dist=temp['off_by']
while verdict=='Waldo is not here!':
test1=f"http://challenge.nahamcon.com:30001/location?lat={lat-1}&long={lon}"
test2=f"http://challenge.nahamcon.com:30001/location?lat={lat+1}&long={lon}"
test3=f"http://challenge.nahamcon.com:30001/location?lat={lat}&long={lon-1}"
test4=f"http://challenge.nahamcon.com:30001/location?lat={lat}&long={lon+1}"
test1=requests.get(test1).text
test2=requests.get(test2).text
test3=requests.get(test3).text
test4=requests.get(test4).text
test1=json.loads(test1)
test2=json.loads(test2)
test3=json.loads(test3)
test4=json.loads(test4)
if int(test1['off_by'])<int(dist):
dist=test1['off_by']
verdict=test1['message']
lat-=1
elif int(test2['off_by'])<int(dist):
dist=test2['off_by']
verdict=test2['message']
lat+=1
elif int(test3['off_by'])<int(dist):
dist=test3['off_by']
verdict=test3['message']
lon-=1
else:
dist=test4['off_by']
verdict=test4['message']
lon+=1
print(verdict)
print(dist)
flag{eff45e45ecc16922b78d4c3b0776b577n}
Red Light Green Light
we're given another apk red_light_green_light.apk i try to decompile it again using apktool then load it into jadx and i read the MainActivity class source code, and i found the interesting part in the source code
we actually need the value of red to be false so we can actually access the decryption process, i tried hooking the apk using frida to change the value of red in the checkLight method and it seem i was able to change it?, but when i push the button there's no response so i think maybe i can't do it that way.
Then i noticed something, there's the "Log.w("KEY", getKey());" syntax so i assume that the log.w will print the key when the CheckLight method was called, so i search for a way to hook that log call using frida, and found the script to hook it.
frida.js
Java.perform(function() {
var Log = Java.use("android.util.Log");
Log.w.overload('java.lang.String', 'java.lang.String').implementation = function(a, b) {
console.log("The application reports Log.w(" + a.toString() + ", " + b.toString() + ")");
return this.w(a, b);
};
});
now i just need to load the script into frida to hook the method.
after getting the key, i found the encrypted file in the /res/raw folder, and finally i just need to recreate the decryption scheme just like in the application.
decrypt.py
from Crypto.Cipher import AES
key=b"zxzaKk5uLHdoKo9y8osZSnTe5DCdrIX0"
iv = 16 * b'\x00'
f=open('encrypted','rb').read()
cipher = AES.new(key, AES.MODE_CBC, iv)
cipher_enc = cipher.decrypt(f)
print(cipher_enc)
hasil=open('hasil','wb')
hasil.write(cipher_enc)
after that i got the file and i do binwalk and i found a JPEG file inside so i extracted it using binwalk too
flag{29b9edf8fd1e28ea8cd4faa37a6dbf25}