DEV Community

wireless90
wireless90

Posted on

Off by One [Android Internals CTF Ex8]

Get the executable here

Instructions

  • Give the program the correct argument so it will print the flag.
  • Do not reverse the decrypt function or modify the program.

Let's Begin

Let's take a look what type of file it is.

┌──(razali㉿razali)-[~/…/Ivy/AndroidVulnResearch/ctf/offByOne] └─$ file a.out a.out: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped 
Enter fullscreen mode Exit fullscreen mode

It is an arm file, so let's push it to our android device.

──(razali㉿razali)-[~/…/Ivy/AndroidVulnResearch/ctf/offByOne] └─$ adb push a.out /data/local/tmp * daemon not running; starting now at tcp:5037 * daemon started successfully a.out: 1 file pushed. 0.2 MB/s (3392956 bytes in 13.310s) 
Enter fullscreen mode Exit fullscreen mode

Next, try to run it in our android device.

126|root@hammerhead:/data/local/tmp # chmod +x a.out root@hammerhead:/data/local/tmp # ./a.out usage: ./a.out <argument> 
Enter fullscreen mode Exit fullscreen mode

It requires an argument. I proceeded to give it a short and a long argument.

root@hammerhead:/data/local/tmp # ./a.out aaaa You failed :( root@hammerhead:/data/local/tmp # /a.out aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  You failed :( 
Enter fullscreen mode Exit fullscreen mode

Both resulted in a failure.

Next, let's proceed to perform our static analysis using IDA.

image

It seems like a very small program. So let's begin reversing from the start.

When performing static analysis, it is best to put lots of comments for each block of code.

I will be showing both IDA view and nasm markdown view as it might be more clearer.

Let's look at the beginning of the main function.
image

; int __cdecl main(int argc, const char **argv, const char **envp) EXPORT main main var_124= -0x124 var_120= -0x120 var_11C= -0x11C var_115= -0x115 var_15= -0x15 var_14= -0x14 var_10= -0x10 var_C= -0xC PUSH {R4,R5,R11,LR} ADD R11, SP, #8 SUB SP, SP, #0x120 MOV R2, #0 STR R2, [R11,#var_C] STR R0, [R11,#var_10] STR R1, [R11,#var_14] LDR R0, [R11,#var_10] CMP R0, #2 BGE loc_857C 
Enter fullscreen mode Exit fullscreen mode

We know that,
R0 - represents argc
R1 - represents argv
R2 - represents envp

Hence, lets rename our variables and add comments. The above block of code now looks like,

; Attributes: bp-based frame ; int __cdecl main(int argc, const char **argv, const char **envp) EXPORT main main var_124= -0x124 var_120= -0x120 var_11C= -0x11C var_115= -0x115 var_15= -0x15 argv= -0x14 argc= -0x10 envp= -0xC PUSH {R4,R5,R11,LR} ADD R11, SP, #8 SUB SP, SP, #0x120 MOV R2, #0 STR R2, [R11,#envp] STR R0, [R11,#argc] STR R1, [R11,#argv] LDR R0, [R11,#argc] CMP R0, #2 ; Checking if argc is >=2 BGE loc_857C 
Enter fullscreen mode Exit fullscreen mode

image
Since it branches based on whether we put in arguments, in our case, we are supplying 1 argument, which makes the argc count 2. Hence the program branches to the right.

Lets take a look at the block on the right.

image

loc_857C LDR R0, =(byte_6325B - 0x8588) ADD R0, PC, R0 ; byte_6325B LDRB R0, [R0] STRB R0, [R11,#var_15] LDR R0, [R11,#argv] LDR R0, [R0,#4] ; s BL strlen CMP R0, #0x100 BLS loc_85BC 
Enter fullscreen mode Exit fullscreen mode

Reversing it produces,

This code executes when there are atleast 1 cmd line argument loc_857C LDR R0, =(byte_6325B - 0x8588) ADD R0, PC, R0 ; byte_6325B LDRB R0, [R0] STRB R0, [R11,#var_15] LDR R0, [R11,#argv] ; ; The below line gets the first argument LDR R0, [R0,#4] ; s BL strlen ; gets the first argument and performs a strlen on it CMP R0, #0x100 ; Length of the first argument is compared against 256 BLS loc_85BC ; Branch to the right if LOWER OR SAME than 256 
Enter fullscreen mode Exit fullscreen mode

image

Looking at the code on the left block, it says Length higher than 256 is not allowed. What if the length is 256? We need to test the edge cases.

Let's now take a look at the block on the right.

image

loc_85BC ADD R0, SP, #0x128+var_115 LDR R1, [R11,#argv] LDR R1, [R1,#4] BL strcpy LDRB R1, [R11,#var_15] CMP R1, #0 BNE loc_8604 
Enter fullscreen mode Exit fullscreen mode

Reversing it gives,

Copies the first argument to the destination buffer loc_85BC ; dest to strcpy ADD R0, SP, #0x128+destBuffer LDR R1, [R11,#argv] LDR R1, [R1,#4] ; LOAD THE FIRST ARGUMENT as src to strcpy BL strcpy LDRB R1, [R11,#var_15] CMP R1, #0 BNE loc_8604 ; if R1 is zero, we will get the flag! 
Enter fullscreen mode Exit fullscreen mode

image

We can see that if R1 is Not Equal to 0, we will fail to the right. Hence R1 has to be 0 in order for us to get the flag.

R1 gets its value from the stack variable var_15.
var_15 gets its value from the previous block, from a readonly memory location named =(byte_6325B - 0x8588).

Let's hover our mouse over it to see what the .rodata contains.

image

It simply contains the number, 1.

Hence, we know that var_15 will contains the number 1 but we somehow need to make it become 0 in order for us to get the flag.

Lets take a look at the stack location of the variable, right at the start of main.

image

The code we looked at allows a max of 256 bytes to be written and the 2 variables are exactly 256 bytes from each other. Hence we need at least 257 characters to override into var_15. Moreover, the character that overrides var_15 has to be 0 in order for us to get the flag.

The flaw here relies on strcpy. If we were to pass 256 characters, strcpy copies all 256 characters into the buffer and it also adds a NULL, \0, into the next position, the 257th position, to terminate the string. This 257th position happens to be our var_15.

Hence from our host machine, we can use python to quickly generate a string of length 256.

┌──(razali㉿razali)-[~/…/Ivy/AndroidVulnResearch/ctf/offByOne] └─$ python -c 'print "a"*256' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 
Enter fullscreen mode Exit fullscreen mode

Then we can copy this string to our android.

root@hammerhead:/data/local/tmp # ./a.out aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa You did it! The flag is: "off_by_one" 
Enter fullscreen mode Exit fullscreen mode

And we got the flag.

Top comments (0)