SubmittedCWE-822 — Untrusted Pointer Dereference
IPC 回调从 IPC 数据读取函数指针并直接调用
View Upstream Issuegitcode.com/openharmony/security_device_auth/issues/1035CWE:CWE-822 — Untrusted Pointer Dereference
Repository:security_device_auth
Date:2026-04-29
Reporter:Zirui
漏洞编号:
CWE-822 (Untrusted Pointer Dereference)
漏洞归属组件
frameworks/src/standard
漏洞归属版本
2026.04.23 最新版本
CVSS V3.0分值
漏洞简述
完整攻击链
Step 1 — 发送端写入原始指针到 IPC
// ipc_callback_proxy.cpp:28-49 — ProxyDevAuthCb::DoCallBack
void ProxyDevAuthCb::DoCallBack(int32_t callbackId, uintptr_t cbHook, ...) {
(void)data.WriteInt32(callbackId); // line 43: 写入 callbackId
(void)data.WritePointer(cbHook); // line 44: 将函数指针作为 uintptr_t 写入 IPC
ret = remote->SendRequest(
static_cast<uint32_t>(DevAuthCbInterfaceCode::DEV_AUTH_CALLBACK_REQUEST), ...);
}
攻击者控制 cbHook 值,通过 WritePointer 序列化为原始 uintptr_t 写入 IPC 消息。
Step 2 — 接收端从 IPC 读出指针,无任何验证
// ipc_callback_stub.cpp:55-80 — StubDevAuthCb::OnRemoteRequest
int32_t StubDevAuthCb::OnRemoteRequest(uint32_t code, MessageParcel &data, ...) {
if (data.ReadInterfaceToken() != GetDescriptor()) { return -1; } // line 58: 仅接口令牌
callbackId = data.ReadInt32(); // line 71
cbHook = data.ReadPointer(); // line 72: 从 IPC 读出原始指针,无验证
StubDevAuthCb::DoCallBack(callbackId, cbHook, data, reply, option); // line 73: 直接分发
}
Step 3 — DoCallBack 仅检查非零
// ipc_callback_stub.cpp:30-53 — StubDevAuthCb::DoCallBack
void StubDevAuthCb::DoCallBack(int32_t callbackId, uintptr_t cbHook, ...) {
if (cbHook == 0x0) { return; } // line 38: 唯一检查:非零即通过
ProcCbHook(callbackId, cbHook, cbDataCache, ...); // line 51: 传递攻击者控制的地址
}
Step 4 — ProcCbHook 分发至回调桩
// ipc_adapt.cpp:736-757 — ProcCbHook
void ProcCbHook(int32_t callbackId, uintptr_t cbHook, ...) {
CallbackStub stubTable[] = {
OnTransmitStub, OnSessKeyStub, OnFinishStub, OnErrorStub,
OnRequestStub, OnGroupCreatedStub, OnGroupDeletedStub, OnDevBoundStub,
OnDevUnboundStub, OnDevUnTrustStub, OnDelLastGroupStub, OnTrustDevNumChangedStub,
OnCredAddStub, OnCredDeleteStub, OnCredUpdateStub,
}; // line 739-744: 15 个回调桩
if ((callbackId < CB_ID_ON_TRANS) || (callbackId > CB_ID_ON_CRED_UPDATE)) { return; }
if (cbHook == 0x0) { return; } // line 750: 再次空指针检查,仍无地址验证
CallbackParams params = { cbHook, cbDataCache, cacheNum, *reply };
stubTable[callbackId - 1](params); // line 755: 分发
}
Step 5 — 回调桩 reinterpret_cast 后直接调用(CFI 已禁用)
// ipc_adapt.cpp:479-497 — OnTransmitStub(其余 14 个模式相同)
__attribute__((no_sanitize("cfi"))) static void OnTransmitStub(CallbackParams params) {
bool (*onTransmitHook)(int64_t, uint8_t *, uint32_t) =
reinterpret_cast<bool (*)(int64_t, uint8_t *, uint32_t)>(params.cbHook); // line 488
// ...从 IPC 数据提取参数...
bRet = onTransmitHook(requestId, data, dataLen); // line 494: 执行攻击者指定的地址
}
// ipc_adapt.cpp:499-516 — OnSessKeyStub(会话密钥回调)
__attribute__((no_sanitize("cfi"))) static void OnSessKeyStub(CallbackParams params) {
void (*onSessKeyHook)(int64_t, uint8_t *, uint32_t) =
reinterpret_cast<void (*)(int64_t, uint8_t *, uint32_t)>(params.cbHook); // line 508
onSessKeyHook(requestId, keyData, dataLen); // line 514: 执行,参数含会话密钥数据
}
全部 15 个桩函数(OnTransmitStub, OnSessKeyStub, OnFinishStub, OnErrorStub, OnRequestStub, OnGroupCreatedStub, OnGroupDeletedStub, OnDevBoundStub, OnDevUnboundStub, OnDevUnTrustStub, OnDelLastGroupStub, OnTrustDevNumChangedStub, OnCredAddStub, OnCredDeleteStub, OnCredUpdateStub)均标注 __attribute__((no_sanitize("cfi"))) 禁用控制流完整性检查。
触发条件
- 攻击者进程可向 device_auth 回调桩发送 IPC 消息(任何拥有
TOKEN_NATIVE权限的本地进程) - 回调桩已注册(任何设备认证客户端调用
InitProxyAdapt()时注册) - 攻击者知道接口描述符字符串(可从公开共享库中获取)
影响性分析说明
影响
- 任意代码执行:攻击者指定
cbHook为任意地址,在 device_manager / softbus_server(APL_SYSTEM_CORE/APL_SYSTEM_BASIC)上下文中执行 - 会话密钥窃取:OnSessKeyStub 接收 ECDH/DH 原始会话密钥数据
- 信任组操控:OnGroupCreatedStub / OnDevBoundStub 控制设备信任关系
- CFI 显式禁用,编译器控制流完整性保护不生效
原理分析
触发条件
- 攻击者进程可向 device_auth 回调桩发送 IPC 消息(任何拥有
TOKEN_NATIVE权限的本地进程) - 回调桩已注册(任何设备认证客户端调用
InitProxyAdapt()时注册) - 攻击者知道接口描述符字符串(可从公开共享库中获取)
受影响版本
规避方案或消减措施
建议修复(伪代码)
用回调索引替代原始指针传输:
// ===== 方案:回调注册表 + 索引传输 =====
// 1) 新增本地回调注册表(仅在进程内部维护)
static std::unordered_map<int32_t, uintptr_t> g_registeredCallbacks;
static int32_t g_nextCallbackIndex = 1;
// 2) 注册时:存储函数指针,返回索引
int32_t RegisterCallback(uintptr_t funcPtr) {
int32_t idx = g_nextCallbackIndex++;
g_registeredCallbacks[idx] = funcPtr;
return idx; // 返回索引而非原始指针
}
// 3) Proxy 端:发送索引而非指针
void ProxyDevAuthCb::DoCallBack(int32_t callbackId, int32_t callbackIndex, ...) {
data.WriteInt32(callbackId);
data.WriteInt32(callbackIndex); // 改为发送索引
// ... 其余不变 ...
}
// 4) Stub 端:通过索引查找本地指针
void StubDevAuthCb::DoCallBack(int32_t callbackId, int32_t callbackIndex, ...) {
auto it = g_registeredCallbacks.find(callbackIndex);
if (it == g_registeredCallbacks.end()) {
LOGE("Invalid callback index: %d", callbackIndex);
return; // 拒绝未注册的索引
}
uintptr_t cbHook = it->second; // 本地验证后的合法指针
ProcCbHook(callbackId, cbHook, ...);
}
// 5) 移除所有 __attribute__((no_sanitize("cfi")))
// OnTransmitStub, OnSessKeyStub, ... (15 个桩函数)