Skip to main content

Learn to Analyse and Program Encryption Scheme in Android(It's not that scary tho!): CyberTruckChallenge 2019 ( ͡° ͜ʖ ͡°) (day 99)

Cryptography has become an important aspect of our life to maintain the confidentiality of our private data whether it's in email, wifi connection, social media, and mobile application. But as a Hacker, just using this technology is not enough we need to go deeper to understand the implementation and find flaws that could arise if we don't treat it carefully. Thus, In this post, we will take a look at how to reverse engineer and implement encryption in Android application using Kotlin.


As proof of concept, we will use the 2019 CyberTruck challenge from NowSecure. You can check the official GitHub page in this link



I use a Genymotion emulator(Google Nexus 6 with Android API level 8.0-API 26) to act as the testing device for the android application. If you installed it and open the application you will see three features like above:

  • "Unlock" to trigger the encryption scheme within the application
  • "Wipe Keys" to delete the existing keys that have been unlocked earlier
  • "TampeProof" to detect whether Frida is installed in the device or not. 
In this post, we will not use Frida to get retrieve key but rather using code lifting and instrumentation a rather unusual method to reverse engineer cryptographic computation. The reason I used this approach is to show that there is always a different approach to finish a challenge, however, if you interest more about using Frida you can check the Github link that I have just earlier and scroll down to the write-up section the author of the Github page have shared several resources for you to follow.
Next, we want to retrieve the source code lets put it in JADX-GUI



Looking at the manifest file it seems there are no many components inside the application besides the MainActivity class.


While analyzing the MainAcivity class source code the above chunk of code gets my attention. This code is responsible to listen to the event for the "unlock" button earlier that triggers the encryption scheme and inside the onClick method, the button called the k() function.


Inside the k() function it calls another three functions: Challenge1(), a(j) and init(). Let's try to visit the first function call!

Unlocking the First Truck:

  • 50pts: There is a secret used to create a DES key. Can you tell me which one?
  • 100pts: There is a token generated at runtime to unlock the carid=1. Can you get it? (flag must be submitted in hex all lowercase)
According to the Github page, we need to get the secret key of the DES encryption to get 50 pts, while for 100 pts we need to get the token to unlock the carid=1

Looking at the Challenge1() class, we can see there is a constructor that called another internal function called generateKey()


Following the function called we got this list of code:



Judging from the logic we can safely assume that this function responsible to generate the token and good things for us the token is hardcoded "CyB3r_tRucK_Ch4113ng3". Let's follow the function logic again!



Aha! we got the secret key for the DES encryption and as you can see is hardcoded(50 pts acquired!).

inside the generateDynamicKey() function the hardcoded token is encrypted with the secret key. We can follow the same logic of this function to acquire the 100 pts, like source code below:



In summary, the source code contain three functions that will do all the task of challenge one. It is a good practice to break down a task into several functions for clarity.

  • fun pts_50() function generally just print out the hardcoded key and tell the user where they can find it within the source code.
  • fun pts_100() function will produce the hex value of the token. We need three things to generate the encryption key(DES key) in android: 
    • DESKeySpec class to create object for the key material. As you can see in the source code we passed the hardcoded key but before you pass it as a parameter you need to convert it to a byte array, in Kotlin, this procedure can be achieved by using toByteArray() method.
    • SecretKeyFactory class act as a factory for the key material. You need to pass two information to the object, first is the algorithm you want to use which is "DES" and the DES key object that we have create earlier using DESKeySpec.
    • Cipher that will do the encryption. You need to do two things before you can finally produce the encrypted content: first is to create the Cipher object along with the algorithm that you want to use(in this case we will be used "DES") and initialize the cipher object by specifying mode of the operation(in this case we will use Cipher.ENCRYPT_MODE that equivalent as one) along with the symmetric key. Finally, you can encrypt your content by passed as a parameter to the instance.doFinal() method, don't forget to convert the content to byte array.

One last thing to do to get the 100 points we need to convert the encrypted content into a hexadecimal. To do that I create printHex() function, I got this code from stackoverflow but the logic is pretty easy to understand, Since the instance.doFinal() return a byte array all we need to do is to iterate each of the index in the array and format every index into hexadecimal format "%02x".

The final result should give you this hex value: 046e04ff67535d25dfea022033fcaaf23606b95a5c07a8c6

Unlocking the Second Truck:

  • 50pts: This challenge has been obfuscated with ProGuard, therefore you will not recover the AES key.

  • 100pts: There is a token generated at runtime to unlock the carid=2. Can you get it? (flag must be submitted in hexa all lowercase)

 

 

Coming back to the k() function that we have found earlier its only logical that each of this called function represent each class that responsible for handling 1st challenge, 2nd challenge, and 3rd challenge respectively.

now that we have done with the first challenge lets go to the second challenge !


tracing back to the a class we can see in the source code that the class have a constructor that take context object from android as its parameter this is why at the MainActivity the a class called by passing the variable j which is a context object.

inside the constructor of the "a" class, it called its own function named "a" with two parameters .  First is a string that look like a key and second is another "a" function that use previous passed context object as its parameter.



Before you going any further you may think the naming of this class is annoying and confusing, well!, what you see here is what security researcher called obfuscated source code. The purpose of this approach is to make attacker confused when their read the source code. so it will be harder to reverse engineer it.

following the function call we arrive at the above source code and by looking at the logic in it we can stated this function responsible for generating the encrypted content but using AES algorithm but there is a catch in here. 

When creating the symmetric key using SecretKeySpec, the function used bArr2 that come from a(context) function so we need to go to this function to get the key. 

It looks like our prediction about "uncr4ck4ble_keyle$$" as the key is wrong and it turns out the string is actually used as the content that later will be encrypted.

 

Going to the a(context) function just like the above, we can see that the function open a a file named "ch2.key" and return the content of it. Getting this file is pretty easy all you need to do is decompile the apk using apktool and go to the assets folder where it contains the file that we want.

 

by dumping the content of the file we can be confident that we just got 50 pts since this is the AES key.

Now all we have to do is to encrypt the string with the content ch2.key. We can achieve this by following source code.

There are two main functions inside this source code. first is "pts_50" which show user about the credential and the second is "pts_100" that do encryption process to get the hex value. 

In "pts_100" it called another 2 functions which are read_file() and printHex() used for retrieve content from the file and convert byte array to hex value(we reuse this function from the previous source code) respectively. 

The logic of encryption is pretty much the same with the challenge 1 source code. But the only difference is that we are going to used AES not DES algorithm. We need two components to encrypt string using AES in android:

  • SecretKeySpec to construct the secret key without having go through SecretKeyFactory. In this case, we need to submit two information, first is the key that we got from ch2.key file and the algorithm of the encryption that we want to use "AES".
  • Cipher class to do the encryption. The setup follow the same logic with the previous source code. 

The final result should give you this hex value: 

512100f7cc50c76906d23181aff63f0d642b3d947f75d360b6b15447540e4f16

Unlocking the Third Truck:

  • 50pts: There is an interesting string in the native code. Can you catch it?

  • 100pts: Get the secret generated at runtime to unlock the carid=3. Security by obscurity is not a great design. Use real crypto! (hint: check the length when submitting the secret!)

The final challenge required us to reverse engineer the native code or library in Android. To all of you who never heard about native code, it is basically just a way of how android application can tap into a lower level process by using C/C++ coding.

So how android application do this? you can go to the k() function earlier. 


To load the native library, android app used System class to load the library and Java native keyword to initialize it.

To obtain the native code in the application you can use apktool and go to the "lib" folder. Now! there will be a different folder that contain the native code with the name based on different architecture, this make sure that the application can run in different hardware.

 

You can choose any arch that you want but in this post we will go with x86 because it's much easier for me and I'm gonna use radare2 to look into the assembly.

once you open it in radare2, you can go to the init() function in the binary, where initialization of the code is taking place.

 

 As you can see from the above figure, the radare2 translate the function as "Java_org_nowsecure_cybertruck_MainActivity_init" you don't have to worry about the naming this is just how android normally named its native library function.

If you scroll down little bit, you will encounter this string operation like the above figure, this is how usually x86 arch prepare string to be used and from the looks of it, it looks like the string that we are looking for. To get the full string you can type "izz" and you will got your fifty point.

Now, onto the hardcore stuff! in order to get 100 pts, we need to find the token that generated at runtime.

if you switch to the visual mode and go down little bit, you will encounter a loop in the binary. This loop is responsible to create the token by incoporating XOR calculation with the string that we have found earlier.

Notice that the first red box show us the index that used to keep track of the loop, whereas the second red box is the XOR operation that use register EAC and ECX, we need to put a breakpoint in it. In order to debug a native library, you need to put gdbserver inside your emulator and tap into the application process.

Once you find the PID of the app, you can go with this command:

~# gdbserver :<port number> --attach <pid number>

Now all you need to do is to connect to gdbserver:

~# adb forward tcp:<port number you set earlier in GDBserver> tcp:<port number you set earlier in GDBserver>

~# gdb

(gdb) target remote : <port number you set earlier in GDBserver>

Now go to the "Java_org_nowsecure_cybertruck_MainActivity_init" function 

Now we need to find the XOR operation address and put a breakpoint in it.

to put a breakpoint:

(gdb) b *<the instruction address>

Then type:

(gdb) c

to continue the process of the application.

If you start to click the buttont, the gdb will halt the process at the breakpoint and from here you can inspect the register EAX and ECX.

As we got all of the key, we can start to code the decryption process like this:

 

Notice that there are no fancy encryption library, all we do is just setup two arrays that based on value that we got from each breakpoint, then each of the value we xor it. I found some pretty neat coding to do xor operation in one single line of code using zip function(). This can be achieved since the two arrays have the same length.

By running this code, you will get this string: backd00r$Mu$tAlw4ysBeF0rb1dd3n$$

That's all for today I hope you enjoy the post and if you like to see the full source code that contain the answer. You can get it from here: https://github.com/sleepyowl-beep/android_security_tutorial/blob/master/cybertruckchallenge19.zip

 

Comments

Popular posts from this blog

Having fun analyzing nginx log to find malicious attacker in the net (ง'̀-'́)ง (day 37)

  What makes you sleepless at night? is it because of a ghost or scary stories? is it because you have an important meeting tomorrow? or is it because you have an exam? For me, what keeps me up all night is that I keep thinking about what happens to a website that I just created, is it safe from an attacker (certainly not) or did I missing some security adjustments that lead to vulnerability? well I'm not the best secure programmer in the world, I'm still learning and there is a big possibility that I can make a mistake but for me, a mistake can be a valuable investment to myself or yourself to be better so from this idea, I want to know more about what attackers casually do when attacking a website. Here in this post, I'm going to show you how I analyzed attack to the website that I have permission to design and also some interesting findings that I could get from the analysis Background: All of this analysis comes from the traffic that is targeted to th

Utilize Pwntools for crafting ROP chain :') (day 69)

who doesn't like pwntools? it is a very versatile tool and can be customized according to our need using the python script but did you need to know that pwntools itself can help us to automatically craft a rop chain for us? so in this post, I will show you how to make rop chain less painful and make pwntools do all the heavy lifting. To demonstrate this I will use the binary challenge callme 64 bit from ropemporium link: https://ropemporium.com/challenge/callme.html Crashing the app: Like any other exploitation process, we need to crash the program by generating a long string pattern to determine the offset. based on the information from the above figure we can see that we required to provide 40 bytes of offset Fun stuff: now this where the fun stuff began write the following python script: as in the guideline of the challenged said we need to chain the function call by first to call the callme_one function, callme_two function and then callme_three funct

WriteUp PWN tarzan ROP UNICTF ಠ_ಠ (day 61)

So in this post, I'm going to talk about how to solve the Tarzan pwn challenge from UNICTF 2019. Back in the day when the competition is still going I couldn't finish it and don't have any clue to solve this but this time I was able to finish it :) Also in this post, we will be going to be heavily focused on how to utilize pwntools to construct a ROP chain. If you kinda confused about my explanation in this post you can refer to this following youtube video, link: https://www.youtube.com/watch?v=gWU2yOu0COk I build the python script based on this video Ok, let's get started! In this challenge, you will get two binary first go with tarzan and libc-2.29.so by providing .so file it tell us what version library that the target machine is using this could help us to do ROP chain. first, we run the Tarzan binary to get the basic idea of the program work and as you can see it just show you some text, newline and when you try to input something it doesn't gi