【Fuzzing101】练习一

➜ pdf_examples git:(main) ✗ $FUZZ/fuzzing_xpdf/install/bin/pdfinfo -box -meta $FUZZ/fuzzing_xpdf/pdf_examples/helloworld.pdf
Tagged: no
Pages: 1
Encrypted: no
Page size: 200 x 200 pts
MediaBox: 0.00 0.00 200.00 200.00
CropBox: 0.00 0.00 200.00 200.00
BleedBox: 0.00 0.00 200.00 200.00
TrimBox: 0.00 0.00 200.00 200.00
ArtBox: 0.00 0.00 200.00 200.00
File size: 678 bytes
Optimized: no
PDF version: 1.7

cargo-fuzz tutorial

一.安装

github: https://github.com/rust-fuzz/cargo-fuzz

安装: cargo install cargo-fuzz

➜ fuzzing cargo install cargo-fuzz
Updating https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git index
Downloaded cargo-fuzz v0.10.0 (registry https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git)
Downloaded 1 crate (30.5 KB) in 2.33s
Installing cargo-fuzz v0.10.0
……
Finished release [optimized] target(s) in 47.02s
Replacing /root/.cargo/bin/cargo-fuzz
Replaced package cargo-fuzz v0.8.0 with cargo-fuzz v0.10.0 (executable cargo-fuzz)

➜ fuzzing cargo-fuzz
cargo-fuzz 0.10.0
A cargo subcommand for using libFuzzer! Easy to use! No need to recompile LLVM!

USAGE:
cargo-fuzz

FLAGS:
-h, –help Prints help information
-V, –version Prints version information

SUBCOMMANDS:
add Add a new fuzz target
build Build fuzz targets
cmin Minify a corpus
coverage Run program on the generated corpus and generate coverage information
fmt Print the std::fmt::Debug output for an input
help Prints this message or the help of the given subcommand(s)
init Initialize the fuzz directory
list List all the existing fuzz targets
run Run a fuzz target
tmin Minify a test case

注意: cargo-fuzz 是集成了libfuzzer,libFuzzer依赖LLVM的,所以只能是x86-64的linux或者macos才可以,而且还需要C++编译器和C++11的支持。

用法

  • 初始化: cargo fuzz init
  • 添加目标: cargo fuzz add <target>
  • 运行: cargo fuzz run <target>
  • 打印测试用户输出 :cargo fuzz fmt <target> <input>
  • 缩小到最小输出:cargo fuzz tmin <target> <input>
  • 缩小输入数据: cargo fuzz cmin <target>
  • 生成覆盖范围信息: cargo fuzz coverage <target>

官方文档: https://rust-fuzz.github.io/book/cargo-fuzz.html

二.实战

我们随机选了一个好fuzzing的库,Human Time, 一个parser 时间的这种库。

crate: https://crates.io/crates/humantime

github: https://github.com/tailhook/humantime

文档: https://docs.rs/humantime/2.1.0/humantime/

第一步: 把项目clone到本地

git clone https://github.com/tailhook/humantime

我们可以看到,这个库自己是没有fuzz文件夹的,说明并没有做fuzzing

➜ humantime git:(master) ls
benches bulk.yaml Cargo.toml LICENSE-APACHE LICENSE-MIT README.md src vagga.yaml

第二步 : 项目初始化

进行项目文件夹执行

cargo fuzz init

可以看到,多了一个fuzz的文件夹

fuzz的文件夹结构:



➜ fuzz git:(master) ✗ tree
.
├── Cargo.toml
└── fuzz_targets
└── fuzz_target_1.rs


1 directory, 2 files



fuzz_target_1.rs的代码:

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    // fuzzed code goes here
});

第三步,开始编写测试用例

我们查看文档,看看这个库的具体用法

https://docs.rs/humantime/2.1.0/humantime/fn.format_duration.html

#![no_main]
use libfuzzer_sys::fuzz_target;

use humantime::parse_duration;

fuzz_target!(|data: &[u8]| {
    
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = parse_duration(s);
    };    

});

我们把data变成字符串,看看parse_duration是怎么解析的。

第四步,运行

cargo fuzz list 可以列出所有的测试用例

➜  fuzz_targets git:(master) ✗ cargo fuzz list
fuzz_target_1

我们继续添加测试用例

#![no_main]
use libfuzzer_sys::fuzz_target;

use humantime::{parse_duration, parse_rfc3339, parse_rfc3339_weak};

fuzz_target!(|data: &[u8]| {
    // fuzzed code goes here
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = parse_duration(s);
        let _ = parse_rfc3339(s);
        let _ = parse_rfc3339_weak(s);
    };
});




第五步 分析结果

libfuzzer demo-01

在上CS110l中有一个程序用来说明C/C++内存不安全的问题,我学到这里想着就用libfuzzer来fuzzer一下吧,顺便学习一下libfuzzer。

libfuzzer我已经搭建好了的,你可以根据https://github.com/Dor1s/libfuzzer-workshop来搭建环境。

源程序:

#include <stdio.h>
#include <string.h>

int main()
{
    char s[100];
    int i;
    
    printf("\nEnter a string:");
    gets(s);

    for(i = 0; s[i] != '\0'; i++)
    {
        if ( s[i] >= 'a' && s[i] <= 'z')
        {
            s[i] = s[i] - 32;
        }
    }

    printf("\nString in Upper Case = %s", s);
    return 0;
}

很明显,这里是有缓冲区溢出的漏洞,gets函数不安全。最后发现,gets在C++11中被移除了,这个程序编译不过,放弃。