静态链接之mprotect
静态链接之mprotect
这篇文章针对ctfshow上的49,50题
什么是mprotect函数
meprotect函数可以用来修改指定内存页的权限为可读、可写、可执行。
大多可写的部分都不可执行,利用该函数修改后可传入shellcode来获取shell
函数原型:
1 | int mprotect(const void*start,size_t len,int prot); |
第一个参数*start是指向需要进行操作的地址
第二个参数len是地址往后多大的长度
第三个参数port是要赋予的权限
区间开始的地址start必须是一个内存页的起始地址(地址的后三位为0,0x1000=4096u),指定的内存区间必须包含整个内存页(4KB=4096B)
区间长度len必须是页大小的整数倍。
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值
prot可以取一下几个值,并且可以用”|”将几个属性合起来使用:
- PROT_READ:表示内存段内的内容可写;
- PROT_WRITE:表示内存段内的内容可读;
- PROT_EXEC:表示内存段中的内容可执行;
- PROT_NONE:表示内存段中的内容根本没法访问;
- prot=7 是可读、可写、可执行7=1+2+4(r:4 w:2 x:1)
返回值:0;成功,-1;失败
gdb调试vmmap看段属性时r w x分别代表可读 可写 可执行
其中0x8048000-0x80d7000段权限为可读(r)可执行(x)
可以通过gdb调试输入disass mprotect
查看mprotect函数对应的汇编代码从而确定其地址
也可以直接通过elf.sym['mprotect']
来间接获取其地址
例题pwn49
exp
1 | from pwn import* |
分析
题目提示mprotect函数,checksc发现开启了NX和canany(实际上没开canany因为checksec的版本过低),file发现程序是静态链接,IDA发现栈溢出漏洞
gdb调试vmmap查看段属性
发现0x80d8000–0x80dc000段可读写但是不可执行因为是静态链接所以程序中有很多函数,包括mprotect函数,故目前思路为利用mprotect函数修改0x80d8000–0x80dc000段其中一部分为可执行,在该部分填入shell绕过NX
IDA中Ctrl+s查看段表
此处选择起始地址为got.plt段首地址(#区间开始的地址start必须是一个内存页的起始地址(地址的后三位为0,0x1000=4096u)
len设置为0x1000(够写入shell且为内存页大小的整数倍)
port设置为7
因为程序是静态链接的所以有很多ROPgadgets
在设置payload时用完mportect函数还需要写入read函数来输入shell故需要用3个pop1个ret来“跨过”mportect的参数
1 | ROPgadget --binary pwn --only 'pop|ret' | grep 'pop' |
因为32位程序是栈传参所以不用管pop到的寄存器,只是为了跳过三个参数ret到read函数上
故payload设置为
1 | payload=b'a'*(0x12+4)+p32(mprotect_addr)+p32(pop_eax_edx_ebx_addr)+p32(got_addr)+p32(0x1000)+p32(0x7) |