Fuzzing start

不可视境界线最后变动于:2023年4月29日 晚上

资源的查

Fuzzer: syzkaller

  • KASAN: kernel address sanitizer - kernel doc
  • Fault Injection : 用于模拟故障情况并观察系统的响应和行为以发现和解决问题。它主要通过在内核代码中添加注释或编写额外的代码来实现。这些注释和代码描述了模拟故障的位置、类型和参数,被称为FIB块(Fault Injection Block)
  • Golang by e.g. | Lang Spec | https://pkg.go.dev/std
  • A method is a function with a receiver.

install

  • 下载linux kernel, 直接github找到对应tag下载zip. 如果是拉取git仓库那就太庞大了, 所有的历史记录都给拉下来, 切换版本是方便了还完全没必要.
  • 参照文档即可. 遇到一些坑记录一下.
  • apt install make gcc flex bison libncurses-dev libelf-dev libssl-dev
  • 设置kernel config:
1
2
3
cd $KERNEL
make defconfig
make kvm_guest.config
  • 手动设置.config文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
# Coverage collection.
CONFIG_KCOV=y

# Debug info for symbolization.
CONFIG_DEBUG_INFO_DWARF4=y

# Memory bug detector
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y

# Required for Debian Stretch and later
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
  • make olddefconfig
  • make -j $(nproc) 本机6个core. 但是链接的时候内存峰值给我用到了9G, 原本2G的kali撑出OOM来…..编译新版本的kernel没有遇到源码问题, 挺不错
  • 接下来构造一下文件系统. 执行一个使用debootstrap的script, 坑点在于这个命令会连外网装上一些packet, 需要梯子: 把create-image.sh中那一行前面加上proxychains即可.
    这个脚本还可以切换成其他的distribution. 这里下载的默认是bullseye. 还有其他功能详见文档.
1
apt install debootstrap
1
2
3
4
5
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
  • qemu安装后的启动脚本
    • 看到hostfwd选项, 这是将主机10021端口映射到虚拟机的22号ssh端口上. 所以在下面ssh命令中指定了-p 10021.
    • 注意到append中有一个net.ifnames=0, 这是禁用了Predictable Network Interface Names, 否则会根据设备来生成接口名称, 而非传统的ethX. 在create-image.sh中指定了ethX的形式, 所以在first.ctg中也要在-append参数中加上这么一行. 更多…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
KERNEL=/root/Desktop/syzkaller_start/linux-6.2
IMAGE=/root/Desktop/syzkaller_start/image

qemu-system-x86_64 \
-m 1G\
-smp 2 \
-kernel $KERNEL/arch/x86/boot/bzImage \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
-drive file=$IMAGE/bullseye.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
  • 但是先别急着启动, 因为我发现这没有init文件. 复制一个现成的. 然后又发现里面的sh不能接受poweroff -d这个参数. 改成简单的poweroff -f.
    这里用_mnt的原因是vmware当成共享文件夹专用地, 不让我在这里make文件夹…….暂时不改了.
    我还没搞清init中一些命令是否是必要的. 反正去掉也能用.
1
2
3
4
5
6
7
8
9
#!/bin/zsh
RELEASE=bullseye
DIR=chroot
SEEK=2047

mount -o loop $RELEASE.img /_mnt/$DIR
echo "wait for input"
read -k 1
umount /_mnt/$DIR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
#echo 1 > /proc/sys/kernel/kptr_restrict
#echo 1 > /proc/sys/kernel/dmesg_restrict

chown root:root /flag
chmod 600 /flag
setsid /bin/cttyhack setuidgid 1000 /bin/sh

umount /proc
umount /sys

poweroff -f
  • ssh连接必须能成功:
1
ssh -i ~/Desktop/syzkaller_start/image/bullseye.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost
  • kill: kill $(cat vm.pid)
  • 接下来是SETUP syzkaller. 由于是go开发的, 先安装golang.
1
apt install golang
  • 编译一下用我了20G内存. 正常谁给这么多啊???整完又改成4G了.
  • 编译好之后出现了syzkaller/bin文件夹, 添加到PATH下. 然后随便哪里新建文件夹workdir, 再新建个json用作设置.
    • 0.0.0.0是为了能在宿主机上访问到.
    • vm的参数参考在此. 其余的参数参考在此.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"target": "linux/amd64",
"http": "0.0.0.0:56741",
"workdir": "$WORKDIR",
"kernel_obj": "$KERNEL",
"image": "$IMAGE/bullseye.img",
"sshkey": "$IMAGE/bullseye.id_rsa",
"syzkaller": "$SYZPATH",
"procs": 8,
"type": "qemu",
"vm": {
"cmdline": "net.ifnames=0",
"count": 4,
"kernel": "$KERNEL/arch/x86/boot/bzImage",
"cpu": 2,
"mem": 1024
}
}
  • 写个简单小脚本来自动替换上面的内容. (又看了一会儿的文档)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/zsh

if [[ $# -eq 0 ]]; then
echo "用法: $0 JSON文件名"
echo "说明: 用于替换JSON文件中的变量为指定的值。"
exit 1
fi

# 从命令行参数读取 JSON 文件名和变量名
json_file=$1
variable_names=("WORKDIR" "KERNEL" "IMAGE" "SYZPATH")

# 读取变量值
for i in {1..${#variable_names}}; do
echo -n "请输入 ${variable_names[$i]} 的值:"
read variable_value
variable_values[i]=${variable_value//\//\\\/}
done

# 替换 JSON 文件中的变量
for i in {1..${#variable_names}}; do
sed "s/\$${variable_names[$i]}/${variable_values[$i]}/g" "$json_file"
done

echo "变量替换完成。"
  • 然后直接运行即可
1
syz-manager -config=first.cfg

配置成功!

感觉细节也是不少, 没别人说的那么容易.

Useage

Process structure for syzkaller

Coverage

  • use sanitizer coverage (tracing mode) and KCOV for coverage collection.

  • Coverage is based on tracing coverage points inserted into the object code by the compiler. A coverage point generally refers to a basic block of code or a CFG edge (this depends on the compiler and instrumentation mode used during build, e.g. for Linux and clang the default mode is CFG edges, while for gcc the default mode is basic blocks).

  • Note that coverage points are inserted by the compiler in the middle-end after a significant number of transformation and optimization passes.

  • Linux specific coverage information. 主要是利用kcov的输出(debugfs), 然后接到binutils转换成

    • readelf解出每个段的虚存偏移

    • nm解出所有代码段的符号地址和大小. 16位对齐向上取整.

    • objdump解出__sanitizer_cov_trace_pc返回的值, 用来和kcov返回的PC值进行对比. 通过反汇编可以得到trace call的起始地址, 也就是the start address of executable blocks.

    • addr2line映射 kcov导出的和objdump解析的PC值 源代码文件和行

    • The final goal is to have a hash table of frames where key is a PC and value is a frame array consisting of a following information:

      • PC - 64bit program counter value (same as key)
      • Func - function name to which the frame belongs
      • File - file where function/frame code is located
      • Line - Line in a file to which program counter maps
      • Inline - boolean inlining information

      Multiple frames can be linked to a single program counter value due to inlining.

    • Creating report

      Once the database of the frames and function address ranges is created the next step is to determine the program coverage. Each program is represented here as a series of program counter values. The resulting coverage is not line based but the basic block based. The end result is stored in the file struct containing the following information:

      • lines - lines covered in the file
      • totalPCs - total program counters identified for this file
      • coveredPCs - the program counters that were executed in the program run
      • totalInline - total number of program counters mapped to inlined frames
      • coveredInline - the program counters mapped to inlined frames that were executed in the program run

Syscall desc