ProcessSkeleton::UnFlattenDBinderData 任意地址读取漏洞
View Upstream Issuegitcode.com/openharmony/communication_ipc/issues/1917漏洞概述
在 OpenHarmony 的分布式 Binder (DBinding) 实现中,ProcessSkeleton::UnFlattenDBinderData 函数从 IPC Parcel 中读取 binder_buffer_object 结构体后,直接将其中的 buffer 字段(一个 binder_uintptr_t 整数)强制转换为本地内存指针,并作为 memcpy_s 的源地址使用。
在跨设备 IPC 场景下,该 Parcel 数据来自远程设备,攻击者可以构造任意指针值和长度,触发:
- 任意地址读取 — 泄露目标进程的内存内容
- 进程崩溃 — 访问不可读地址导致 SIGSEGV
- 潜在的缓冲区溢出 —
obj->length未与目标缓冲区大小比较
漏洞详情
问题代码
文件: ipc/native/src/core/framework/source/process_skeleton.cpp
// Lines 410-420
bool ProcessSkeleton::UnFlattenDBinderData(Parcel &parcel, dbinder_negotiation_data *&dbinderData)
{
auto *buf = parcel.ReadBuffer(sizeof(binder_buffer_object), false);
if (buf == nullptr) {
return false;
}
auto obj = reinterpret_cast<const binder_buffer_object *>(buf);
// ⚠️ 漏洞点:obj->buffer 是从 Parcel 读取的整数,直接当作指针使用
auto ret = memcpy_s(dbinderData, sizeof(dbinder_negotiation_data),
reinterpret_cast<const void *>(obj->buffer), // ← 攻击者可控的指针值
obj->length); // ← 攻击者可控的长度
return (ret == EOK);
}
对比:正常的 Flatten 流程
// Lines 393-407
bool ProcessSkeleton::FlattenDBinderData(Parcel &parcel, const dbinder_negotiation_data *&dbinderData)
{
binder_buffer_object obj;
obj.hdr.type = BINDER_TYPE_PTR;
obj.flags = BINDER_BUFFER_FLAG_HAS_DBINDER;
obj.buffer = reinterpret_cast<binder_uintptr_t>(dbinderData); // ← 本地指针转整数
obj.length = sizeof(dbinder_negotiation_data);
if (!parcel.WriteBuffer(&obj, sizeof(binder_buffer_object))) {
return false;
}
return true;
}
问题根源:FlattenDBinderData 将本地指针值写入 Parcel,UnFlattenDBinderData 在接收端直接将该整数值当作本地指针使用。这在同一设备内的进程间通信中是安全的(指针值在同一地址空间有效),但在跨设备的分布式 Binder 场景下,远程设备的指针值在本地地址空间无意义。
攻击场景
场景 1:跨设备 IPC(分布式 Binder)
设备 A (攻击者) 设备 B (受害者)
| |
| 1. 构造恶意 Parcel |
| obj.buffer = 0x7fff12340000 |
| obj.length = 256 |
| |
| 2. 通过 DBinding 发送 |
|----------------------------------->|
| | 3. UnFlattenDBinderData 被调用
| | memcpy_s(dbinderData, 256,
| | (void*)0x7fff12340000, 256)
| | ↓
| | 4. 读取地址 0x7fff12340000 的 256 字节
| | - 若地址可读:泄露内存内容
| | - 若地址不可读:SIGSEGV 崩溃
场景 2:本地 IPC 数据损坏
即使在本地 IPC 中,如果 Parcel 数据在传输过程中被损坏(内存错误、竞态条件),也可能导致 obj->buffer 指向无效地址。
触发条件
- 分布式 Binder 通信:设备 A 与设备 B 通过 DBinding 进行跨设备 IPC
- 攻击者控制发送端:攻击者可以构造 Parcel 数据(例如通过恶意应用或中间人攻击)
- 目标函数被调用:接收端调用
UnFlattenDBinderData解析 Parcel 中的 dbinder 协商数据
调用链
DBinderService::OnRemoteRequest
→ DBinderService::ProcessOnSessionOpened
→ DBinderService::AttachSessionObject
→ ProcessSkeleton::UnFlattenDBinderData ← 漏洞触发点
影响分析
1. 任意地址读取(信息泄露)
攻击者可以设置 obj->buffer 为目标进程中的任意地址(例如堆地址、栈地址、.data 段),并设置 obj->length 为期望读取的字节数。memcpy_s 会将该地址的内容拷贝到 dbinderData 缓冲区。
泄露目标:
- 堆上的敏感数据(用户凭证、密钥、会话令牌)
- 栈上的返回地址(用于绕过 ASLR)
- 全局变量(配置信息、状态标志)
利用难度:
- 需要知道目标地址(可通过信息泄露或猜测)
- 需要能够接收到
dbinderData的内容(可能需要进一步的漏洞链)
2. 进程崩溃(拒绝服务)
如果 obj->buffer 指向不可读的地址(例如 NULL、未映射的内存区域),memcpy_s 会触发 SIGSEGV,导致目标进程崩溃。
影响范围:
- 系统服务进程(如
foundation、samgr) - 应用进程(通过 DBinding 与远程设备通信的应用)
3. 潜在的缓冲区溢出
obj->length 未与 sizeof(dbinder_negotiation_data) 进行比较。虽然 memcpy_s 的 destMax 参数限制了写入上限,但如果 obj->length > sizeof(dbinder_negotiation_data),memcpy_s 会返回错误(ERANGE),但调用者可能未检查返回值。
漏洞复现
PoC 概念(需要 OpenHarmony 设备)
#include "ipc_skeleton.h"
#include "message_parcel.h"
#include "dbinder_service.h"
void TriggerArbitraryRead() {
MessageParcel data;
// 构造恶意的 binder_buffer_object
struct binder_buffer_object {
struct binder_object_header hdr;
binder_uintptr_t buffer; // ← 设置为目标地址
binder_size_t length; // ← 设置为读取长度
binder_size_t parent;
size_t parent_offset;
};
binder_buffer_object malicious_obj;
malicious_obj.hdr.type = BINDER_TYPE_PTR;
malicious_obj.buffer = 0x7fff12340000; // 目标地址(例如栈地址)
malicious_obj.length = 256; // 读取 256 字节
malicious_obj.parent = 0;
malicious_obj.parent_offset = 0;
data.WriteBuffer(&malicious_obj, sizeof(malicious_obj));
// 通过 DBinding 发送到远程设备
// (具体实现取决于 DBinding 的 API)
// 远程设备调用 UnFlattenDBinderData 时会读取地址 0x7fff12340000
}
崩溃验证
// 设置 obj->buffer 为 NULL 或无效地址
malicious_obj.buffer = 0x0; // NULL pointer
malicious_obj.length = 1;
// 发送后,目标进程会在 memcpy_s 中触发 SIGSEGV
修复建议
方案 1:直接从 Parcel 读取数据(推荐)
不使用 binder_buffer_object 中的指针,而是直接从 Parcel 中按字段读取 dbinder_negotiation_data:
bool ProcessSkeleton::UnFlattenDBinderData(Parcel &parcel, dbinder_negotiation_data *&dbinderData)
{
// 直接从 Parcel 读取 dbinder_negotiation_data 的内容
const uint8_t *negotiationBuf = parcel.ReadBuffer(sizeof(dbinder_negotiation_data));
if (negotiationBuf == nullptr) {
ZLOGE(LOG_LABEL, "ReadBuffer failed for dbinder_negotiation_data");
return false;
}
errno_t ret = memcpy_s(dbinderData, sizeof(dbinder_negotiation_data),
negotiationBuf, sizeof(dbinder_negotiation_data));
if (ret != EOK) {
ZLOGE(LOG_LABEL, "memcpy_s failed: %d", ret);
return false;
}
return true;
}
方案 2:验证指针有效性(不推荐,难以实现)
在跨设备场景下,无法验证远程指针的有效性。此方案仅适用于本地 IPC。
涉及文件
ipc/native/src/core/framework/source/process_skeleton.cpp(line 410-420)ipc/native/src/core/framework/include/process_skeleton.h(line 49)
参考
- CWE-822: Untrusted Pointer Dereference
- CWE-125: Out-of-bounds Read
- OpenHarmony DBinding 架构文档