Use cutter to solve a simple crackme.
Information
- category : reverse
- points : 0
Description
Das Blech umdrehen!
1 file: maybe.bin
Writeup
Let’s start by loading the binary with cutter and check the binary’s information
using the Dashboard
window.
The binary is a 64 bit ELF. PIE, NX, Stack Canary are enabled and it’s stripped.
We can check what functions have the program in the Functions
tab.
Interesting… we have two init
, and two fini
(usually there are only one
of them).
init\(x\) with \(x \in (0, 10)\) contains special code that is executed
before the main
function.
While fini\(x\) are executed after the main
.
Let’s see what init1
does:
In this case I renamed by hand the variable i
(right click on the
variable -> Retype function local var, or press Y
on the var)
and I commented some lines (press ;
).
The code is trivial, is reversing the string (badly) since the half of the characters will be symmetrically moved, while the other half will be lost:
Before init1
:
junior-totally_the_flag_or_maybe_not
After:
ton_ebyam_ro_galf__flag_or_maybe_not
Now let’s move into main:
The decompiled with r2ghidra-dec
is the following:
undefined8 main(undefined8 argc, char **argv)
{
int32_t iVar1;
uint8_t uVar2;
uint32_t uVar3;
char **_argv;
int64_t _argc;
undefined8 i;
i._0_4_ = 0;
while ((int32_t)i < 0x24
// start with i = 0) {
"junior-totally_the_flag_or_maybe_not"[(int32_t)i + 0x40] = argv[1][(int32_t)i];
uVar3 = (uint32_t)((int32_t)"junior-totally_the_flag_or_maybe_not"[(int32_t)i] >> 0x1f) >> 0x18;
iVar1 = (((int32_t)"junior-totally_the_flag_or_maybe_not"[(int32_t)i] + uVar3 & 0xff) - uVar3) + 0x100;
uVar2 = (uint8_t)(iVar1 >> 0x37);
"junior-totally_the_flag_or_maybe_not"[(int32_t)i] = ((char)iVar1 + (uVar2 >> 1) & 0x7f) - (uVar2 >> 1);
"junior-totally_the_flag_or_maybe_not"[(int32_t)i] = "junior-totally_the_flag_or_maybe_not"[(int32_t)i];
i._0_4_ = (int32_t)i + 1;
}
sym.imp.puts(0x9b1); // str.wrong
return 0;
}
As I showed in the precedent graph, the function will print or
correct
or wrong
, but the decompiler shows that will be
printed wrong
unconditionally.
Why?
Because the program will check if the address of the string
str.junior_totally
is equal to the address of the string
str.this_should
, but there aren’t instructions
that modifies the first string’s address, so this check will
always puts("wrong")
.
Possible Problem
The string str.junior_totally
could be modified by our input?
(argv[1]).
There are two ways to verify if the code in the main change the bytes in the string.
- Understand the code, maybe reproduce the code in a
c
program and test it. - Use Cutter’s debugger (easier).
Let’s start with 1, I used the decompiled code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
char totally [] = "this_should_totally_be_a_hering_on_a_kuchenblech";
// remember that the string is 'reversed'
char junior [64] = "ton_ebyam_ro_galf__flag_or_maybe_not";
int main(int argc, char **argv)
{
int index = 0;
u_int8_t var2;
u_int32_t var3;
int32_t var1;
char c;
printf("%d\n", (char *)((int64_t)(2 * 2 + 1) + 0x2010a0));
while(index < 0x24)
{
junior[index + 0x40] = argv[1][(int32_t)index];
var3 = (u_int32_t)((int32_t)junior[(int32_t)index] >> 0x1f) >> 0x18;
var1 = (((int32_t)junior[(int32_t)index] + var3 & 0xff) - var3) + 0x100;
var2 = (u_int8_t)(var1 >> 0x37);
junior[(int32_t)index] = ((char)var1 + (var2 >> 1) & 0x7f) - (var2 >> 1);
index += 1;
}
printf("%s\n", junior);
return 0;
}
$ gcc test.c -o test
$ ./test aaaaaaaaaaaaaaaaaaaaaaaaaaaa
ton_ebyam_ro_galf__flag_or_maybe_not
$ ./test sknvdfvbusivndsnvdsovbsdovbdf
ton_ebyam_ro_galf__flag_or_maybe_not
$ ./test cdsklnclkdscev2984385205832905903285
ton_ebyam_ro_galf__flag_or_maybe_not
Nothing change, so probably it doesn’t change the string
in anyway. I personally studied the algorithm and I’m pretty sure
that it doesn’t change the input since there are a
lots of shr
which zeroize the various variable var\(x\).
The second option is easier, we can just put a breakpoint (press F2) after the first while, and see if the string changes.
And nothing change, perfect.
Wait ok… but it prints wrong.
As I said before there are other code which is executed after the main
.
Let’s analyze the first part of fini1
.
Basically it does:
str.junior_totally
=
str.junior_totally xor argv[1]
While the second part:
It compares the result from the first part with an array
(str.002010a0
), if they are the same the flag var will be set to 1
and it will print “correct”.
To view this array we can use the hexdump
(right click on 0x002010a0
-> 0x002010a0
-> new hexdump).
Then we can parse the array:
.
Now what
We can write a simple script to find the right argument to pass to the binary since the xor operation is very easy to invert.
Exploit
#!/usr/bin/env python3
arr = [0,30,0,26,0,0,0,54,0,10,0,16,0,84,0,0,0,1,0,51,0,23,0,28,0,0,0,9,0,
20,0,30,0,57,0,52,0,42,0,5,0,4,0,4,0,9,0,61,0,3,0,23,0,60,0,5,0,62,
0,20,0,3,0,3,0,54,0,15,0,78,0,85]
def main():
totally = "ton_ebyam_ro_galf__flag_or_maybe_not"
flag = ""
for j in range(0, len(totally)):
flag += chr(ord(totally[j]) ^ arr[(j * 2) + 1])
print(flag)
if __name__ == '__main__':
main()
./maybe.bin $(./exploit.py)
wrong!
aber es ist nur noch eine sache von sekunden!
correct!
Flag
junior-alles_nur_kuchenblech_mafia!!