afl_training

libxml2:

前言:

本文记录afl-training的第一篇,复现CVE-2015-7497

准备工作:

按照官方操作,本机下面需要设置下临时环境变量

git submodule init && git submodule update
cd libxml2
export CC=afl-clang-fast 
./autogen.sh 
export AFL_USE_ASAN=1 
make -j 4

我们使用了ASAN,即address sanitizer,一个快速的内存错误检测工具,可以在内存分配失败时立即退出并打印错误信息,但有的情况下内存分配失败并不会导致程序崩溃
为了方便后面fuzz后处理crash,我们可以开启ASAN并设置临时环境变量禁用export ASAN_OPTIONS=detect_leaks=0

不过开启ASAN之后编译的测试程序./testModule运行时报错

fk@fk-pop:/home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2# ./testModule 
[-] FATAL: forkserver is already up, but an instrumented dlopen() library loaded afterwards. You must AFL_PRELOAD such libraries to be able to fuzz them or LD_PRELOAD to run outside of afl-fuzz.
To ignore this set AFL_IGNORE_PROBLEMS=1 but this will lead to ambiguous coverage data.
In addition, you can set AFL_IGNORE_PROBLEMS_COVERAGE=1 to ignore the additional coverage instead  is going to be easy to
fuzz, is definitely exposed to user-provided input in many situations, and is at high risk of containing bugs (parsing a
complex data format in an unsafe language).

This functionality is exposed in the [parser](http://xmlsoft.org/html/libxml-parser.html) API, and whilst you could dig
through this documentation, the easiest approach is to look at an example.
[`parse1.c`](http://xmlsoft.org/examples/parse1.c) (also in the (use with caution!).

有人提过类似的issue https://coder.social/antonio-morales/fuzzing101/issues/21
追溯到官方FAQ有两种解决方案,一种是设置临时环境变量

export AFL_PRELOAD=foo.so

但是笔者设置之后仍然报错,且在本机检索后并未找到foo.so
(foo.so其实并不是什么东西,foo的本意就是无意义,得strace附加进程上看相关的动态链接库

第二种设置临时环境变量忽略后测试成功

export AFL_IGNORE_PROBLEMS=1 

编写harness:

以上我们并未生成fuzz所需的入口程序,根据官方文档所说,即是我们需要一个fuzzing harness,笔者也是第一次,所以跟着官方文档的hint走了
根据官方文档描述libxml2的api很多,但是最容易进行模糊测试的是核心XML解析逻辑,因为它暴露在用户输入的情况下,可能在处理不安全的复杂数据时出现bug
根据官方给的examplehttp://xmlsoft.org/examples/parse1.c可以自己大概改一个,从命令行读入文件名的harness

#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

int main(int argc, char **argv) {
    if (argc != 2)
        return(1);
    xmlDocPtr doc; /* the resulting document tree */

    doc = xmlReadFile(argv[1], NULL, 0);
    if (doc == NULL) {
        fprintf(stderr, "Failed to parse %s\n", argv[1]);
	return 0;
    }
    xmlFreeDoc(doc);

    /*
     * Cleanup function for the XML library.
     */
    xmlCleanupParser();
    return(0);
}

使用以下指令编译

afl-clang-fast ./fuzz.c -I ./include ./.libs/libxml2.a -lz -lm -g -o fuzzer 

#-I 指定include目录和libxml2的静态链接库
#-lm使用math库,-lz使用zlib库,-g开启debug模式

报错

[-] PROGRAM ABORT : Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!
         Location : check_asan_opts(), src/afl-fuzz-init.c:2651

查询相关issue:https://groups.google.com/g/afl-users/c/XaE06yzKOu8
再次设置环境变量

export ASAN_OPTIONS=abort_on_error=1:symbolize=0

可以正常fuzz

优化harness:

但是太慢了,我们根据afl++文档可以添加一点优化
注意不必要语句的删除,下面是失败示例

#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

int main(int argc, char **argv) {
    if (argc != 2)
        return(1);
    xmlDocPtr doc; /* the resulting document tree */
    #ifndef __AFL_HAVE_MANUAL_CONTROL
        __AFL_INIT();
    #endif
    while(__AFL_LOOP(1000)){
    doc = xmlReadFile(argv[1], NULL, 0);
    if (doc == NULL) {
        fprintf(stderr, "Failed to parse %s\n", argv[1]);
	return 0;
    }
    xmlFreeDoc(doc);
    }

    /*
     * Cleanup function for the XML library.
     */
    xmlCleanupParser();
    return(0);
}

成功示例,注意两者区别:

#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

int main(int argc, char **argv)
{
    if (argc != 2)
        return (1);
    xmlDocPtr doc; /* the resulting document tree */
    #ifndef __AFL_HAVE_MANUAL_CONTROL
        __AFL_INIT();
    #endif
    while (__AFL_LOOP(1000))
    {
        doc = xmlReadFile(argv[1], NULL, 0);
        if (doc != NULL)
        {
            xmlFreeDoc(doc);
        }
    }

    /*
     * Cleanup function for the XML library.
     */
    xmlCleanupParser();
    return (0);
}

最后效率提升最高30x

crash分析:

4个小时8个unique crash

使用afl-tmin精简input:

for i in id*; do afl-tmin -i $i -o ./tmp/$i -m none -- ./fuzzer @@; done;

../fuzzer 输入文件查看报错信息,发现有几个输入文件错误发生在parser.c的第10666行,即错误发生在宏MOVETO_ENDTAG();中,并不是我们想要复现的xmlDictComputeFastQKey函数

使用gdb调试跟进确认错误出现在宏定义处,并不是我们想要复现的错误

好在有一个输入样本能触发CVE中的错误

fk@fk-pop:~/文档/fuzzing/afl-training/challenges/libxml2/libxml2/opt/default/crashes/tmp$ hexdump -C id\:000002\,sig\:06\,src\:002889\,time\:8672911\,execs\:11122815\,op\:havoc\,rep\:16 
00000000  3c 4a 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |<J00000000000000|
00000010  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
00000350  30 30 3a 4a 30 30 30 30  30 30 30 30 30 30 30 30  |00:J000000000000|
00000360  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
000003b0  30 30 30 30 30 30 30 30  30 30                    |0000000000|
000003ba

xmlDictComputeFastQKey函数中的溢出

=================================================================
==3397754==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100000123c at pc 0x556c7cb6c531 bp 0x7ffe76acfde0 sp 0x7ffe76acfdd8
READ of size 1 at 0x62100000123c thread T0
    #0 0x556c7cb6c530 in xmlDictComputeFastQKey /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/dict.c:489:18
    #1 0x556c7cb6a16f in xmlDictQLookup /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/dict.c:1093:12
    #2 0x556c7cb79590 in xmlSAX2StartElementNs /home/fk/文档/fuzzing/afl-training/challenges/libxml2/libxml2/SAX2.c:2238:17

binary ninja查看调用xmlDictComputeFastQKey函数的偏移

gdb断点打过去:

gdb --args ../fuzzer id\:000002\,sig\:06\,src\:002889\,time\:8672911\,execs\:11122815\,op\:havoc\,rep\:16 

b *$rebase(0x03de16b)

c

可以得到相关寄存器值

 ► 0x55555593216b <xmlDictQLookup+235>    call   xmlDictComputeFastQKey                <xmlDictComputeFastQKey>
        rdi: 0x6190000000d7 ◂— 0x303030303030304a ('J0000000')
        rsi: 0x351
        rdx: 0x621000001528 ◂— 'J000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
        rcx: 0x67
        r8: 0x2e70efeb

根据dict.c中函数传参规则,plen对应$rsi,len对应$rcx
len-(plen+1+1)0x67-(0x351+0x1+0x1)=-0x2EC
下标负数,数组越界

static unsigned long
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
                       const xmlChar *name, int len, int seed)
{
    unsigned long value = (unsigned long) seed;

    if (plen == 0)
	value += 30 * (unsigned long) ':';
    else
	value += 30 * (*prefix);

    if (len > 10) {
        value += name[len - (plen + 1 + 1)];
        len = 10;
	if (plen > 10)
	    plen = 10;
    }
    switch (plen) {
        case 10: value += prefix[9];
        case 9: value += prefix[8];
        case 8: value += prefix[7];
        case 7: value += prefix[6];
        case 6: value += prefix[5];
        case 5: value += prefix[4];
        case 4: value += prefix[3];
        case 3: value += prefix[2];
        case 2: value += prefix[1];
        case 1: value += prefix[0];
        default: break;
    }
    len -= plen;
    if (len > 0) {
        value += (unsigned long) ':';
	len--;
    }
    switch (len) {
        case 10: value += name[9];
        case 9: value += name[8];
        case 8: value += name[7];
        case 7: value += name[6];
        case 6: value += name[5];
        case 5: value += name[4];
        case 4: value += name[3];
        case 3: value += name[2];
        case 2: value += name[1];
        case 1: value += name[0];
        default: break;
    }
    return(value);
}

heartbleed:

前言:

本文记录afl-training的第二篇,复现心脏滴血漏洞

准备工作:

设置环境变量:

export CC=afl-clang-fast CXX=afl-clang-fast++ AFL_USE_ASAN=1

然后:

./config -d
make

在原来的handshake.cc上作修改,在BIO_write前初始化sizedata

// Copyright 2016 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <assert.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>

#ifndef CERT_PATH
# define CERT_PATH
#endif

SSL_CTX *Init() {
  SSL_library_init();
  SSL_load_error_strings();
  ERR_load_BIO_strings();
  OpenSSL_add_all_algorithms();
  SSL_CTX *sctx;
  assert (sctx = SSL_CTX_new(TLSv1_method()));
  /* These two file were created with this command:
      openssl req -x509 -newkey rsa:512 -keyout server.key \
     -out server.pem -days 9999 -nodes -subj /CN=a/
  */
  assert(SSL_CTX_use_certificate_file(sctx, "server.pem",
                                      SSL_FILETYPE_PEM));
  assert(SSL_CTX_use_PrivateKey_file(sctx, "server.key",
                                     SSL_FILETYPE_PEM));
  return sctx;
}

int main() {
  static SSL_CTX *sctx = Init();
  SSL *server = SSL_new(sctx);
  BIO *sinbio = BIO_new(BIO_s_mem());
  BIO *soutbio = BIO_new(BIO_s_mem());
  SSL_set_bio(server, sinbio, soutbio);
  SSL_set_accept_state(server);

  /* TODO: To spoof one end of the handshake, we need to write data to sinbio
   * here */
  #ifdef __AFL_HAVE_MANUAL_CONTROL
    __AFL_INIT();
  #endif
  uint8_t data[100] = {0};
  size_t size = read(STDIN_FILENO, data, 100);
  if (size == -1) {
    printf("Failed to read from stdin\n");
    return(-1);
  }
  BIO_write(sinbio, data, size);

  SSL_do_handshake(server);
  SSL_free(server);
  return 0;
}

编译:

afl-clang-fast++ ./handshake.cc -I ./include ./libssl.a ./libcrypto.a -ldl -g -o handshake

Fuzzing:

ipt中生成随机种子:

cd ipt && dd if=/dev/urandom of=1.in bs=64 count=10

开始fuzz前注意将server.key server.pem放在和handshake同目录下

afl-fuzz -i ipt/ -o opt/ -m none ./handshake

crash分析:

handshake移至crashes目录下使用afl-tmin简化input

for i in id*;do afl-tmin -i $i -o ./tmp/$i  -- ./handshake;done;

之后使用简化后的input测试

fk@fk-pop:~/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp$ gdb handshake 
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 147 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from handshake...
------- tip of the day (disable with set show-tips off) -------
Disable Pwndbg context information display with set context-sections ''
pwndbg> ls
handshake									 id:000006,sig:06,src:000236,time:22339456,execs:17321069,op:havoc,rep:7
id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1		 id:000007,sig:06,src:000233,time:23634783,execs:18301688,op:havoc,rep:3
id:000001,sig:06,src:000024+000075,time:4119745,execs:3210038,op:splice,rep:17	 id:000008,sig:06,src:000294+000226,time:25859527,execs:19982226,op:splice,rep:2
id:000002,sig:06,src:000053+000041,time:5079793,execs:3956550,op:splice,rep:4	 id:000009,sig:06,src:000270,time:26541712,execs:20500629,op:havoc,rep:1
id:000003,sig:06,src:000024+000068,time:10115665,execs:7871015,op:splice,rep:12  server.key
id:000004,sig:06,src:000040+000006,time:16647584,execs:12944780,op:splice,rep:4  server.pem
id:000005,sig:06,src:000224,time:22158868,execs:17184882,op:havoc,rep:4
This command is deprecated in Pwndbg. Please use the GDB's built-in syntax for running shell commands instead: !ls <args>
pwndbg> run < id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1
Starting program: /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake < id:000000,sig:06,src:000031,time:54840,execs:42018,op:havoc,rep:1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==7701==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x629000009748 at pc 0x5555556f96d7 bp 0x7fffffffd4d0 sp 0x7fffffffcca0
READ of size 25648 at 0x629000009748 thread T0
[Attaching after Thread 0x7ffff7e99800 (LWP 7701) fork to child process 7704]
[New inferior 2 (process 7704)]
[Detaching after fork from parent process 7701]
[Inferior 1 (process 7701) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 7704 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    #0 0x5555556f96d6 in __asan_memcpy (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a56d6) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)
    #1 0x555555744ca1 in tls1_process_heartbeat /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/t1_lib.c:2586:3
    #2 0x5555557b9129 in ssl3_read_bytes /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_pkt.c:1092:4
    #3 0x5555557bdcd9 in ssl3_get_message /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_both.c:457:7
    #4 0x555555786696 in ssl3_get_client_hello /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_srvr.c:941:4
    #5 0x5555557823ec in ssl3_accept /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/s3_srvr.c:357:9
    #6 0x555555737a7f in main /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/./handshake.cc:53:3
    #7 0x7ffff7829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #8 0x7ffff7829e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #9 0x555555677564 in _start (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x123564) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)

0x629000009748 is located 0 bytes to the right of 17736-byte region [0x629000005200,0x629000009748)
allocated by thread T0 here:
    #0 0x5555556fa3ae in malloc (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a63ae) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641)
    #1 0x5555557f0ee9 in CRYPTO_malloc /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/crypto/mem.c:308:8

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/opt/default/crashes/tmp/handshake+0x1a56d6) (BuildId: 2eb1559a07e34eae87f86fae3812f29a9b98a641) in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c527fff9290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c527fff92a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c527fff92b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c527fff92c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c527fff92d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c527fff92e0: 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa
  0x0c527fff92f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c527fff9300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c527fff9310: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c527fff9320: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c527fff9330: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==7701==ABORTING
[Inferior 2 (process 7704) exited normally]

定位到t1_lib.c中的tls1_process_heartbeatmemcpy(bp, pl, payload);即是触发心脏滴血的关键代码

*RIP  0x555555744c9d (tls1_process_heartbeat+765) ◂— call 0x5555556f9520
────────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────────────────────────────────────────────────────────
   0x555555744c89 <tls1_process_heartbeat+745>    jne    tls1_process_heartbeat+1249                <tls1_process_heartbeat+1249>
 
   0x555555744c8f <tls1_process_heartbeat+751>    mov    byte ptr [rbx + 2], r12b
   0x555555744c93 <tls1_process_heartbeat+755>    lea    rdi, [rbx + 3]
   0x555555744c97 <tls1_process_heartbeat+759>    mov    rsi, r15
   0x555555744c9a <tls1_process_heartbeat+762>    mov    rdx, rbp
 ► 0x555555744c9d <tls1_process_heartbeat+765>    call   __asan_memcpy                <__asan_memcpy>
        rdi: 0x62b000000203 ◂— 0xbebebebebebebebe
        rsi: 0x62900000520b ◂— 0xbebebebebebebebe
        rdx: 0x6430
        rcx: 0x55555662f720 (__afl_area_initial) ◂— 0x1000000000002
 
   0x555555744ca2 <tls1_process_heartbeat+770>    lea    rdi, [rbx + rbp]
   0x555555744ca6 <tls1_process_heartbeat+774>    add    rdi, 3
   0x555555744caa <tls1_process_heartbeat+778>    mov    esi, 0x10
   0x555555744caf <tls1_process_heartbeat+783>    call   RAND_pseudo_bytes                <RAND_pseudo_bytes>
 
   0x555555744cb4 <tls1_process_heartbeat+788>    mov    rdi, qword ptr [rsp + 0x10]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
In file: /home/fk/文档/fuzzing/afl-training/challenges/heartbleed/openssl/ssl/t1_lib.c
   2581                 bp = buffer;
   2582                 
   2583                 /* Enter response type, length and copy payload */
   2584                 *bp++ = TLS1_HB_RESPONSE;
   2585                 s2n(payload, bp);
 ► 2586                 memcpy(bp, pl, payload);
   2587                 bp += payload;
   2588                 /* Random padding */
   2589                 RAND_pseudo_bytes(bp, padding);
   2590 
   2591                 r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd4e0 —▸ 0xc34000001b0 ◂— 0x0
01:0008│     0x7fffffffd4e8 —▸ 0x6180000000f0 ◂— 0x0
02:0010│     0x7fffffffd4f0 —▸ 0x618000000080 ◂— 0x200000000301
03:0018│     0x7fffffffd4f8 ◂— 0x644300005203
04:0020│     0x7fffffffd500 —▸ 0x618000000118 ◂— 0x0
05:0028│     0x7fffffffd508 —▸ 0x7fffffffd640 ◂— 0x3fff800
06:0030│     0x7fffffffd510 —▸ 0x7fffffffd540 ◂— 0x41b58ab3
07:0038│     0x7fffffffd518 —▸ 0xc34000001b6 ◂— 0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 ► 0   0x555555744c9d tls1_process_heartbeat+765
   1   0x5555557b912a ssl3_read_bytes+13306
   2   0x5555557bdcda ssl3_get_message+538
   3   0x555555786697 ssl3_get_client_hello+535
   4   0x5555557823ed ssl3_accept+3405
   5   0x555555737a80 main+448
   6   0x7ffff7829d90 __libc_start_call_main+128
   7   0x7ffff7829e40 __libc_start_main+128
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> ni
=================================================================
==8348==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x629000009748 at pc 0x5555556f96d7 bp 0x7fffffffd4d0 sp 0x7fffffffcca0
READ of size 25648 at 0x629000009748 thread T0
[Attaching after Thread 0x7ffff7e99800 (LWP 8348) fork to child process 8359]
[New inferior 2 (process 8359)]
[Detaching after fork from parent process 8348]
[Inferior 1 (process 8348) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 8359 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x5555557b9125

sendmail_1301:

前言:

本文为afl-training的第三篇,针对sendmail的fuzz,比较了一下普通harness普通fuzz与并行fuzz的效率提升,以及普通harness与优化过后的harness进行fuzz时的效率差别

准备工作:

这道题已经准备好了harness
根据makefile

CFLAGS ?= -g

m1-bad: mime1-bad.c main.c
	${CC} ${CFLAGS} -o m1-bad mime1-bad.c main.c -I .

clean:
	rm -f *-bad *-ok

我们设置环境变量export CC=afl-clang-fast后make即可
生成m1-bad,测试一下能正常运行

fk@fk-pop:~/文档/fuzzing/afl-training/challenges/sendmail/1301$ ./m1-bad 1.txt 
buf-obuf=64
obp-obuf=0
canary-obuf=4294967268
canary = GOOD
obuf = 1

canary should be GOOD
canary = GOOD

Fuzzing:

建好文件夹和生成随机种子之后开始fuzz

afl-fuzz -i ipt/ -o opt/ -- ./m1-bad @@

没有修改过的harness编译出的进行fuzz时表现如下:

在3min左右的时候跑出了crash;

然后我们使用并行fuzz:

afl-fuzz -i ipt1/ -o opt1/ -M f1 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f2 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f3 -- ./m1-bad @@
afl-fuzz -i ipt1/ -o opt1/ -S f4 -- ./m1-bad @@

这次我们不到1min主fuzzer就跑出了crash:

最后我们测试一下延迟初始化和持久化模式需要多久能跑出,修改一下harness:

#include "my-sendmail.h"
#include <assert.h>

int main(int argc, char **argv)
{

     HDR *header;
     register ENVELOPE *e;
     FILE *temp;

     #ifdef __AFL_HAVE_MANUAL_CONTROL
     __AFL_INIT();
     #endif 
     while(__AFL_LOOP(1000))
     {
          assert(argc == 2);
          temp = fopen(argv[1], "r");
          assert(temp != NULL);

          header = (HDR *)malloc(sizeof(struct header));

          header->h_field = "Content-Transfer-Encoding";
          header->h_value = "quoted-printable";
          header->h_link = NULL;
          header->h_flags = 0;

          e = (ENVELOPE *)malloc(sizeof(struct envelope));
          e->e_id = "First Entry";
          e->e_dfp = temp;


          mime7to8(header, e);

          fclose(temp);
          free(e);
          free(header);
          return 0;
     }
}

重新make后进行fuzz:

在2min30s左右跑出crash,相较于最原始的harness还是有效率提升的

crash分析:

使用afl-tmin精简样本:

for i in id*;do afl-tmin -i $i -o ./tmp/$i  -- ./m1-bad @@;done;

得到简化后样本

fk@fk-pop:~/文档/fuzzing/afl-training/challenges/sendmail/1301/opt1/f1/crashes/tmp$ hexdump -C id\:000000\,sig\:11\,src\:000038\,time\:3687\,execs\:21373\,op\:havoc\,rep\:1 
00000000  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
00000230  30 30 30 30 30 30 30 30  30 30 30 3d 0a 30 30 30  |00000000000=.000|
00000240  30 30 30 30 30 30 30 30  30 30 30 30 30 30 30 30  |0000000000000000|
*
00000260  30 30 30 3d 0a 30 30 30  30 30 30                 |000=.000000|
0000026b

后用AFL_USE_ASAN=1的编译选项重新编译了一份之后跑样本确定crash发生处确实与给出的mime1-bad.c中注释BAD的地方吻合

==2927476==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffdc92 at pc 0x555555631800 bp 0x7fffffffdbb0 sp 0x7fffffffdba8
WRITE of size 1 at 0x7fffffffdc92 thread T0
[Attaching after Thread 0x7ffff7e9cc40 (LWP 2927476) fork to child process 2927479]
[New inferior 2 (process 2927479)]
[Detaching after fork from parent process 2927476]
[Inferior 1 (process 2927476) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 2927479 is executing new program: /usr/lib/llvm-14/bin/llvm-symbolizer
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    #0 0x5555556317ff in mime_fromqp /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/mime1-bad.c:248:18
    #1 0x5555556317ff in mime7to8 /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/mime1-bad.c:156:7
    #2 0x5555556323c6 in main /home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/main.c:94:11
    #3 0x7ffff7c29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #4 0x7ffff7c29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #5 0x5555555733f4 in _start (/home/fk/文档/fuzzing/afl-training/challenges/sendmail/1301/opt/default/crashes/m1-bad+0x1f3f4) (BuildId: 774480b07edb24c4f93faf62426d5928c809433f)

小结:

以上为afl-training几个具有代表性的练习,帮助熟悉afl++的fuzz流程,现整合为1篇