AFL源码剖析条记(一) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

AFL源码剖析条记(一)

申博_安全防护 申博 326次浏览 已收录 0个评论

通过三道CTF学习反馈移位寄存器

通过三道CTF学习反馈移位寄存器 前言 流密码的关键在于设计好的伪随机数生成器。一般来说,伪随机数生成器的基本构造模块为反馈移位寄存器。反馈函数或者反馈逻辑F如果是线性函数,那么称其为线性反馈移位寄存器(LFSR)否则为非线性反馈

0x00 媒介

总结师傅们条记,主要源码剖析。

0x01 代码掩盖率

代码掩盖率是fuzz中基本观点,先相识清这个观点背面的插装编译等观点才好明白。

代码掩盖率是一种器量代码的掩盖水平的体式格局,也就是指源代码中的某行代码是不是已执行;对二进制顺序,还可将此观点明白为汇编代码中的某条指令是不是已执行。对fuzz来讲,固然愿望每句代码都能被检测到,掩盖率越高越好。

计量体式格局主要为三种:函数基本块界限

插桩

插桩是为了掩盖率而执行的要领。

afl-gcc.c

afl-gcc是gcc的一个封装(wrapper)

主要三个功用

find_as(argv[0]);                     //找到gcc/clang/llvm编译器
  edit_params(argc, argv);               //处置惩罚参数
  execvp(cc_params[0], (char**)cc_params);//执行

打印出cc_params,看看真正的参数是什么

gcc -o test test.c -B /usr/local/lib/afl -g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1

看看参数的意义。用了编译优化,指定了编译的标记,终究要的是-B指定了编译器(Assembler)

-funroll-loops      执行轮回强度消弭并消弭在轮回内部运用的变量。这是用简朴而疾速的操纵(如加法和减法)替换耗时操纵(如乘法和除法)的历程
-B <目次>            将 <目次> 添加到编译器的搜刮途径中
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION     /* a flag also shared with libfuzzer) or */
#ifdef __AFL_COMPILER                             /* (this one is just for AFL). */

这一步恰是汇编文件经由过程as进一步编译成二进制文件,这里替换了Assembler,固然为了插桩

AFL源码剖析条记(一)

afl-as.c和afl-as.h

反汇编适才天生的test,会发明插了一些插进去了分外的汇编指令

AFL源码剖析条记(一)

这两个文件被零丁提出来可以来诠释这里是怎样操纵的

The sole purpose of this wrapper is to preprocess assembly files generated by GCC / clang and inject the instrumentation bits included from afl-as.h. It is automatically invoked by the toolchain when compiling programs using afl-gcc / afl-clang.

重如果处置惩罚分歧平台设置标记,处置惩罚参数等等.主要函数add_instrumentation

fprintf将插桩用的汇编用fprintf插如适宜的处所

static  void  add_instrumentationvoid{
    ......
    while (fgets(line, MAX_LINE, inf)) {//读取每行
    ......
      fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32,
              R(MAP_SIZE));//插进去,注重R(MAP_SIZE)
    ......
        //下面有意义是怎样推断适宜的处所插进去,挑选分支,有兴致本身看看
    }
}

下面分别是32位和64位的,和调试看的一样

static const u8* trampoline_fmt_32 =

  "\n"
  "/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
  "\n"
  ".align 4\n"
  "\n"
  "leal -16(%%esp), %%esp\n"    //太高栈
  "movl %%edi,  0(%%esp)\n"     //生存寄存器
  "movl %%edx,  4(%%esp)\n"
  "movl %%ecx,  8(%%esp)\n"
  "movl %%eax, 12(%%esp)\n"
  "movl $0x%08x, %%ecx\n"       //生存随机数
  "call __afl_maybe_log\n"      //挪用__afl_maybe_log
  "movl 12(%%esp), %%eax\n"
  "movl  8(%%esp), %%ecx\n"
  "movl  4(%%esp), %%edx\n"
  "movl  0(%%esp), %%edi\n"
  "leal 16(%%esp), %%esp\n"
  "\n"
  "/* --- END --- */\n"
  "\n";

static const u8* trampoline_fmt_64 =

  "\n"
  "/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
  "\n"
  ".align 4\n"
  "\n"
  "leaq -(128+24)(%%rsp), %%rsp\n"
  "movq %%rdx,  0(%%rsp)\n"
  "movq %%rcx,  8(%%rsp)\n"
  "movq %%rax, 16(%%rsp)\n"
  "movq $0x%08x, %%rcx\n"
  "call __afl_maybe_log\n"
  "movq 16(%%rsp), %%rax\n"
  "movq  8(%%rsp), %%rcx\n"
  "movq  0(%%rsp), %%rdx\n"
  "leaq (128+24)(%%rsp), %%rsp\n"
  "\n"
  "/* --- END --- */\n"
  "\n";

以是能看到,插桩是为了统计掩盖率。至于细致怎样完成,继承看背面

fork service

这是一种为了不运用execve()函数进步效力想出来的设施,免却动态链接等历程,在lcamtuf的blog上也有细致的引见。

afl-fuzz.c

EXP_ST void init_forkserver(char** argv) {

  int st_pipe[2], ctl_pipe[2];//敕令管道和状况管道
    ......

 execv(target_path, argv);   //执行fork server
}

有两个重点

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

  • 怎样高效反复执行测试样例
  • 纪录样例的状况

最先fork service确认建立终了

/* Close the unneeded endpoints. */
//封闭不须要的通道
  close(ctl_pipe[0]);
  close(st_pipe[1]);

//读取通道状况敕令
  fsrv_ctl_fd = ctl_pipe[1];
  fsrv_st_fd  = st_pipe[0];

......

  rlen = read(fsrv_st_fd, &status, 4);//从状况通道读取4个字节


  /* If we have a four-byte "hello" message from the server, we're all set.
     Otherwise, try to figure out what went wrong. */

  if (rlen == 4) {//推断读取是不是胜利
    OKF("All right - fork server is up.");
    return;
  }

__afl_maybe_log()

这里由于AFL自带的延时检测,以是没法调试看,这里只要看源码

这里先检测是不是分配到大众内存,__afl_area_ptr内里就是地点,不然先挪用__afl_setup初始化

.text:0000000000000950                 lahf
.text:0000000000000951                 seto    al
.text:0000000000000954                 mov     rdx, cs:__afl_area_ptr
.text:000000000000095B                 test    rdx, rdx
.text:000000000000095E                 jz      short __afl_setup

__afl_forkserver

写4个字节到状况管道st_pipe[0],forkserver通知fuzzer本身预备好了,而这正好是rlen = read(fsrv_st_fd, &status, 4);中守候的信息

.text:0000000000000ABB __afl_forkserver:
.text:0000000000000ABB                 push    rdx
.text:0000000000000ABC                 push    rdx
.text:0000000000000ABD                 mov     rdx, 4          ; n
.text:0000000000000AC4                 lea     rsi, __afl_temp ; buf
.text:0000000000000ACB                 mov     rdi, 0C7h       ; fd
.text:0000000000000AD2                 call    _write
.text:0000000000000AD7                 cmp     rax, 4
.text:0000000000000ADB                 jnz     __afl_fork_resume

__afl_fork_wait_loop

fork server直到从状况管道read到4个字节注解fuzzer预备好了

text:0000000000000AE1                  mov     rdx, 4          ; nbytes
.text:0000000000000AE8                 lea     rsi, __afl_temp ; buf
.text:0000000000000AEF                 mov     rdi, 0C6h       ; status
.text:0000000000000AF6                 call    _read
.text:0000000000000AFB                 cmp     rax, 4
.text:0000000000000AFF                 jnz     __afl_die
.text:0000000000000B05                 call    _fork
.text:0000000000000B0A                 cmp     rax, 0
.text:0000000000000B0E                 jl      __afl_die
.text:0000000000000B14                 jz      short __afl_fork_resume

纪录子历程的pid,一旦子历程执行完了,经由过程状况管道发送到fuzzer继承执行

.text:0000000000000B16                 mov     cs:__afl_fork_pid, eax
.text:0000000000000B1C                 mov     rdx, 4          ; n
.text:0000000000000B23                 lea     rsi, __afl_fork_pid ; buf
.text:0000000000000B2A                 mov     rdi, 0C7h       ; fd
.text:0000000000000B31                 call    _write
.text:0000000000000B36                 mov     rdx, 0          ; options
.text:0000000000000B3D                 lea     rsi, __afl_temp ; stat_loc
.text:0000000000000B44                 mov     rdi, qword ptr cs:__afl_fork_pid ; pid
.text:0000000000000B4B                 call    _waitpid
.text:0000000000000B50                 cmp     rax, 0
.text:0000000000000B54                 jle     __afl_die
.text:0000000000000B5A                 mov     rdx, 4          ; n
.text:0000000000000B61                 lea     rsi, __afl_temp ; buf
.text:0000000000000B68                 mov     rdi, 0C7h       ; fd
.text:0000000000000B6F                 call    _write
.text:0000000000000B74                 jmp     __afl_fork_wait_loop

用伪代码更能看清楚逻辑

if ( write(0xC7, &_afl_temp, 4uLL) == 4 )
      {
        while ( 1 )
        {
          v25 = 0xC6;
          if ( read(0xC6, &_afl_temp, 4uLL) != 4 )
            break;
          LODWORD(v26) = fork();
          if ( v26 < 0 )
            break;
          if ( !v26 )
            goto __afl_fork_resume;
          _afl_fork_pid = v26;
          write(0xC7, &_afl_fork_pid, 4uLL);
          v25 = _afl_fork_pid;
          LODWORD(v27) = waitpid(_afl_fork_pid, &_afl_temp, 0);
          if ( v27 <= 0 )
            break;
          write(199, &_afl_temp, 4uLL);
        }
        _exit(v25);
      }

fuzzer这边来看,发出要求,接收状况,依据状况管道推断执行效果……

if ((res = write(fsrv_ctl_fd, &prev_timed_out, 4)) != 4); //启动fork server
if ((res = read(fsrv_st_fd, &child_pid, 4)) != 4)  
    .......
 /* Report outcome to caller. */
  if (WIFSIGNALED(status) && !stop_soon) {
    kill_signal = WTERMSIG(status);
    if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT;
    return FAULT_CRASH;

  }

分支纪录

怎样推断这条途径(代码)执行过,背面还要依据这些纪录对背面变异有资助。既要勤俭空间又要有效力,那单链表之类的一定不能用,AFL用的是二元tuple(跳转的源地点和目的地点)来纪录分支信息。

比方:

A->B->C->D->A-B

可以用[A,B] [B,C] [C,D] [D,A]四个二元组透露表现,只须要纪录跳转的源地点和目的地点。而且[A,B]执行了两次,其他执行了一次,这里用hash映照在一张map中。

接下来代码细致讲讲。

之前在__afl_maybe_log背面另有_afl_store这个函数

.text:0000000000000960 __afl_store:                            ; CODE XREF: __afl_maybe_log+4F↓j
.text:0000000000000960                                         ; __afl_maybe_log+309↓j
.text:0000000000000960                 xor     rcx, cs:__afl_prev_loc
.text:0000000000000967                 xor     cs:__afl_prev_loc, rcx
.text:000000000000096E                 shr     cs:__afl_prev_loc, 1
.text:0000000000000975                 inc     byte ptr [rdx+rcx]

对应的伪代码。COMPILE_TIME_RANDOM就是add_instrumentationfprintfR(MAP_SIZE),也是在执行call __afl_maybe_log汇编前rcx中生存的随机数,这个随机数代表分支

cur_location = <COMPILE_TIME_RANDOM>;       //随机数以后分支
shared_mem[cur_location ^ prev_location]++;  //前一分支和以后分支锁透露表现的随机数异或透露表现二元tuple映照map
prev_location = cur_location >> 1;          //将以后分支再纪录

为何以后分支末了须要向右移一名?好比A->A或许A->B->A这类不右移异或为0

而且同享内存的MAP_SIZE=64K碰撞几率减少许多。下面是官方给的

Branch cnt | Colliding tuples | Example targets
------------+------------------+-----------------
      1,000 | 0.75%            | giflib, lzo
      2,000 | 1.5%             | zlib, tar, xz
      5,000 | 3.5%             | libpng, libwebp
     10,000 | 7%               | libxml
     20,000 | 14%              | sqlite
     50,000 | 30%              | -

分支信息处置惩罚

同享内存另有个变量trace_bits来纪录分支执行次数

classify_counts((u32*)trace_bits);

fuzzer主要将每一个分支处置惩罚次数归入下面这个表中

static const u8 count_class_lookup8[256] = {

  [0]           = 0, 
  [1]           = 1, 
  [2]           = 2, 
  [3]           = 4, 
  [4 ... 7]     = 8, 
  [8 ... 15]    = 16,
  [16 ... 31]   = 32,
  [32 ... 127]  = 64,
  [128 ... 255] = 128
};

好比执行了4-7次的其计数为8,末了用一个hash还推断新测试用例分支数增添没有

u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);

某商城cms v1.7 前台两处SQL注入

前言 某商城cms1.7版本中中存在两个前台注入漏洞。话不多说,直接进入分析。 SQL注入① 分析 我们直接定位到漏洞存在点 module/index/cart.php :12-32 行处。 case ‘pintuan’:
$product_id = intval($_g_id);
$product_g


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明AFL源码剖析条记(一)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址