Dashboard/Issues/OH-2026-IPC-001
SubmittedCWE-822 — Untrusted Pointer Dereference

ProcessSkeleton::UnFlattenDBinderData 任意地址读取漏洞

View Upstream Issuegitcode.com/openharmony/communication_ipc/issues/1917
CWE:CWE-822 — Untrusted Pointer Dereference
Date:2026-05-07
Reporter:Zirui

漏洞概述

在 OpenHarmony 的分布式 Binder (DBinding) 实现中,ProcessSkeleton::UnFlattenDBinderData 函数从 IPC Parcel 中读取 binder_buffer_object 结构体后,直接将其中的 buffer 字段(一个 binder_uintptr_t 整数)强制转换为本地内存指针,并作为 memcpy_s 的源地址使用。

在跨设备 IPC 场景下,该 Parcel 数据来自远程设备,攻击者可以构造任意指针值和长度,触发:

  1. 任意地址读取 — 泄露目标进程的内存内容
  2. 进程崩溃 — 访问不可读地址导致 SIGSEGV
  3. 潜在的缓冲区溢出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 指向无效地址。

触发条件

  1. 分布式 Binder 通信:设备 A 与设备 B 通过 DBinding 进行跨设备 IPC
  2. 攻击者控制发送端:攻击者可以构造 Parcel 数据(例如通过恶意应用或中间人攻击)
  3. 目标函数被调用:接收端调用 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,导致目标进程崩溃。

影响范围

  • 系统服务进程(如 foundationsamgr
  • 应用进程(通过 DBinding 与远程设备通信的应用)

3. 潜在的缓冲区溢出

obj->length 未与 sizeof(dbinder_negotiation_data) 进行比较。虽然 memcpy_sdestMax 参数限制了写入上限,但如果 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 架构文档