首页
社区
课程
招聘
[原创]house of pig详解
发表于: 2021-6-26 17:14 20741

[原创]house of pig详解

2021-6-26 17:14
20741

在复现这题之前需要了解一些前置知识:libc2.31下的largebin_attacktcache_stashing_unlink plus以及高版本glibc下的IO_FILE攻击

首先看到libc2.31下的largebin_attack

跟随how2heap项目中的largebin_attack以及源码调试来学习。

从libc2.30开始,largebin的插入代码中新增了两个检查

先看到第一个点

2c5gYj.jpg

将unsortedbin插入到largebin中时,且这个unsortedbin大于largebin的size,此时插入过程增加了双向链表完整性检查。

通常就是修改largebin的bk_nextsize=target_addr-0x20,然后在插入一个比原有largebin更大的unsortedbin时(后面称原有的largebin为largebin1,新插入的为largebin2),在插入过程中,largebin1的bk_nextsize被设置为largebin1的bk_nextsize,即target_addr-0x20,后续victim->bk_nextsize->fd_nextsize = victim这条语句,会将target_addr-0x20+0x20位置写入largebin2的地址,这是第一个点。

第二个点如下

2chIg0.png

这里的利用方式是修改largebin1的bk=target_addr-0x10,bck = fwd->bk;bck->fd = victim;这两句代码执行完毕后会将target_addr-0x10+0x10的位置写入largebin1的地址。

如图所示,这两处都添加了检查。

但当要插入的unsortedbin小于largebin的size时并没有做检查,如下图

2cIbb8.jpg

在这里并没有进行检查,因此在libc2.31下这里就成了新的利用点。

demo文件如下

我删掉了原文件中的一些描述性代码,以便于观看代码。

整体攻击思路就是申请一大一小两个chunk(后面称为chunk1,chunk2),先free掉chunk1,然后申请一个更大的chunk来将chunk1从unsortedbin中插入到largebin,接着将chunk1的bk_nextsize设置为target_addr-0x20,这是第一步;第二步,free掉chunk2,然后申请一个更大的chunk来将chunk2从unsortedbin中插入到largebin中,由于此时插入的chunk2的size要小于chunk1,所以会触发新的攻击流程,这里我们采用源码调试,以便更直观地学习。

在程序执行到size_t *g4 = malloc(0x438);这一句时,堆的情况如下

2Ro900.png

largebin里放着0x430的chunk,unsortedbin里面则是0x420的

2Ro1hD.png

2RoB4S.png

chunk1的bk_nextsize被设置为了target_addr-0x20

接下来我们将断点下在_int_malloc函数

2Roo34.png

2RoLHx.png

然后我们运行到将unsortedbin插入到largebin的代码

2WoKdP.png

首先获取要插入的unsortedbin对应的largebin的index,然后获取到对应的链表头

2WoUZq.png

由于此时largebin中已经有了一个chunk,所以对应链表头的fd和bk都被设置为了这个largebin的地址,类似于下面这样

2WLTl8.png

然后进入到插入环节

2WoHOA.png

将bck,也就是链表头赋值给fwd,将bck->bk(chunk1的地址)赋值给bck,进入到插入操作,首先将chunk2(即将插入的chuhnk)的fd_nextsize设置为chunk1的地址

RGPD3Q.png

再将chunk2的bk_nextsize设置为chunk1的bk_nextsize,而chunk1的bk_nextsize已经被修改为了target_addr-0x20,因此chunk2的bk_nextsize也会指向target_addr-0x20

2WHyQg.png

最后一行代码用于修改chunk1的fd_nextsize和bk_nextsize为chunk2的地址,由于设置chunk1的fd_nextsize是通过

victim->bk_nextsize->fd_nextsize来设置的,而victim->bk_nextsize指向的是一个错误的地址,执行完这条赋值语句后就会在target_addr+0x20的位置上写入chunk2的地址

2WX5M8.png

至此就实现了类似于libc2.23下的unsortedbin attack,往任意地址写入一个堆地址。

此种利用方式可以达成任意地址处分配一个chunk

demo如下

整体思路如下

1.tcache中放5个,smallbin中放两个

2.将后进smallbinchunkbk(不破坏fd指针的情况下)修改为目标地址-0x10,同时将目标地址+0x8处的值设置为一个指向可写内存的指针。

3.从smallbin中取一个chunk,走完stash流程,目标地址就会被链入tcache中。

依然是源码调试

将断点下在calloc(1, 0x50);这一行,以及_int_malloc

先看看此时的内存情况

2fpHdU.png

victim就是我们的目标,&victim+0x8的位置已经被设置为了一个指向可写内存的指针

2f9MTS.png

后进入的smallbin的bk指针指向了&victim-0x10

2f9tO0.png

0x60的tcache里面有五个,0x60的smallbin里面有两个

接下来我们跟进到int_malloc中

由于calloc不从tcache中取chunk,所以会直接从smallbin中取出一个chunk

2fCLvR.png

2fPXWQ.png

链表头和两个smallbin的结构如下

2fkFxJ.png

2fkcd0.png

victim = last (bin)取最后一个chunk(从上面的结构图里面能看得很清楚)

last是一个宏定义,如下

接着往前遍历,取倒数第二个chunk,还会进行双向链表的完整性检查

2fE9c4.png

2fEtC8.png

这两条代码将最后一个chunk解链,执行后结构如下

2fZVSO.png

然后运行到这里

2fmGLQ.png

size_t tc_idx = csize2tidx (nb);会先计算出此时申请的chunk的size对应于tcache的哪一条链

2fno90.png

如果这条链的tcache中还有空余且smallbin也有chunk

2fun8P.png

2fuN80.png

满足条件,然后取出smallbin中的最后一个chunk

2fuhrD.png

依次运行上述代码

2fKXf1.png

bck如果我们分析一样,是&victim-0x10

2fMT3t.png

链表头的bk指针指向了&victim-0x10

2fQiuT.png

bck->fd=bin,即&victim-0x10+0x10=&victim被设置为了bin头,解链后结构如下

2f1K0O.png

之后再将tc_victim放入tcache

放入前

2f1hEF.png

放入后

2f1oC9.png

看到这里相信大家就大概能明白为什么这个攻击手法可以任意地址分配一个chunk了,此时tcache中还剩一个空位,程序会继续从smallbin中取chunk放入tcache,此时smallbin中只剩下victim,我们继续调试

2f8Yef.png

新一轮的tc_victim为&victim-0x10

接下来会找到倒数第二个chunk

2f8bTO.png

因此我们需要将&victim-0x10+0x18处设置为一个可写地址x,方便后面往其中写bin头地址,而demo一开始就将其设置好了。

2fGoCQ.png

后续的就是继续解链,然后将victim加入到tcache

2fJiK1.png

至此攻击完成

在2.23下一般攻击FILE结构体就是劫持IO函数的_chain字段为我们伪造的IO_FILE_plus,然后修改vtable表中的io_str_overflow为system。在高版本libc下,如libc2.31下也依然是利用io_str_overflow这个函数,但io_str_overflow函数的实现发生了变化

可以看到io_str_overflow调用了malloc,memcpy,free等函数

我们回溯一下malloc的参数new_size的来源

如果我们能控制(fp)->_IO_buf_end - (fp)->_IO_buf_base就能够控制malloc申请的chunk大小,再看到后面这一段

如果old_buf指向的内存空间有数据,则将使用memcpy将old_buf中的数据拷贝到new_buf中,拷贝的长度为old_blen,然后再free (old_buf);

再看到_IO_str_overflow的汇编

2fBjyD.png

在+53这一行,会将[rdi+0x28]处的值送到rdx中,而自从libc2.29开始,setcontext中的gadget索引由rdi变为了rdx,需要先控制rdx的值才能够进行后续的srop

_IO_str_overflow中的这条汇编正好可以将rdx进行赋值,且来源还是rdi,rdi正好是FILE结构体的首地址,只需要将fp+0x28设置为我们可以控制的地址就可以进行srop,且这条语句是在malloc之前执行的,所以利用方法就是:首先将malloc_hook设置为setcontext+61,然后触发_IO_str_overflow,事先在我们伪造的FILE结构体中设置好相应的数据,从而将rdx赋值为我们可以控制的地址,接着_IO_str_overflow调用malloc触发setcontext,进行srop。

触发malloc的条件如下

libc版本为2.31,用c++写的

IDA反编译,查看main函数

2fcGdg.png

看到一条 __asm { jmp rax },这是因为IDA没能正确识别switch跳转,修复一下,看到汇编

2fWMtg.png

jmp rax的地址在0x3794,跳转表在0x69e0

2fWtBV.png

跳转表是一字节一字节的,连续按d修改成四字节一组,一共有6个跳转

2fW6nx.png

接下来开始修复跳转表,首先选中jmp rax那条汇编,再按如下路径选取specify switch idiom

2f7GwR.png

配置如下

2fLdUA.png

由上至下分别为

跳转表的地址;跳转的数量;每一个跳转值的长度;基值;跳转开始的位置;跳转使用的寄存器;/;默认跳转位置。

修复成功后如下所示

2fXMSx.png

舒服了许多。

开始分析程序

随便点开程序开头的一个函数

RnZHmT.png

一串赋值0的操作,不明白什么意思,继续往下

找到菜单

RnZjh9.png

一共有五个功能,增查改删以及切换人物

点开case 1对应的函数,也就是add函数

RneP0O.png

里面嵌套着一个switch,点开case1对应的函数看看

Rne8hj.png

相当难看,很多对地址进行的操作,这种情况尽可能恢复程序的结构体方便后续分析,先大致分析一下这个函数的流程。

最多能申请20次,申请的chunk的大小要大于等于0x90小于等于0x430.并且每一次申请的chunk的size都要大于等于上一次所申请的。申请堆块用的是calloc函数,calloc不从tcache中取。

如果size合法的话,就将size赋值给*(_DWORD *)(unk_9070 + 0x150LL),这个unk_9070 中存放的是mmap出来的一块内存的地址。

这一段寻址看的不清晰,我们修改一下这个函数传入的参数的类型

RKKhLR.png

原本是int64类型,我们将其修改为char * ,在参数上按y修改类型

RKKoo6.png

修改完后上面那一段就变成了如下所示

RKKOQH.png

这样子看起来就比上面那段看起来清楚一些,将a1[4 * i + 0xC0]赋值为size,将a1[i + 0x120]以及a1[i + 0x138]都赋值为0.

继续往下看

RKMklQ.png

size/0x30,也就是将申请的chunk以0x30大小切片,每次都往切片的顶部读入0x10个字节的数据。

这个函数到这就算分析得差不多了,但如果我们要恢复程序使用的结构体,这些信息还不够,继续分析其他函数。

再看到add功能的case 2

case 2的函数与case1对应的函数只有几处不一样

RKQwEq.png

size存放到mmap中的位置变了

RKQrCT.png

将chunk切片之后不再是从顶部写入,而是从偏移0x10处开始写

case3对应的函数不同之处也是在这两处,就不再说了。

由add功能可以猜测,add功能传入的参数应该是一个结构体,

再看到view功能

RKzN40.png

首先判断*(int *)(unk_9070 + 0x404LL)的值是否大于0,然后依然有三个函数,进入第一个函数

RMS0it.png

输入一个序号,然后会有以下三个判断

满足这三个条件就会输出对应chunk的值

另外两个函数也基本一致

看到edit功能

RM9FjU.png

和view功能一样,开头会检查一个全局变量的值*(int *)(unk_9070 + 0x408LL)是否大于0,大于0才会进入到edit的子函数中,查看第一个函数

RM9rDg.png

依然有三个判断,和view的判断是相同的,通过判断的话就会重新往chunk中写入数据,也是先切片再写数据,和add功能中的逻辑一样

看到delete功能

RM9Ha9.png

delete功能没有次数的限制,进入第一个子函数

RM9jxK.png

delete功能依然有三个检查

检查chunk是否存在,检查!a1[v4 + 0x120]!a1[v4 + 0x120]这两个位置是否为0,通过检查的话则进行free,并将两个标志位置1,但并没有将堆指针清0。

前四个功能分析完毕,第五个功能稍后再谈,我们先通过这四个功能恢复出程序使用的结构体

总结如下

初步猜测结构体应该如下所示

但这样子其实是有错误的,回到add功能

RMP9yT.png

堆地址是从结构体的头部开始存储的,而size数据则是从结构体偏移0xc0开始存储的,如果只存储20个堆地址的话,那么只占用0xa0的大小,size应该从0xa0开始存储;要使size从0xc0开始存储,则堆地址存储的上限应为0xc0/0x8=0x18个,同样的,size,flag,flagg的存储上限也应为0x18(大家可以自行计算一下,上限为0x18的话各种偏移就都正好满足),这样的话结构体应该如下

规划好结构体之后就可以往ida中添加了

RMPfnU.png

点到local types界面

RMP474.png

右键选择insert

RMPThR.png

直接输入结构体代码

RMPb1x.png

local types中会出现我们自定义的结构体类型

我们再回到add功能,修改参数类型

RMip4A.png

将其修改为house *类型

RMiigP.png

修改成功

我们再接着把程序中用到了这个结构体的函数的参数类型统统修改

比如我们在一开始点进去的那个一大堆赋值0的函数,修改完后就变成了下面这样

RMi3uT.png

清楚了亿点点

其他需要修改为house结构体的位置我就不一一修改给大家看了,可以自行操作

但,程序中不止存在这一个结构体,在add功能中

RMisbD.png

很明显,0x9070的值指向的空间也是一个结构体,我们接下来对这个结构体进行恢复

在程序一开始,有这么一个函数

RMib5j.png

将0x9070指向位置的数据拷贝到house结构体中,并且每一个memcpy函数拷贝的大小都是house结构体不同成员的大小

现在我们继续往下看到功能5 Change roles

RMOojJ.png

让我们输入密码,调用strlen得出密码的长度,调用sub_13C9这个函数

这一串十六进制数是md5加密的特征,后面的sub_2916sub_2A8B也就不用看了,实际上就是将我们输入的密码进行md5加密,然后将md5加密后的数据和设置的好的md5值进行对比,进行比较的条件有三个

RQ2vxf.png

满足任意一个即可进入到if代码块中,这里需要注意的是,前两个比较使用的是memcmp,而第三个比较使用的是strcmp,strcmp存在0字符截断问题,我们看到dword_6928

RlSEM8.png

这串md5的开头存在\x00,所以在和这串md5值比较的时候实际上会提前截断,因此我们要找的只是以0x3c4400开头的md5对应的原值

我们再看到if代码块,if中的代码会判断我们输入的密码的第一个字符为A,B,C中的哪一个,然后返回1或2或3来切换角色。

根据以上的分析,如果我们想要切换角色,输入的密码需要以A,或B或C开头,且经过md5加密后需要以0x3c4400开头,这样的一串md5的原值还是有一些的,写了个烂脚本,就嗯爆破,在9位数字内各找到了满足条件的密码

如果师傅们有更好的办法的话希望能指点一下(太菜了)

知道了如何更改角色以后再继续往下看

R1z9jf.png

会根据返回的角色值来进行switch选择要执行的函数,进入case1查看一下

R3KZcT.png

将house结构体的数据拷贝到qword_9070

看到case2

R3K2Dg.png

一样的功能,只不过拷贝的目标地址发生了变化,case3也是一样的

到这里为止,我们先考虑恢复qword_9070指向的结构体,很明显,根据这三个case,我们可以推断出这个结构体(后面称为tmp_house)至少有3个house结构体的大小,但还有一些小细节需要注意,回看到add功能

case1

R31irT.png

case2

R31Ea4.png

在tmp_house结构体中还需要存储当前size的大小,每一个角色都有一个对应的current_size,因此tmp_house还应在原有的3个house结构体的大小上在添加3个int类型的大小,但这依然不够

在view功能中

R31GIH.png

tmp_house结构体中还需要记录能够view的次数,注意,这里qword_9070+0x101,并不是说在偏移为0x101的位置处,还需要看到前面是int类型的指针,是四字节,所以实际上应该是0x101*4=0x404的偏移,上面的current_size也是如此。

在edit功能中

R31DeS.png

tmp_house结构体中还需要记录能够edit的次数

因此,tmp_house结构体应该如下

在ida中创建结构体,看看我们的推测是否正确

add功能

R33h9A.png

R335ct.png

edit功能

R33HHS.png

成功

结构体恢复就到此为止了,现在再来分析程序就会舒服很多(贴了好多图,希望师傅们看得清楚些)

经过了上一阶段的分析,我们已经大致梳理了一遍程序的逻辑,现在来总结一下,并补充上面没有说到的

首先,这个程序有三个角色可以选择,在三个角色之间可以来回切换。在程序的开头会将三个角色各自的结构体的两个标志位都清0,程序运行起来后默认是使用的peppa这个角色,每个角色都有增删查改切换这五个功能,在view和edit功能中会检查第一个标志位flag是否为0,为0的情况下才能够进行相关操作;delete功能会检查flag和flagg这两个标志位是否都为0,都为0才会进行free,free之后将flag和flagg标志位置1,但并没有清空堆指针,所以这里可能会存在uaf;随后是切换角色的流程,假如我们从peppa切换到mummy

R3oJWF.png

如果next_character_num!=character_num,就会先将当前的角色的house结构体存储到global_house中,看到save_peppa_house这个函数

R3oNQJ.png

仔细观察,是不是少了些什么?house有两个标志位,flag和flagg,但这里只将flagg标志位保存了下来,继续往下看

R3owe1.png

接着根据next_character_num来恢复现场

R3osJO.png

在recover_peppahouse函数中将global_house中所有的数据都拷贝到了对应角色的house结构体中,而global_house中的对应的flag标志位是0,也就是说,当我们使用peppa这个角色free了一个chunk之后,flag=1,flagg=1,且这个chunk的指针没有清0,再切换到mummy,只会将flagg标志位进行存储,我们再切换回peppa这个角色,就会将global_house中的flag标志位赋值给house,这样一来peppa_house的flag=0,flagg=1,除了不能再次delete,view和edit功能都可以使用,也就是一个uaf漏洞。其他的角色也是一样,来回切换一次可以造成一个uaf。

程序使用calloc来申请chunk,因此无法使用tcache attack,并且申请的chunk要大于0x90,也无法使用fastbin attack(不知是否可以使用largebin_attack来攻击global_max_fast?)

house of pig本质上是通过 libc2.31 下的 largebin attack以及 FILE 结构利用,来配合 libc2.31 下的 tcache stashing unlink attack 进行组合利用的方法

整体思路如下:

1.为tcache_stashing_unlink plus做好准备,往一个tcache链中放入五个chunk,再往同样大小的smallbin中放入两个chunk,

2.构造出largebin,泄露libc地址和heap地址,进行第一次largebin attack,将free_hook-0x8的位置写上一个堆地址

3.进行tcache_stashing_unlink ,将free_hook-0x10作为一个堆地址链入tcache头,但由于使用calloc,我们无法申请到这个chunk

4.进行第二次largebin attack,将_io_list_all覆盖成一个堆地址,我们在这个堆上伪造IO_FILE,伪造的FILE结构体需要满足要求以调用malloc来申请tcache中的chunk,也就是我们要使2 * ((fp)->_IO_buf_end - (fp)->_IO_buf_base) + 100=free_hook所在的那个tcache链的大小,并且还要修改vtable指针,vtable原本指向IO_file_jumps,将其修改为指向_IO_str_jumps,原本应该调用 IO_file_overflow 的时候,就会转而调用如下的 IO_str_overflow,这样一来就能够进而调用malloc申请到free_hook-0x10处的空间,而如果_IO_buf_base指向的空间有数据的话,还会将其中的数据拷贝到malloc申请的chunk中,所以我们可以在IO_buf_base指向的空间布置好/bin/sh和system的地址,这样一来就被被memcpy到free_hook-0x10处,IO_str_overflow在最后还会free掉IO_buf_base指向的chunk,这样就会触发system('/bin/sh')getshell

(我太懒了,就直接拿官方exp来讲解了,师傅们包容包容orz)

第一部分,为tcache_stashing_unlink 做准备

R8GVNF.png

此时的bins如上图

第二部分,泄露libc地址和heap地址

R8YmO1.png

上图是将A16 free之后的情况

以及下图是此时的bins

R8YdTf.png

第三部分,第一次largebin_attack 将free_hook-0x8写为一个堆地址

这里解释一下Add(0xa0, 'C'*0x28)为什么能触发largebin_attack

R8DquF.jpg

在int_malloc函数的大循环开始处就会获取unsortedbin中的chunk

还是通过源码调试来看看,不过就只标出几个关键点,毕竟这不是本文的重点

R8rrVJ.png

取到了unsortedbin中的chunk

R8siR0.png

R8sAMT.png

将unsortedbin中的chunk解链

R8sKiR.png

将unsortedbin插入到largebin

R8sNod.png

这一步之后实际上就已经完成了largebin attack,但我们继续把流程走完

R8sRFs.png

largebin中多出了一个chunk

后续会进行一大堆标志位设置,我们直接看到切割chunk

R8ySOO.png

R8yVpt.png

切割了之前的unsortedbin,remainder产生

R8ylkj.png

然后将last_remainder插入到unsortedbin中

R8yJ10.png

流程结束,第一次largebin attack完成

R8y0AJ.png

free_hook-0x8被写入了一个堆地址

第四部分 第二次largebin attack ,往_io_list_all写入一个堆地址

R86CvV.png

第二次largebin attack完成

第五部分 tcache_stashing_unlink plus IO_FILE攻击

R87Wt0.png

在largebin中设置好chain

在使用角色三daddy申请到C4时会有一个gift

R8HPHA.png

会额外申请一个0xf0的chunk,然后往其中读入数据,并且是连续读入

R8XpTJ.png

这个0xf0的chunk会从unsortedbin中切割

R8X31P.png

largebin中的chain指向的正是这个chunk,使用fp命令可以将这个地址作为一个IO_FILE结构体查看

R8X51x.png

根据io_str_overflow申请chunk的size计算规则

根据malloc的申请规则,会申请0xa0的chunk,此时0xa0的tcache中的第一个chunk为free_hook-0x10

R8j9u8.png

malloc申请完chunk后,如果_IO_buf_base指向的空间有数据的话就会将其中的数据拷贝到new_buf中,也就是free_hook-0x10,_IO_buf_base指向位置的数据为/bin/sh和system的地址,因此最终会将/bin/sh拷贝到free_hook-0x10,将system拷贝到free_hook-0x8和free_hook,最终调用free则会触发system('/bin/sh')

R8vgY9.png

house of pig的触发条件就是调用 _IO_flush_all_lockp的条件,即需要满足如下三个之一:

这个程序里调用了很多exit,可以触发house of pig

我们跟到io_str_overflow里看看执行过程

RG9PbV.png

size和我们计算的一样

调用malloc之前

RG9VC4.png

调用malloc之后

RG9e29.png

free_hook已经被申请出去了

接下来开始memcpy

RG9Mb6.png

RG9art.png

old_buf指向的的数据是/bin/sh

memcpy执行之后

RG92Mn.png

随后就是getshell

最后提一嘴,为什么要将chunk申请到free_hook-0x10而不是free_hook-0x8,是因为新版本的glibc对tcache增加了检查,tcache申请的地址需要0x10对齐,这就是原因。

通过这题学到了很多,也复习巩固了很多知识点,耐着性子进行调试,硬着头皮恢复了结构体,收获满满。

house of pig一个新的堆利用详解 - 安全客,安全资讯平台 (anquanke.com)

新版本glibc下的IO_FILE攻击 - 安全客,安全资讯平台 (anquanke.com)

 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
/*
 
A revisit to large bin attack for after glibc2.30
 
Relevant code snippet :
 
    if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
        fwd = bck;
        bck = bck->bk;
        victim->fd_nextsize = fwd->fd;
        victim->bk_nextsize = fwd->fd->bk_nextsize;
        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
    }
 
 
*/
 
int main(){
  /*Disable IO buffering to prevent stream from interfering with heap*/
  setvbuf(stdin,NULL,_IONBF,0);
  setvbuf(stdout,NULL,_IONBF,0);
  setvbuf(stderr,NULL,_IONBF,0);
 
  size_t target = 0;
  size_t *p1 = malloc(0x428);
  size_t *g1 = malloc(0x18);
 
  size_t *p2 = malloc(0x418);
  size_t *g2 = malloc(0x18);
 
  free(p1);
  size_t *g3 = malloc(0x438);
 
 
  free(p2);
 
  p1[3] = (size_t)((&target)-4);
 
  size_t *g4 = malloc(0x438);
 
  assert((size_t)(p2-2) == target);
 
  return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
/*
 
A revisit to large bin attack for after glibc2.30
 
Relevant code snippet :
 
    if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
        fwd = bck;
        bck = bck->bk;
        victim->fd_nextsize = fwd->fd;
        victim->bk_nextsize = fwd->fd->bk_nextsize;
        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
    }
 
 
*/
 
int main(){
  /*Disable IO buffering to prevent stream from interfering with heap*/
  setvbuf(stdin,NULL,_IONBF,0);
  setvbuf(stdout,NULL,_IONBF,0);
  setvbuf(stderr,NULL,_IONBF,0);
 
  size_t target = 0;
  size_t *p1 = malloc(0x428);
  size_t *g1 = malloc(0x18);
 
  size_t *p2 = malloc(0x418);
  size_t *g2 = malloc(0x18);
 
  free(p1);
  size_t *g3 = malloc(0x438);
 
 
  free(p2);
 
  p1[3] = (size_t)((&target)-4);
 
  size_t *g4 = malloc(0x438);
 
  assert((size_t)(p2-2) == target);
 
  return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
 
static uint64_t victim[4] = {0, 0, 0, 0};
 
int main(int argc, char **argv){
    setbuf(stdout, 0);
    setbuf(stderr, 0);
 
    char *t1;
    char *s1, *s2, *pad;
    char *tmp;
 
    printf("You can use this technique to get a tcache chunk to arbitrary address\n");
 
    printf("\n1. need to know heap address and the victim address that you need to attack\n");
 
    tmp = malloc(0x1);
    printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        &victim, victim[0], victim[1], victim[2], victim[3]);
    printf("heap address: %p\n", tmp-0x260);
 
    printf("\n2. change victim's data, make victim[1] = &victim, or other address to writable address\n");
    victim[1] = (uint64_t)(&victim);
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
 
    printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
    printf("Here, I choose the size 0x60\n");
    for(int i=0; i<5; i++){
        t1 = calloc(1, 0x50);
        free(t1);
    }
 
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
        t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");
 
    s1 = malloc(0x420);
    printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
    pad = malloc(0x20);
    printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
    free(s1);
    printf("Free chunk %p to unsortedbin\n", s1);
    malloc(0x3c0);
    printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
    malloc(0x100);
    printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);
 
    printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
    printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
    s2 = malloc(0x420);
    pad = malloc(0x80);
    free(s2);
    malloc(0x3c0);
    malloc(0x100);
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
    printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);
 
    printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
    printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
    *(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;
 
    printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");
 
    calloc(1, 0x50);
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
        &victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");
 
    uint64_t *r = (uint64_t*)malloc(0x50);
    r[0] = 0xaa;
    r[1] = 0xbb;
    r[2] = 0xcc;
    r[3] = 0xdd;
 
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
 
static uint64_t victim[4] = {0, 0, 0, 0};
 
int main(int argc, char **argv){
    setbuf(stdout, 0);
    setbuf(stderr, 0);
 
    char *t1;
    char *s1, *s2, *pad;
    char *tmp;
 
    printf("You can use this technique to get a tcache chunk to arbitrary address\n");
 
    printf("\n1. need to know heap address and the victim address that you need to attack\n");
 
    tmp = malloc(0x1);
    printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        &victim, victim[0], victim[1], victim[2], victim[3]);
    printf("heap address: %p\n", tmp-0x260);
 
    printf("\n2. change victim's data, make victim[1] = &victim, or other address to writable address\n");
    victim[1] = (uint64_t)(&victim);
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
 
    printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
    printf("Here, I choose the size 0x60\n");
    for(int i=0; i<5; i++){
        t1 = calloc(1, 0x50);
        free(t1);
    }
 
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
        t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");
 
    s1 = malloc(0x420);
    printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
    pad = malloc(0x20);
    printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
    free(s1);
    printf("Free chunk %p to unsortedbin\n", s1);
    malloc(0x3c0);
    printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
    malloc(0x100);
    printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);
 
    printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
    printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
    s2 = malloc(0x420);
    pad = malloc(0x80);
    free(s2);
    malloc(0x3c0);
    malloc(0x100);
    printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
    printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);
 
    printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
    printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
    *(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;
 
    printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");
 
    calloc(1, 0x50);
    printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
        &victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);
 
    printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");
 
    uint64_t *r = (uint64_t*)malloc(0x50);
    r[0] = 0xaa;
    r[1] = 0xbb;
    r[2] = 0xcc;
    r[3] = 0xdd;
 
    printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
        victim[0], victim[1], victim[2], victim[3]);
 
    return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#define last(b)      ((b)->bk)
#define last(b)      ((b)->bk)
 
 
bin->bk = bck;
bck->fd = bin;
bin->bk = bck;
bck->fd = bin;
 
 
 
 
 
 
 
 
 
 
bck = tc_victim->bk;//取倒数第二个smallbin,但tc_victim->bk已经被我们设置为了&victim-0x10
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
    set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;//将倒数第一个chunk解链
bck = tc_victim->bk;//取倒数第二个smallbin,但tc_victim->bk已经被我们设置为了&victim-0x10
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
    set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;//将倒数第一个chunk解链
 
 
 
 
 
 
 
 
 
 
 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 9
支持
分享
最新回复 (4)
雪    币: 4168
活跃值: (15932)
能力值: ( LV9,RANK:710 )
在线值:
发帖
回帖
粉丝
2
支持!
2021-6-29 08:24
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3

 你是如何编译的2.31,有源码符号?


2021-7-4 16:32
0
雪    币: 2143
活跃值: (2807)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
wwwfo &nbsp;你是如何编译的2.31,有源码符号?
直接在Ubuntu官方网站下载对应源码https://launchpad.net/ubuntu/+source/glibc/
2021-7-4 21:50
0
雪    币: 1856
活跃值: (1221)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2024-5-8 11:27
0
游客
登录 | 注册 方可回帖
返回
//