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
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)
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>
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 :(
Both resulted in a failure.
Next, let's proceed to perform our static analysis using IDA.
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.

; 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
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

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.
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
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
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.
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
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!
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.
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.
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
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"
And we got the flag.







Top comments (0)