9

syzkaller 环境搭建

 2 years ago
source link: https://kiprey.github.io/2021/11/syzkaller_1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Syzkaller 是一个无监督的覆盖引导的内核 fuzzer,是目前类似里程碑一样的 fuzz 。

项目地址 - syzkaller

本人对 syzkaller 的工作机制比较感兴趣,同时课题组也会经常用到 syzkaller,因此了解 syzkaller 是一个必不可少的过程。

在研究内核fuzz的工作机制之前,我们需要先学会如何搭建它的环境。

二、环境搭建

  • syzkaller 使用 Go 语言编写,因此需要获取 go 语言的 tool chain。不过我倒没怎么在这上面踩过坑,直接

    sudo apt-get install golang # 此时的 golang 是 1.15 版
    make all

    就完成了所有 syzkaller 程序的编译。

  • qemu 安装不多说

    sudo apt install qemu-system-x86
  • 接下来是获取内核源码,要拉n久:

    git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git

    本人使用的 git commit id: a90af8f15bdc9449ee2d24e1d73fa3f7e8633f81

    参考一下 syzkaller 官方文档,先 make defconfig 生成默认的内核编译配置,之后手动在 .config 文件中添加以下选项:

    添加这些选项的目的是为了更好的被测试。

    # Coverage collection.
    CONFIG_KCOV=y

    # Debug info for symbolization.
    CONFIG_DEBUG_INFO=y

    # Memory bug detector
    CONFIG_KASAN=y
    CONFIG_KASAN_INLINE=y

    # Required for Debian Stretch
    CONFIG_CONFIGFS_FS=y
    CONFIG_SECURITYFS=y

    之后重新 make olddefconfig 更新编译参数,并执行以下命令以编译完整内核(包括驱动):

    make -j `nproc`
  • 配置Imgage 镜像

    首先安装 debootstrap,它是 linux 下用来构建一套基本根文件系统的工具。

    sudo apt-get install debootstrap

    之后在 linux 项目目录下键入以下命令,以创建 Debian Stretch Linux image

    mkdir image
    cd image/
    wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.sh
    chmod +x create-image.sh
    ./create-image.sh

    创建好后,同级目录下会多出几个文件:

    image-20211121114611675

  • 上述操作全部完成后,执行以下命令来尝试启动

    注意自行替换 kernel 和drive file 的路径。

    qemu-system-x86_64 \
    -m 2G \
    -smp 2 \
    -kernel /usr/class/linux/arch/x86/boot/bzImage \
    -append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
    -drive file=/usr/class/linux/image/stretch.img,format=raw \
    -net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \
    -net nic,model=e1000 \
    -enable-kvm \
    -nographic \
    -pidfile vm.pid \
    2>&1 | tee vm.log

    如果成功的话,就会出现 syzkaller login。用户名键入root,无需输入密码,即可进入终端:

    image-20211121115312828

    之后我们还可以再另外一个终端里键入以下命令以进入 ssh

    之所以还要测试 ssh 能否成功工作,是因为 syzkaller 会用到 ssh。

    ssh -i $IMAGE/stretch.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost

    image-20211121115439899

    确认无误后,直接执行 poweroff 关闭 kernel。

  • 尝试执行 syzkaller-manager

    先新建一个my.cfg,这个文件将是 syzkaller 的配置文件,内容如下:

    务必自行替换里面的各种路径。

    {
    "target": "linux/amd64",
    "http": "127.0.0.1:56741",
    "workdir": "/usr/class/syzkaller/workdir",
    "kernel_obj": "/usr/class/linux",
    "image": "/usr/class/linux/image/stretch.img",
    "sshkey": "/usr/class/linux/image/stretch.id_rsa",
    "syzkaller": "/usr/class/syzkaller",
    "procs": 8,
    "type": "qemu",
    "vm": {
    "count": 4,
    "kernel": "/usr/class/linux/arch/x86/boot/bzImage",
    "cpu": 2,
    "mem": 2048
    }
    }

    之后开跑看看:

    cd syzkaller
    mkdir workdir
    ./bin/syz-manager -config=my.cfg

    貌似跑的相当顺利:

    image-20211121120608962

    syzkaller Web端 也工作正常:

    image-20211121120652065

    很好,环境这边没咋踩坑(笑)

三、crash 测试

俗话说,是骡子是马拉出来溜溜,我们将一个带有漏洞的驱动编入 kernel,之后尝试让 syzkaller fuzz出来。

这里主要参考链接 github 中的 syzkaller crash demo 演示。

1. 将漏洞驱动编译进 kernel

这里,我们使用的是其他人已经准备好的漏洞驱动 test.c

简单讲讲这个漏洞驱动:

  • 初始加载驱动时,会在 /proc 文件夹下创建文件 proc。而针对于该 proc 的读写操作,内核实际会调用 proc_* 系列函数来进行处理。

    #define MY_DEV_NAME "test"
    static struct file_operations a = {
    .open = proc_open,
    .read = proc_read,
    .write = proc_write,
    };


    static int __init mod_init(void)
    {
    struct proc_dir_entry *test_entry;
    const struct file_operations *proc_fops = &a;
    printk(DEBUG_FLAG":proc init start!\n");

    // 创建 proc
    test_entry = proc_create(MY_DEV_NAME, S_IRUGO|S_IWUGO, NULL, proc_fops);
    if(!test_entry)
    printk(DEBUG_FLAG":there is somethings wrong!\n");

    printk(DEBUG_FLAG":proc init over!\n");
    return 0;
    }
  • 驱动中的漏洞代码部分如下所示,可以看到当我们针对 proc 文件进行写入操作时,会造成内核堆溢出:

    static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff)
    {
    char *c = kmalloc(512, GFP_KERNEL);
    // 溢出,原先只有 512 字节,但是复制了 4096
    copy_from_user(c, proc_user, 4096);
    printk(DEBUG_FLAG":into write!\n");
    return 0;
    }

尝试将其加载进内核。

# 下载源代码至 linux/drivers/char/test.c
proxychains wget -v https://github.com/hardenedlinux/Debian-GNU-Linux-Profiles/raw/master/docs/harbian_qa/fuzz_testing/test.c -O linux/drivers/char/test.c
# 修改 Makefile
echo "obj-y += test.o" >> linux/drivers/char/Makefile
# 尝试编译
make

报错,提示:

image-20211121142921325

这里主要有两个地方出问题:

  • 原先代码中使用的 struct file_operations 需要替换成 struct proc_ops

  • linux kernel 编译时会进行静态编译检测,这类通过静态推断便能得出的溢出问题将不予编译:

    char *c = kmalloc(512, GFP_KERNEL);
    copy_from_user(c, proc_user, 4096);

    绕过静态检测也很简单,引入一个可变量即可。

因此我简单修改了一下这个驱动文件,修改后如下所示:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define MY_DEV_NAME "test"
#define DEBUG_FLAG "PROC_DEV"

static ssize_t proc_read (struct file *proc_file, char __user *proc_user, size_t n, loff_t *loff);
static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff);
static int proc_open (struct inode *proc_inode, struct file *proc_file);
static struct proc_ops a = {
.proc_open = proc_open,
.proc_read = proc_read,
.proc_write = proc_write,
};


static int __init mod_init(void)
{
struct proc_dir_entry *test_entry;
const struct proc_ops *proc_fops = &a;
printk(DEBUG_FLAG":proc init start!\n");

test_entry = proc_create(MY_DEV_NAME, S_IRUGO|S_IWUGO, NULL, proc_fops);
if(!test_entry)
printk(DEBUG_FLAG":there is somethings wrong!\n");

printk(DEBUG_FLAG":proc init over!\n");
return 0;
}

static ssize_t proc_read (struct file *proc_file, char __user *proc_user, size_t n, loff_t *loff)
{
printk(DEBUG_FLAG":finish copy_from_use,the string of newbuf is");

return 0;
}

static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff)
{
char *c = kmalloc(n + 512, GFP_KERNEL);

size_t size = copy_from_user(c, proc_user, n + 4096);
printk(DEBUG_FLAG":into write %ld!\n", size);
return 0;
}

int proc_open (struct inode *proc_inode, struct file *proc_file)
{
printk(DEBUG_FLAG":into open!\n");
return 0;
}

module_init(mod_init);

然后make一下,之后就可以在 /proc/test 下找到目标 proc:

root@syzkaller:~# ls -al /proc/test 
-rw-rw-rw-. 1 root root 0 Nov 21 06:43 /proc/test

很好,漏洞驱动已经成功加载进 kernel 中,接下来直接 poweroff 掉,开始配置 syzkaller。

2. 配置 syzkaller 规则

syzkaller/sys/linux/ 创建一个对应于这个漏洞驱动的处理规则 test.txt(名字取什么无所谓),内容如下:

include <linux/fs.h>

open$proc(file ptr[in, string["/proc/test"]], flags flags[proc_open_flags], mode flags[proc_open_mode]) fd
read$proc(fd fd, buf buffer[out], count len[buf])
write$proc(fd fd, buf buffer[in], count len[buf])
close$proc(fd fd)

proc_open_flags = O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, FASYNC, O_CLOEXEC, O_CREAT, O_DIRECT, O_DIRECTORY, O_EXCL, O_LARGEFILE, O_NOATIME, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_PATH, O_SYNC, O_TRUNC, __O_TMPFILE
proc_open_mode = S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH

在 syzkaller 项目根目录下执行以下命令以创建对应的 .const 文件

bin/syz-extract -os linux -sourcedir "/usr/class/linux" -arch amd64 test.txt

执行以下命令重新构建 syzkaller

bin/syz-sysgen
make all

此时 syzkaller 构建完成。

可能你会有些疑问,为什么要配置这些规则,这些规则的配置基于什么,它的语法结构是什么样的。

以及,为什么要创建 .const 文件,创建后为什么要再跑一遍 syz-sysgen。

这些问题正是我写下这行文字时所产生的疑问。

不过暂时不急,因为后面都会慢慢讲到。

3. syzkaller 开跑

在最后真正的执行前,我们需要修改一下 syzkaller 的运行配置。修改 my.cfg ,添加上以下内容:

"enable_syscalls": [
"open$proc",
"read$proc",
"write$proc",
"close$proc"
],

之后执行 syzkaller 开跑:

bin/syz-manager -config my.cfg -vv 10

过一小段时间(很快)后便可以得到:

image-20211122092540576

此时 syzkaller 正在复现 crash,crash样例不太直观:

image-20211122093042147

不过问题不大,再等亿段时间,我们便可以在网页端查看到其效果 可能是我点背,等了半个小时没等到 reprocude 结束(复现要花n久的时间…):

image-20211122095516144

试用了一段时间,不得不说一下,当 syzkaller 检测到 crash 时,貌似它会停下所有 VM 跑来复现这个 crash。这样的话个人感觉可能会带来一点性能开销?(不是很懂)

四、参考链接


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK