AUCTF 2020
AUCTF 2020 was put on Auburn University in April 2020. I've included my writeups for the reversing challenges I did below.
Cracker Barrel
I found a USB drive under the checkers board at cracker barrel. My friends told me not to plug it in but surely nothing bad is on it?
I found this file, but I can't seem to unlock it's secrets. Can you help me out?
Also.. once you think you've got it I think you should try to connect to challenges.auctf.com at port 30000 not sure what that means, but it written on the flash drive..
...
│ 0x0000137b e8a0fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ 0x00001380 488d85f0dfff. lea rax, [var_2010h]
│ 0x00001387 4889c7 mov rdi, rax
│ 0x0000138a e8dafeffff call sym.remove_newline
│ 0x0000138f 488b85e8dfff. mov rax, qword [var_2018h]
│ 0x00001396 4889c7 mov rdi, rax
│ 0x00001399 e8c8000000 call sym.check_1
│ 0x0000139e 85c0 test eax, eax
│ ┌─< 0x000013a0 0f84a5000000 je 0x144b
│ │ 0x000013a6 488d3d7b0c00. lea rdi, str.You_have_passed_the_first_test__Now_I_need_another_key ; 0x2028 ; "You have passed the first test! Now I need another key!" ; const char *s
│ │ 0x000013ad e82efdffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x000013b2 488b15672c00. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ │ ; [0x4020:8]=0 ; FILE *stream
│ │ 0x000013b9 488b85e8dfff. mov rax, qword [var_2018h]
│ │ 0x000013c0 be00200000 mov esi, obj._IO_stdin_used ; 0x2000 ; int size
│ │ 0x000013c5 4889c7 mov rdi, rax ; char *s
│ │ 0x000013c8 e853fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ │ 0x000013cd 488b85e8dfff. mov rax, qword [var_2018h]
│ │ 0x000013d4 4889c7 mov rdi, rax
│ │ 0x000013d7 e88dfeffff call sym.remove_newline
│ │ 0x000013dc 488b85e8dfff. mov rax, qword [var_2018h]
│ │ 0x000013e3 4889c7 mov rdi, rax
│ │ 0x000013e6 e8e4000000 call sym.check_2
│ │ 0x000013eb 85c0 test eax, eax
│ ┌──< 0x000013ed 745c je 0x144b
│ ││ 0x000013ef 488d3d6a0c00. lea rdi, str.Nice_work__You_ve_passes_the_second_test__we_aren_t_done_yet ; 0x2060 ; "Nice work! You've passes the second test, we aren't done yet!" ; const char *s
│ ││ 0x000013f6 e8e5fcffff call sym.imp.puts ; int puts(const char *s)
│ ││ 0x000013fb 488b151e2c00. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ││ ; [0x4020:8]=0 ; FILE *stream
│ ││ 0x00001402 488b85e8dfff. mov rax, qword [var_2018h]
│ ││ 0x00001409 be00200000 mov esi, obj._IO_stdin_used ; 0x2000 ; int size
│ ││ 0x0000140e 4889c7 mov rdi, rax ; char *s
│ ││ 0x00001411 e80afdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ ││ 0x00001416 488b85e8dfff. mov rax, qword [var_2018h]
│ ││ 0x0000141d 4889c7 mov rdi, rax
│ ││ 0x00001420 e844feffff call sym.remove_newline
│ ││ 0x00001425 488b85e8dfff. mov rax, qword [var_2018h]
│ ││ 0x0000142c 4889c7 mov rdi, rax ; char *arg1
│ ││ 0x0000142f e83c010000 call sym.check_3
│ ││ 0x00001434 85c0 test eax, eax
│ ┌───< 0x00001436 7413 je 0x144b
│ │││ 0x00001438 488d3d610c00. lea rdi, str.Congrats_you_finished__Here_is_your_flag ; 0x20a0 ; "Congrats you finished! Here is your flag!" ; const char *s
│ │││ 0x0000143f e89cfcffff call sym.imp.puts ; int puts(const char *s)
...
As can be seen from this excerpt of the check function, the binary will ask for three strings and give us the flag if they are all correct.
[0x00001180]> pdf@sym.check_1
; CALL XREF from sym.check @ 0x1399
┌ 105: sym.check_1 (char *arg1);
│ ; var char *s1 @ rbp-0x18
│ ; var char *s2 @ rbp-0x10
│ ; var char *var_8h @ rbp-0x8
│ ; arg char *arg1 @ rdi
│ 0x00001466 f30f1efa endbr64
│ 0x0000146a 55 push rbp
│ 0x0000146b 4889e5 mov rbp, rsp
│ 0x0000146e 4883ec20 sub rsp, 0x20
│ 0x00001472 48897de8 mov qword [s1], rdi ; arg1
│ 0x00001476 488d054d0c00. lea rax, str.starwars ; 0x20ca ; "starwars"
│ 0x0000147d 488945f0 mov qword [s2], rax
│ 0x00001481 488d054b0c00. lea rax, str.startrek ; 0x20d3 ; "startrek"
│ 0x00001488 488945f8 mov qword [var_8h], rax
│ 0x0000148c 488b55f0 mov rdx, qword [s2]
│ 0x00001490 488b45e8 mov rax, qword [s1]
│ 0x00001494 4889d6 mov rsi, rdx ; const char *s2
│ 0x00001497 4889c7 mov rdi, rax ; const char *s1
│ 0x0000149a e891fcffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
│ 0x0000149f 85c0 test eax, eax
│ ┌─< 0x000014a1 7525 jne 0x14c8
│ │ 0x000014a3 488b55f8 mov rdx, qword [var_8h]
│ │ 0x000014a7 488b45e8 mov rax, qword [s1]
│ │ 0x000014ab 4889d6 mov rsi, rdx ; const char *s2
│ │ 0x000014ae 4889c7 mov rdi, rax ; const char *s1
│ │ 0x000014b1 e87afcffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
│ │ 0x000014b6 85c0 test eax, eax
│ ┌──< 0x000014b8 7507 jne 0x14c1
│ ││ 0x000014ba b800000000 mov eax, 0
│ ┌───< 0x000014bf eb0c jmp 0x14cd
│ │││ ; CODE XREF from sym.check_1 @ 0x14b8
│ │└──> 0x000014c1 b801000000 mov eax, 1
│ │┌──< 0x000014c6 eb05 jmp 0x14cd
│ │││ ; CODE XREF from sym.check_1 @ 0x14a1
│ ││└─> 0x000014c8 b800000000 mov eax, 0
│ ││ ; CODE XREFS from sym.check_1 @ 0x14bf, 0x14c6
│ └└──> 0x000014cd c9 leave
└ 0x000014ce c3 ret
The function check_1 will jump to 0x14c8 (aka return false) if your input is not equal to the string "starwars" and then return true if your input is not equal to the string "startrek". The input "starwars" passes both of these constraints and will pass the first checking function. I didn't actually reverse check_2 and check_3 because I bumped my keyboard and noticed that an empty string would pass both of them.
flag: auctf{w3lc0m3_to_R3_1021}
mobile0
Hey, look its an android file. Can you find the flag?
We are provided with an android apk file mobile0.apk
and told to find the flag. The flag can be found with strings mobile0.apk | grep auctf
.
flag: auctf{m0b1le_r3v3rs1ng!!}
mobile1
My friend sent this file to me and said that there was a flag in it. Can you help me?
We are provided with an ipa file - which is an iOS app store package. These are compressed, rather like java jars, so you need to unzip it first. I was pretty invested in not actually reversing the code for this so I ran strings on a few files inside and found the flag in the root info.plist file.
flag: auctf{i0s_r3v3rs1ng_1s_1nt3r3st1ng}
sora
This obnoxious kid with spiky hair keeps telling me his key can open all doors.
Can you generate a key to open this program before he does?
Connect to challenges.auctf.com 30004
I opened up the binary with radare2 to determine the key length and target location and then solved it with angr. We can see from this excerpt of the main function that it reads in 0x1e bytes from stdin and the program calls print_flag at 0x12aa
...
│ 0x0000126d 488945c8 mov qword [var_38h], rax
│ 0x00001271 488d3da10d00. lea rdi, str.Give_me_a_key ; 0x2019 ; "Give me a key!" ; const char *s
│ 0x00001278 e843feffff call sym.imp.puts ; int puts(const char *s)
│ 0x0000127d 488b15ac2d00. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ; [0x4030:8]=0 ; FILE *stream
│ 0x00001284 488d45d0 lea rax, [s]
│ 0x00001288 be1e000000 mov esi, 0x1e ; int size
│ 0x0000128d 4889c7 mov rdi, rax ; char *s
│ 0x00001290 e86bfeffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ 0x00001295 488b45c8 mov rax, qword [var_38h]
│ 0x00001299 4889c7 mov rdi, rax
│ 0x0000129c e83c000000 call sym.encrypt
│ 0x000012a1 85c0 test eax, eax
│ ┌─< 0x000012a3 7411 je 0x12b6
│ │ 0x000012a5 b800000000 mov eax, 0
│ │ 0x000012aa e8d9000000 call sym.print_flag
│ │ 0x000012af b800000000 mov eax, 0
│ ┌──< 0x000012b4 eb11 jmp 0x12c7
│ ││ ; CODE XREF from main @ 0x12a3
│ │└─> 0x000012b6 488d3d6b0d00. lea rdi, str.That_s_not_it ; 0x2028 ; "That's not it!" ; const char *s
│ │ 0x000012bd e8fefdffff call sym.imp.puts ; int puts(const char *s)
...
import angr
import sys
from claripy import *
from pwn import *
def main(argv):
path_to_binary = "sora"
project = angr.Project(path_to_binary, load_options={'main_opts': {'base_addr': 0x0}})
x = BVS('x', 0x1e * 8)
initial_state = project.factory.entry_state(stdin=x)
# constrain to printable characters
def char(state, byte):
return initial_state.solver.And(byte <= '~', byte >= ' ')
for c in x.chop(8):
initial_state.solver.add(char(initial_state, c))
simulation = project.factory.simgr(initial_state)
simulation.explore(find=0x000012aa)
if simulation.found:
solution_state = simulation.found[0]
r = remote('challenges.auctf.com',30004)
r.sendline(solution_state.solver.eval(x, cast_to=bytes))
print(r.recvall())
else:
print(simulation.stashes)
raise Exception('Could not find the solution')
if __name__ == '__main__':
main(sys.argv)
flag: auctf{that_w@s_2_ezy_29302}
dont_break_me
from pwn import *
key = "MDULCTKBSJARIZQHYPGXOFWNEV"
comp = "SASRRWSXBIEBCMPX"
password = ""
for i in range(16):
password += chr(0x41 + key.find(comp[i]))
r = remote('challenges.auctf.com',30005)
r.sendline(password)
print(r.recvall())
this challenge encrypts your input and then compares it against a constant, failing if it detects any CC bytes. The encryption function is a simple polyalphabetic cipher that can be leaked with ltrace.
flag: auctf{static_or_dyn@mIc?_12923}