not_the_same_3dsctf_2016-mprotect提升权限
这道题有两种解法,一种是fgets读取flag,另一种是mprotect提升权限,这里我们只介绍第二种解法
checksec,题目只开了栈不可执行,其他保护没有开启
int mprotect(void *addr, size_t len, int prot);
addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。
len:被修改保护属性区域的长度,最好为页大小整数倍。prot: 内存的权限 # 0x7 == 可读可写可执行
PROT_READ:可写,值为 1
PROT_WRITE:可读, 值为 2
PROT_EXEC:可执行,值为 4
PROT_NONE:不允许访问,值为 0
程序有mprotect函数,所以我们可以用mprotect函数修改got表
或bss段
内存区域的权限为0x7
ctrl+s
打开程序的段表,观察到bss段的起始地址不是4kb(0x1000)
的整数倍,所以选择修改got表
值得注意的是,这个程序没有关闭输入输出缓冲区,所以我们recv
不到b0r4 v3r s3 7u 4h o b1ch4o m3m0...
直接发送数据即可
缓冲区的三种类型
1.全缓冲
实际的I/O操作只有在缓冲区被填满了之后才会进行。flush描述了标准I/O缓冲的写操作。缓冲区可以由标准I/O函数自动flush(例如缓冲区满的时候);或者我们调用fflush函数。
2.行缓冲
在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的I/O操作。这允许我们一次写一个字符,但是只有在写完一行之后才做I/O操作。一般的,涉及到终端的流–例如标准输入(stdin)和标准输出(stdout)–是行缓冲的。
3.无缓冲
标准I/O库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。
缓冲区刷新
- 缓冲区满时;
- 执行flush语句,即使用特定函数刷新缓冲区;
- 执行endl语句,即行缓冲区遇到回车时(即’\n’);
- 关闭文件。
gdb计算出偏移,找出合适的gadget
1 | payload1=b'a'*45+mprotect地址+gadget+参数addr+参数len+参数prot |
exp如下:
1 | from pwn import * |