Category: Mobile Security
You can download the Mobile Security Challenge, along with all the challenges for the 2016 Greek Qualifier CTF of European Cybersecurity Challenge, in this link. More details on the Greek ECSC 2016 Qualifier CTF event can be found here.
Points: 60
Challenge designer: Kolovos
Description: > Your goal is to find the flag hidden within the communication with the server. There is no need to hammer the server in order to get the flag.
ooo '*` ###
(o o) (o o) (o o)
ooO--(_)--Ooo-ooO--(_)--Ooo-ooO--(_)--Ooo-
Write-up
First step is to extract the Java code from the provided apk
file. We use the flag --deobf
to avoid overloaded functions in jadx
output and get some unique class and function names that we can reference and understand easier.
❯❯❯ d2j-dex2jar.sh otp.apk
❯❯❯ jadx --deobf otp.jar
The application has a button that when clicked executes function C0312e.m2871a()
.
/ctf/cirrus/otp/C0312e.java
public static String m2871a() {
String str;
JSONObject b;
String str2 = "https://token-1321.appspot.com/getToken?";
String bigInteger = new BigInteger(130, new SecureRandom()).toString(10);
String str3 = "";
try {
hash = new String(Hex.encodeHex(MessageDigest.getInstance("MD5").digest(("type=otp&generate=new&challenge=" + bigInteger + "&magic=y0l0").getBytes())));
try {
Log.i("INFO", "hash=" + hash);
} catch (NoSuchAlgorithmException e) {
Log.i("ERROR", "NoSuchAlgorithmException");
b = C0312e.getJsonObjectFromString(C0312e.m2873a(str2 + ("type=otp&challenge=" + bigInteger + "&hash=" + hash)));
str3 = "";
return b.getString("token");
}
} catch (NoSuchAlgorithmException e2) {
hash = str3;
Log.i("ERROR", "NoSuchAlgorithmException");
b = C0312e.getJsonObjectFromString(C0312e.m2873a(str2 + ("type=otp&challenge=" + bigInteger + "&hash=" + hash)));
str3 = "";
return b.getString("token");
}
b = C0312e.getJsonObjectFromString(C0312e.m2873a(str2 + ("type=otp&challenge=" + bigInteger + "&hash=" + hash)));
str3 = "";
try {
return b.getString("token");
} catch (JSONException e3) {
Log.i("ERROR", "Token not found");
return "There was an error retrieving the token.";
}
}
The application first generates a random BigInteger
(130 bits/16 bytes
) which is used as the challenge
in the actions to follow. There is also a static magic
string (y0l0
) and a type
(otp
) which all together are put in a string and used to generate a MD5 hash.
hash = new String(
Hex.encodeHex(
MessageDigest.getInstance("MD5")
.digest(("type=otp&generate=new&challenge=" + bigInteger + "&magic=y0l0").getBytes())));
We can replay the same actions using Python and generate the MD5 digest ourselves:
import random
import hashlib
challenge = random.getrandbits(130)
secret_request = 'type=otp&generate=new&challenge=%s&magic=y0l0' % str(challenge)
secret_hash = hashlib.md5(secret_request).hexdigest()
print 'Challenge: %s' % challenge
print 'Hash: %s' % secret_hash
The final step is to use the challenge
and the hash
in a GET request to generate a new token and get our flag. All steps have been combined in the following Python script:
import random
import hashlib
from pwn import *
HOST = 'token-1321.appspot.com'
challenge = random.getrandbits(130)
log.success('Challenge: %s' % challenge)
secret_request = 'type=otp&generate=new&challenge=%s&magic=y0l0' % str(challenge)
secret_hash = hashlib.md5(secret_request).hexdigest()
log.success('Hash: %s' % secret_hash)
r = remote(HOST, 443, ssl=True)
r.send('GET /getToken?type=otp&generate=netype=otp&challenge=%s&hash=%s HTTP/1.1\r\nHost: %s\r\n\r\n' % (challenge, secret_hash, HOST))
resp = r.recv().splitlines()
flag = [f for f in resp if 'flag' in f][0]
log.success(flag)
r.close()
❯❯❯ python solution.py
[+] Challenge: 359664723468245819351269767034398323045
[+] Hash: e5b1858b72939c47ad1ce90f36d58dbe
[+] Opening connection to token-1321.appspot.com on port 443: Done
[+] {"token": "6324", "flag": "N0tV3ryRand0mStr1ng"}
[*] Closed connection to token-1321.appspot.com port 443
The flag is: > N0tV3ryRand0mStr1ng
# echo 7eamnull