首页
社区
课程
招聘
[原创]成功通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行
发表于: 2017-8-24 19:42 14684

[原创]成功通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行

2017-8-24 19:42
14684

      终于成功实现了ubuntu12.04 32位系统上,通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行。前一段时间还发帖求助,说GOT无法覆写,今天终于找到了原因,其实是因为notes是程序运行时第一次malloc分配的,很大几率在内存边界,然后unlink时,note[0]=notes-12时,在写入时就是非法访问,导致出错。解决办法就是在notes前面,malloc一个临时空间tmp,然后notes-12的地址,就会指向tmp,也就不会是非法访问。

另外notes数组中存放small bin chunk的地址,也就是NOTE SIZE的大小要大于0x64,避免分配fastbins,因为fastbin中空闲chunk不会合并,也就无法进行地址任意写。

下面就详细讲讲漏洞利用过程。

漏洞代码unlink.c如下:

上述代码的作用是:

该代码主要功能是建立一个note,并保存用户的输入到该note,每一个note的大小为0x80,总共可以新建10个note,每个note可以通过notes这个全局变量来索引。比如新建一个note,索引为0,note0的内容为”1234“;然后把note0的内容重新改为”5678”;接着释放掉note0,;最后退出程序。其操作如下:

unlink漏洞基础知识准备:

alloc chunk结构:


prev_size:若上一个chunk可用,则此结构成员赋值为上一个chunk的大小;否则若上一个chunk被分配,此结构成员为上一个chunk的用户数据。Size:此结构成员赋值为已分配chunk大小(包括chunk size和prev_size所占用的空间),其最后三位包含标志(flag)信息.

2)IS_MMAPPED(M)–置“1”表示这一个「chunk」是直接mmap申请的;

free chunk结构:


prev_size:两个空闲「chunk」不能毗连,而应合并成一个。因此前一个「chunk」和后一个空闲「chunk」都会被分配,此时 prev_size 中保存上一个「chunk」的用户数据。 size:该结构成员保存本空闲「chunk」的大小(包括pre_size、size、fd和bk所占用的空间)。 

fd:Forward pointer ——指向同一「bin」中的下一个「chunk」(而非物理内存中下一块)。

bk:Backward pointer ——指向同一「bin」中的上一个「chunk」(而非物理内存中上一块)。

注意:

1)malloc_chunk中的其余结构成员,如fd、bk不用于已分配的chunk,在已分配的chunk中fd和bk用来存储用户数据;

3)当前chunk块是否空闲,由后一块(物理相邻,而非fd指向的chunk)的P标志位决定。

漏洞利用过程分为6步:

1)第一步:新建note0和note1;

               #step 1 new malloc note0 and note1

print "\nmalloc note0 and note1"

change_note_content(0,0,4,"aaaa")

change_note_content(0,1,4,"aaaa")

新建的两个note,内容均为“aaaa”,并把这两个note的地址,存入到notes数组中。

2)第二步:chunk块伪造,并利用note0块堆溢出,覆盖chunk块note1的P标志位和prev_size;

在note0中伪造fake note0(fake fd = notes_addr-12,fake bk=notes_addr-8),同时通过堆溢出改写note1的P标志位和pre_size,让系统以为note1前面是空闲的chunk;

               #step2 fake fd,bk note0 and note1's presize,size

print "\n fake data padding note0 and note1"

notes_addr = int(raw_input("notes address:"), 16)

#content = fake pre_size +fake size + fake_fd + fake_bk + "c"*0x70 + fake_presize + modify_pre_inuse_flag

#next pre_size= content+pre_size + size+fd+bk,0x81 to make note0‘s privious chunk is using,0x88 make note0 seems is free

    note0_content = p32(0x0)+p32(0x81) + p32(notes_addr - 12) + p32(notes_addr - 8) + "c" * 0x70 + p32(0x80) + p32(0x88)

    change_note_content(2,0, 0x88, note0_content)

free(note1)时,由于系统认为note0是空闲的,当free一个内存块时,会检查前一个内存块是否是空闲的,如果是,首先会调用unlink()函数将前一个空闲内存块从空闲链表中移除,然后将当前内存块和前一个内存块合并,最后将合并后的内存块加入空闲链表中。(当然也会检查后一个内存块是否空闲,其实原理是一样的。

unlink操作如下:会进行双链表检测

F = p -> fd;  //F = notes - 12

B = p -> bk;  //B = notes - 8

if (F -> bk == p && B -> fd == p){


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 4
支持
分享
最新回复 (15)
雪    币: 210
活跃值: (641)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
不懂,顶下,看有没有人回答
2017-8-25 11:35
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
3
Loopher 不懂,顶下,看有没有人回答
多谢支持
2017-8-25 14:45
0
雪    币: 144
活跃值: (31)
能力值: ( LV8,RANK:140 )
在线值:
发帖
回帖
粉丝
4
notes  因为是第一次申请的    有非常大几率在一个分页的边界,  unlink后指向边界-8位置后再写入就gg了。
2017-8-25 21:06
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
5
冰怜泯灭 notes 因为是第一次申请的 有非常大几率在一个分页的边界, unlink后指向边界-8位置后再写入就gg了。
你说的这种情况我考虑过哈,但是这种情况free函数GOT表的地址应该写不进去的,现在是free函数的GOT表写进去了,进行GOT覆写是出错了。。
2017-8-25 23:59
0
雪    币: 60
活跃值: (319)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
6
提供一下binary和exploit.py?
2017-8-27 00:51
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
7
返無歸一 提供一下binary和exploit.py?
刚刚上传了unlink.c  编译后的binary哈,最后的python代码就是exp.py哈
2017-8-27 18:28
0
雪    币: 60
活跃值: (319)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
8
唔,因为你的code有indent问题所以才想说要不要直接附exp.py档这样,我研究看看
2017-8-29 22:45
0
雪    币: 60
活跃值: (319)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
9

我跑起来的结果跟你不一样......你的free@got位址好奇怪,我以为got地址不开PIE大多都是804XXXX之类的说

另外我gdb attach的结果是在输入free@got address前就abort了,原因是unlinkable失败,所以我也不知道为什麽你可以把notes[0]内容改成got@free= =


喔对了,我的系统是ubuntu 16.04


一般来说,unsafe unlink的用法应该满足:

指向heap的ptr

ptr已知(放在global之类)

能对该heap进行写入(!)


我看了下你的程式,好像是:

global notes malloc 10个ptr大小的空间来存放接下来指向的note的ptr

新增一个note就是malloc固定大小的空间,并且把该空间的ptr存在notes指向的heap空间里面

这样变成说:


bss:            heap:

notes------> notes[0] -----+

                   notes[1].....  |

                  省略......        |

                  *notes[0] <---+ (你实际可以写的笔记内容)

而unsafe unlink的要求是在notes[0]的地方可以做写入,所以按理来说应该unlink会失败




图中的804b000是notes[10]的header address(notes应该是存804b008)

804c038则是*notes[0]的header地址,804c0c0则是*notes[1]的地址

你写的fake chunk应该在*notes[0]里面,所以fake chunk header地址在804c40,用工具检查一下可不可以unlink:



看起来是失败的,原因是FD->bk != BK->fd



最後看一下notes[0]有没有被修成free@got地址:


看起来是没有的,虽然错误讯息不太一样,不过我觉得你的exploit有问题。有错请见谅

2017-8-29 23:47
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
10
返無歸一 我跑起来的结果跟你不一样......你的free@got位址好奇怪,我以为got地址不开PIE大多都是804XXXX之类的说另外我gdb attach的结果 ...
你好,首先感谢你的解答哈.unlink的条件是满足的哈,FD->bk=BK->fd.  我构造的fake  fd=notes_addr-12,bk=notes_addr-8;FD->bk=(notes_addr-12)->bk=notes_addr,BK->fd=(notes_addr-8)->fd=notes_addr.P就是fake_free_note  ,也就是notes[0],*notes_addr=notes[0]。可以绕过unlink的检测的
2017-9-5 17:56
0
雪    币: 60
活跃值: (319)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
11
你上面输入的是notes的值还是notes的地址?如果你输入的是notes的值那就没错,但是那地址不是malloc出来的位址吗?你是在没有leak  的情况下怎么知道malloc分出来的地址是多少?还是你没开aslr?
2017-9-6 00:29
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
12
我输入的是notes的地址哈,没有leak哈,开了aslr的,只是偷懒了一下,程序运行时通过gdb来获得notes的地址的。
2017-9-7 14:03
0
雪    币: 60
活跃值: (319)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
13
我算给你看:

通过unlink检查的要求是:
FD->bk  =  P  &&  BK->fd  =  P,其中P要是unlink  chunk的地址,也就是notes[0]
你现在的FD是&notes  -  12,BK是&notes  -  8
FD->bk  =  P
FD  ==    P  ->  fd  ==  *(&notes  )  -  12  ==  &notes[0]  -  12
FD->bk  ==  &notes[0]  -  12  +  12  =  &notes[0]  !=  notes[0] 

BK  ->  fd  =  P
BK  ==  P->bk  ==  *(&notes)  -  8  =  &notes[0]  -  8
BK->fd  ==  &notes[0]  -  8  +  8  ==  &notes[0]  !=  notes[0]

得证,你输入notes的地址的话unlink检查不会过

我知道你是看这篇教学:http://manyface.github.io/2016/05/19/AndroidHeapUnlinkExploitPractice/?spm=a313e.7916648.0.0.36f39d7eDZCDZY

人家填的地址是:p/x  *(0xb004),0xb004如果我没会错意的话应该是&notes,这样印出来的会是notes的值,也就是一地址指向malloc出来的空间,这样unlink是可以的,但是如果你malloc出来的第1块马上拿去用,notes[0]应该会指到heap  segment更前面的地址去,通常在开启aslr的情况下应该是不合法的空间会出错  (如同前面一位所说)

我是不知道你填的是&notes还是notes,如果你填的是&notes,那这地址按理来说根本就不会unlink成功,如同我前面证明的,再说要是&notes其实写死在exploit里面就好了,全局变量地址应该是不会变。如果是notes的值,则unlink会成功,但是后续我觉得很容易失败,如果你的notes是第一个chunk的话

另外我也不确定android和ubuntu  16.04环境上会不会差异很大,说不定人家可以在手机上面跑,我搬来ubuntu上就不能用,要学习还是找个跟自己环境比较搭的教程吧

那我就不再回这帖了,我相信我的理解应该没有错,有错的话之前解的题目都解的出来就是有鬼了XD我也不能去用你那确认,这样讨论下去各说各话没意义

你可以去做做看今年tokyowestern的simple_notes,相当典型的一个unlink题目,做完应该就知道unlink怎么做了
2017-9-7 23:28
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
14






返無歸一



我算给你看:

通过unlink检查的要求是:
FD->bk = P && BK->fd = P,其中P要是unlink chunk的地址,也就是notes[0]
...

你好,感谢你的解答,不过经过我的测试,就是*ptr=P,才能绕过unlink哈。
2017-9-9 19:17
0
雪    币: 506
活跃值: (185)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
15
冰怜泯灭 notes 因为是第一次申请的 有非常大几率在一个分页的边界, unlink后指向边界-8位置后再写入就gg了。
先前不好意思哈,没理解你的意思,经过多天思考终于结局了,才发现原来你早就解释过了,多谢哈。我通过在notes前面malloc申请了一个临时内存,避免再向notes-12地址写入时出错,终于GOT覆写成功了。
2017-9-22 22:37
0
雪    币: 52
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
ok
最后于 2019-2-18 13:26 被btrace编辑 ,原因:
2019-1-18 21:52
0
游客
登录 | 注册 方可回帖
返回
//