2017 Flare-On Challenge writeups
flareon4 has just ended, and it was a blast! i had learned a lot in the past few weeks and the challenges really put my skills to the test. since the fireeye team has published the full writeup on their blog, i will only cover two challenges and introduce a few remarkable tools that were used to solve them
Challenge 8 - flair.apk
challenge 8 provide us with an android apk file — flair.apk, and we need to solve 4 mini challenges (Michael, Brian, Milton, Printer) in order to form AES key to decrypt the final flag bytes. to solve the challenges, i used JADX for the decompilation purposes, android emulator for emulation and the awesome frida framework for dynamic instrumentation
Michael
the first challenge is fairly straightforward. it can be easily solved upon decompiling com.flare_on.flair.Michael.checkPassword()
method
first challenge answer: MYPRSHE__FTW
Brian
for the second challenge, there are multiple ways to solve it. you can either solve it statically by going through the xmls or you can solve it by overriding com.flare_on.flair.Brian.teraljdknh()
method. i chose the latter option
as the method will compare the input value (first argument) and the answer (second argument), we will need to overload the method with our own method which will print the second argument passed to it. after loading frida server on the emulator, the hooking process were done using the following scripts:
1 | # hook.py |
1 | // brian.js |
running the script on our terminal will spit out the answer for the second challenge: hashtag_covfefe_Fajitas!
Milton
Upon decompiling com.flare_on.flair.Milton
class, we can see that the “submit” button is disabled by default. we can enable it by giving 4 stars on the rating bar. next, we need to make sense of the checking method — com.flare_on.flair.Milton.breop()
we can see that the method will return true if the return value of Stapler.neapucx(<our input>)
and nbsadf()
is the same. we can easily obtain the return value of nbsadf()
by overriding the method with frida
1 | // milton.js |
then we can create the reverse implementation of Stapler.neapucx()
method to get the answer: 10aea594831e0b42b956c578ef9a6d44ee39938d
1 | # decrypt.py |
Printer
in the last challenge, we can see that most of the strings are obfuscated and were decrypted with Stapler.iemm()
method. we can easily obtain the original string by intercepting Stapler.iemm()
return value
1 | // iemm.js |
we can see that the string equals
is being decrypted. and upon crosschecking with the decompiled code, we know that the method is being called in com.flare_on.flair.Printer.cgHbC()
. it will check whether Stapler.neapucx(<our input>)
equals Stapler.poserw(tVvV)
. therefore, in order to obtain the answer we need to intercept the return value of Stapler.poserw()
1 | // poserw.js |
then we can use our previous implementation of neapucx()
to obtain the last answer: 5f1be3c9b081c40ddfc4a0238156008ee71e24a4
1 | print neapucx([95, 27, -29, -55, -80, -127, -60, 13, -33, -60, -96, 35, -127, 86, 0, -114, -25, 30, 36, -92]) |
once we submit all the answers, we will finally obtain the flag for challenge 8: `pc_lo4d_l3tt3r_gl1tch@flare-on.com`
Challenge 10 - shell.php
challenge 10 is a cryptography challenge which requires a lot guessing and manual work. we are presented with a php file that includes encrypted data which are xored in a chained manner. thus, we need to recover the key used to decrypt the data
the algorithm
1 | for ($i = 0; $i < 2268; $i++) { |
based the decryption algorithm above, we can conclude that the first N
(key length) character of the original text is xored with the key and the remaining part of it is xored with itself to form the encrypted data. the encryption algorithm can be represented with the following expression:
recovering key length
1 | $key = isset($_POST['key']) ? $_POST['key'] : ""; |
in order to break the crypto, we need to know the length of the key which is used to encrypt the plaintext data. based on the piece of code above, the possible key length will be between 32 and 64. and seeing that it is derived from md5 sums, the key can only be formed with hexadecimal characters. equipped with all the information above, i wrote a “smart” bruteforcer using z3 theorem prover to determine the possible key length
1 | # brute_length.py |
we find out that the length of the key used in the encryption is 64 bytes. thus, we can conclude that the original string used in the encryption routine is 32 byte long, which is really too large for anyone to bruteforce (string.printable32). therefore, we need to find a more feasible way to recover the original string
the possibilities
now that we have obtained the key length, we can modify the bruteforce script to spit out every possible characters at each offset
1 | # possible.py |
executing the script will produce the following output:
1 | # mkhdznfq @ ubuntu in ~/ctf/flareon/shell [11:46:30] |
manual labour
this is the most tedious part of the challenge as we need to manually check each possibilities. to ease the process, i had designed a simple web interface to help me construct the key
1 |
|
above is the sample screenshot in the middle of the guessing decryption process. the fact that the decrypted data mostly consist of php and html script sure help sped up the process a lot. full key: db6952b84a49b934acb436418ad9d93d237df05769afc796d067bccb379f2cac
subchallenges
supplying the key to the decryption routine will produce the following script (it has been reformatted to make it easier to understand):
1 |
|
we can see that now we have 3 more base64-encoded blobs to decode. but with following the previous methods (recover key length, obtain candidates, etc), and with a few little tweaks, we will obtain 3 substrings that can be combined to form the final flag for challenge 10: `th3_xOr_is_waaaay_too_w34k@flare-on.com`
1 | import sys |
Final words
all over, i think my methods on solving these challenges might seems a little bit overcomplex, but it is actually a good chance for me to actually familiarize myself with all the cool toys tools, as well as demonstrate their capalities and potential use cases in ctf-ish challenges. finally, i want to congratulate all the winners who managed to complete this year challenges. and also shout out to the challenge authors for their hard work developing each of the challenges while making it a fun experience for the players