The challenge
Event | AUCTF 2020 |
Challenge name | Don't Break Me! |
Challenge category | Reversing |
Challenge points | 763 |
Executable | dont_break_me |
What does the executable do?
The executable asks for an input
.
user@pc:~$ ./dont_break_me
54 68 65 20 6d 61 6e 20 69 6e 20 62 6c 61 63 6b 20 66 6c
65 64 20 61 63 72 6f 73 73 20 74 68 65 20 64 65 73 65 72
74 2c 20 61 6e 64 20 74 68 65 20 67 75 6e 73 6c 69 6e 67
65 72 20 66 6f 6c 6c 6f 77 65 64 2e
Input: DUMMYINPUT
Not quite
Don’t worry about all those numbers, we don’t really need them to complete the challenge.
So, our input
is passed as an argument to sym.encrypt, then the encrypted string returned by the function
is compared with sym.imp.strcmp to an hardcoded key
and if they are equal, it prints the flag
.
We can see this flow through the radare2
’s output.
│ 0x000012f8 e853fdffff call sym.imp.fgets
│ ... skiping
│ 0x00001321 50 push eax
│ 0x00001322 e8a8010000 call sym.encrypt
│ ... skiping
│ 0x00001350 ff75ec push dword [s2]
│ 0x00001353 ff75e8 push dword [s1]
│ 0x00001356 e8d5fcffff call sym.imp.strcmp
│ 0x0000135e 85c0 test eax, eax
│ ┌─< 0x00001360 7507 jne 0x1369
│ │ 0x00001362 e889000000 call sym.print_flag
What can we do?
It seems that all we have to do is reverse the sym.encrypt
function, so we can finally provide the
correct input and get the flag
.
Unfortunately as we can see from the Official writeup, there are some antidebugging techniques, so the debug would be a litte tricky.
But luckly, looking at the ltrace
output, we can see the hardcoded key
compared to our encrypted input
.
user@kvm:~$ ltrace ./dont_break_me
printf("Input: "Input: ) = 7
fgets( DUMMYINPUT
"DUMMYINPUT\n", 8192, 0xf7f4b5c0)
... skiping
strcmp("SASRRWSXBIEBCMPX", "LOIIESZHOX")
printf("Not quite"Not quite)
So the key is "SASRRWSXBIEBCMPX"
and "DUMMYINPUT"
apparently becomes "LOIIESZHOX"
.
Now, we can try to input all printable ascii characters for each position of the key,
see what our input char becomes, and then compare it to that of the key until we get the right one.
Here is the ugly script xD..
import re
import string
from pwn import *
secret = ''
for index in range(16):
for guess in string.printable:
p = process('ltrace ./dont_break_me', shell=True)
data = p.recvuntil('fgets(')
p.sendline(guess)
data = p.recvuntil('(status 0) +++')
line = re.findall(r'strcmp.*', data)
try:
key, ch = re.findall(r'"[A-Z]+"|"[A-Z]"', line[0])
except:
p.close()
continue
key = key.replace('"','')
ch = ch.replace('"','')
if ch == key[index]:
secret += guess
p.close()
break
p.close()
log.info(secret)
After a long and ugly output, we get the key (c1cffqcnbgsbyuln
)
So the last step would be:
user@pc:~$ nc challenges.auctf.com 30005
54 68 65 20 6d 61 6e 20 69 6e 20 62 6c 61 63 6b 20 66 6c
65 64 20 61 63 72 6f 73 73 20 74 68 65 20 64 65 73 65 72
74 2c 20 61 6e 64 20 74 68 65 20 67 75 6e 73 6c 69 6e 67
65 72 20 66 6f 6c 6c 6f 77 65 64 2e
Input: c1cffqcnbgsbyuln
auctf{static_or_dyn@mIc?_12923}