ENPM809V

Linux Kernel Security Part 2

Agenda

  • Kernel API's Worth Taking a Look
  • Privilege Escalation
  • Escaping Seccomp
  • Writing Kernel Shellcode

Kernel API's Worth

Looking At

What have we learned last class?

  • How kernel modules work and how they are compiled
  • How they are loaded in
  • How we can interact with them

What have we learned last class?

  • Character Devices
  • Very basics on Interrupts
  • Threading
  • And much much more...

But the kernel has vulnerabilities too...

  • Memory Corruption is still a thing...
    • Stack/Heap Overflows
  • Race Conditions
  • And many many more...

Interesting API Calls...

  • copy_from_user(kernel_address, userspace_address, length);
    • Copies data from userspace into a buffer in the kernel space.
      • What happens if we don't do proper bounds checking?
      • What happens if we copy in too much data?
  • copy_to_user(userspace_address, kernel_address, length);
    • copies data from kernel and sends it back to userspace
      • What if we are able to send arbitrary data back to the user?

Interesting API Calls...

  • There are other ways that data can be exchanged (but don't recommend)
    • get_user_pages_fast((unsigned long)user_ptr, 1, 1, &page);
      • Fast access to a userspace page from the kernel
    • mmap - high performing shared buffers
static int my_mmap(struct file *filp, struct vm_area_struct *vma) {
    unsigned long pfn = virt_to_phys(kernel_buffer) >> PAGE_SHIFT;
    return remap_pfn_range(vma, vma->vm_start, pfn,
                           vma->vm_end - vma->vm_start,
                           vma->vm_page_prot);
}
int fd = open("/dev/mydevice", O_RDWR);
char *mapped = mmap(NULL, length, PROT_READ 
	| PROT_WRITE, MAP_SHARED, fd, 0);
strcpy(mapped, "hello from user space");

Interesting API Calls...

Our First Privilege Escalation

Let's Review Interacting with The Kernel

  • System Calls
    • Can be triggered at userspace
    • Very Difficult to modify without recompiling the kernel

Let's Review Interacting with The Kernel

  • Input/Output Control provides a much more flexible interface.

    • From kernel space: static long device_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param)

    • From user space:

 

  • Useful for setting and querying non-stream data (i.e., webcam resolution settings as opposed to webcam video stream).

int fd = open("/dev/pwn-college", 0);

ioctl(fd, COMMAND_CODE, &custom_data_structure);

Let's Review Interacting with The Kernel

  • One interaction mode is to handler read() and write() for your module's exposed file.
  • From kernel space:

 

 

  • From user space:

 

 

  • Useful for modules that deal with streams (i.e., a stream of
  • audio or video data).
static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
static ssize_t device_write(struct file *filp, const char *buf, size_t len, loff_t *off)
fd = open("/dev/pwn-college", 0)
read(fd, buffer, 128);

Why Is This Important?

Can Be A Vector for Exploitation

from pwn.college - baby_kernel2.0 challenge

What if contains something else?

from pwn.college

How can we modify the cred structure?

  • Credentials should be immutable, but can be replaced
  • struct cred * prepare_kernel_cred(struct task_struct *reference_task_struct)
    • Creates a cred structure with the credentials of a particular task
  • commit_creds(struct cred *)
    • Commits the credentials to the current task.

How can we modify the cred structure?

  • What if we pass NULL to prepare_kernel_creds?
    • It gives the creds struct of the root process!
  • What if we commit this?
    • We become root!

commit_creds(prepare_kernel_creds(NULL))

Let's Practice This

Bypassing Seccomp (Again)

What have we learned?

  • Seccomp is a system call that restricts user space programs from making certain system calls
  • If we do not properly utilize seccomp, we can bypass it with injection
    • Children only inherit seccomp rules if seccomp is called before the fork
  • Otherwise it is not possible to bypass

Except...

  • There is a way to disable it from the kernel!
  • Let's look back at the task_struct. There is more in here.

Except...

  • Under task_struct->thread_info, there is a flags field.
  • One of the bits is TIF_SECCOMP (this determines if SECCOMP is enabled/disabled)

Except...

Note: this is as of kernel version 5.4

Except...

https://elixir.bootlin.com/linux/v5.4/source/arch/x86/include/asm/thread_info.h

https://elixir.bootlin.com/linux/v5.4/source/include/linux/sched.h#L624 - task struct

Where does it get checked?

https://elixir.bootlin.com/linux/v5.4/source/arch/x86/entry/vsyscall/vsyscall_64.c

Where does it get checked?

This is where the filtering happens and ensure whether the system call actually happens.

 

https://elixir.bootlin.com/linux/v5.4/source/include/linux/seccomp.h#L35

https://elixir.bootlin.com/linux/v5.4/source/kernel/seccomp.c#L920

How do we get around this?

  • We need to set the TIF_SECCOMP bit in task_struct->thread_info.flags to 0.
    • We do this by setting the following:
      • current->thread_info.flags &= ~(1 << TIF_SECCOMP)
      • Current is the task struct that we are currently working in. This is stored in the gs register (important for shellcoding later)
    • Plan of attack
      • Access current->thread_info.flags via the gs register
      • clear the TIF_SECCOMP bit
      • Win!
    • Only issue is that our children will still be seccomped (if enabled)

Writing Kernel Shellcode

How did we do this before?

        /* push b'/flag\x00' */
        mov rax, 0x101010101010101
        push rax
        mov rax, 0x101010101010101 ^ 0x67616c662f
        xor [rsp], rax
        /* call open('rsp', 'O_RDONLY', 'rdx') */
        push 2 /* 2 */
        pop rax
        mov rdi, rsp
        xor esi, esi /* O_RDONLY */
        syscall
        /* call sendfile(1, 'rax', 0, 0x7fffffff) */
        mov r10d, 0x7fffffff
        mov rsi, rax
        push 40 /* 0x28 */
        pop rax
        push 1
        pop rdi
        cdq /* rdx=0 */
        syscall

Whether we used shellcraft to help us or not, we ended up making lots of system calls to get our desired behavior.

 

Why?

  • Always accessible
  • Gave us deep functionality
  • Gave us enough to execute something else

Not the case for the Kernel!!!

  • Syscalls are userspace interfaces to the kernel!
    • Execution immediately jumps to syscall_entry function in the kernel after calling the syscall instruction
    • Assures that it is being called from userspace
  • We need to utilize the API's already given to us!
    • This means we need to locate the addresses of functions and methods we need to execute.

Not the case for the Kernel!!!

  • If we ran `pwn asm -c amd64 "mov rdi, 0; call prepare_kernel_creds` what will happen?
    • Doesn't know what to do!
  • How do we find the address of prepare_kernel_creds?
    • if KASLR is disabled - get the address from /proc/kallsyms on an identical system
    • if KASLR is enabled - You will need to leak the kernel address and calculate the offset (just like in userspace)
      • Think of this like ret2libc

Figuring out Offsets in Kernel Functions

  • You could in theory do this manually ...
    • Way too difficult (especially with randomize_struct_offsets)
  • Create a kernel module to figure this out!
    • For homeworks, use vm build in practice mode to help you!
    • Reverse Engineer it to see how actions work in assembly.
    • Re-implement it in shellcode!

One important thing

  • Make sure you have cleanup shellcode if it's needed
  • In userspace, we can segfault and move on (unless we are trying to hide)
  • In the kernel, we can't do that (we will crash the system).
    • Ensure registers are in the proper place!
    • If you are calling your shellcode like it's a function, ensure you return
      • Maybe even have a prologue and epilogue

More Practice

Practice at pwn.college

  • System Security Dojo - Kernel Security Module
  • This lecture is based on this
  • Best content for getting into the basics of Kernel Security

 

Next week we will focus on stack exploitation!

Homework

Create a Character Device

  • You are going to write an encryption character device (basic XOR and shift encryption)
  • Do development in either your local environment or pwn.college
  • Ensure you pass the tests on Github Classroom!
    • No flag for this one.

Privilege Escalation Shellcode

  • You are going to write shellcode that bypasses seccomp and elevates you to root!
  • Get flag and win

ENPM809V - Kernel Hacking Part 2

By Ragnar Security

ENPM809V - Kernel Hacking Part 2

  • 151