二进制漏洞分析-5.华为安全监控漏洞(SMC MNTN OOB 访问)
二进制漏洞分析-10.华为TrustZone TEE_SERVICE_VOICE_REC漏洞
二进制漏洞分析-19.华为TrustZone TCIS漏洞
二进制漏洞分析-21.华为TrustZone TALoader信息泄露
二进制漏洞分析-22.华为TrustZone TA_uDFingerPrint漏洞
二进制漏洞分析-23.华为TrustZone TA_SensorInfo漏洞
华为TrustZone TA_HuaweiWallet漏洞
此通报包含有关以下漏洞的信息:
HWPSIRT-2022-12799 来电者验证不完整
HWPSIRT-2022-38244 GetCardALLByIndexV2 中的堆栈缓冲区溢出
HWPSIRT-2022-13974 genOffPayCodeSeedParam 中的堆栈缓冲区溢出
HWPSIRT-2022-57851 decodeCRSCert 中的堆栈缓冲区溢出
HWPSIRT-2022-20808 initPayCodeHead 中的堆缓冲区溢出
HWPSIRT-2022-94156 isSamePayCodeSeed 中的堆缓冲区过度读取
HWPSIRT-2022-46681 transferV1ToV2Paycode 中的堆缓冲区过度读取
HWPSIRT-2022-67754 CmdWalletGenPayCodeSeedParam 中的 OOB 访问
HWPSIRT-2022-31335 CmdWalletSavePayCodeSeed 中的 OOB 访问
HWPSIRT-2022-39460 CmdWalletSetPayCodeAuthInfo 中的 OOB 访问
HWPSIRT-2022-45266 CmdWalletGetTrafficPayCode 中的 OOB 访问
HWPSIRT-2022-28524 CmdWalletGetFinancePayCode 中的 OOB 访问
HWPSIRT-2022-82607 CmdWalletVerifyPayCodeAuthInfo 中的 OOB 访问
HWPSIRT-2022-61804 SendSetStatusCmd 中的 OOB 访问
HWPSIRT-2022-31800 CmdWalletGetCardByIndex 中的参数缓冲区溢出
HWPSIRT-2022-70865 CmdWalletApplyEnableAndDisableCardToI2C 中的参数缓冲区过度读取
HWPSIRT-2022-85843 CmdWalletActivateCardByBiometricsId 中的参数缓冲区过度读取
HWPSIRT-2022-55550 CmdWalletVerifySwipeCard 中的参数缓冲区过度读取
华为的TEE OS iTrustee实现了白名单机制,只允许特定CA与TA通话。TA 通过调用其 中的 and/或函数来声明允许哪些 CA 与其通信。对于原生二进制文件,它指定了预期的二进制路径和用户 ID,对于 APK,它指定了用于验证 APK 签名的软件包名称和公钥。AddCaller_CA_exec
AddCaller_CA_apk
TA_CreateEntryPoint
我们注意到 HuaweiWallet TA 没有调用这两个函数。因此,我们相信任何 APK 都可以与此 TA 对话。
GetCardALLByIndexV2
¶函数中存在堆栈缓冲区溢出,该溢出是从命令 (ID #0x20010) 调用的。此缓冲区溢出是函数中多个整数溢出/下溢的结果。GetCardALLByIndexV2
CmdWalletGetCardByIndex
unsigned int CmdWalletGetCardByIndex(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
card_list_t cards;
// ...
GetCardALLByIndexV2(params[3].value.a, &cards);
// ...
}
此函数以整数形式作为参数,表示要复制到其另一个参数中的哪0x10卡片。index
cards
在 ,检查以确保其为正值。[1]
index
在 ,将 乘以 0x2300 以计算对文件的读取偏移量。此处可能会发生整数溢出。[2]
index
在 ,将 乘以 0x10 以计算请求的最后一张卡的编号。此处可能会发生整数溢出。如果此数字小于 ,则执行一些计算。[3]
index+1
amount
在 ,要返回的卡数的计算方法是减去 乘以 0x10。减法可能发生整数下溢,乘法时可能发生整数上溢。[4]
count
amount
index
在 ,要从文件中读取的数据的大小是通过将 乘以 0x230 来计算的。此处可能会发生整数溢出。[5]
index
在 ,从堆中分配字节,用零填充,然后用文件的内容填充。[6]
buffer
size
在 处,对预期大小和读取大小执行检查,如果它们不同,则打印一条消息,但继续执行。[7]
在 ,将 (其所有字节)复制到参数中,如果大于 0x2304,则触发堆栈缓冲区溢出(堆栈帧中的堆栈变量也是如此)。[8]
buffer
size
cards
size
card
CmdWalletGetCardByIndex
int GetCardALLByIndexV2(int index, card_list_t *cards) {
// ...
if (index < 0 /* [1] */) { /* ... */ return 0xFFFF0006; }
// ...
int amount;
GetCardListV2Length(&amount);
// ...
TEE_SeekObjectData(object, 0x2300 * index /* [2] */, TEE_DATA_SEEK_SET);
// ...
if (amount <= 0x10 * (index + 1) /* [3] */) {
count = amount - 0x10 * index /* [4] */;
size = 0x230 * count /* [5] */;
} else {
count = 0x10;
size = 0x2300;
}
// ...
buffer = TEE_Malloc(/* [6] */ size, 0);
TEE_MemFill(buffer, 0, size);
TEE_ReadObjectData(object, buffer, size, &read_size);
if (/* [7] */ read_size != size) { /* ... */ }
// ...
TEE_MemFill(cards, 0, 0x2304);
TEE_MemMove(cards, buffer, size); /* [8] */
// ...
}
为了找到符合所有要求的值,我们在下面给出的 Python 脚本中使用了 Z3 求解器。运行此脚本会为我们提供 0x4924a3 的值,从而产生 0x35c0 字节。通过改变约束条件,可以获得多个工作值,从而产生不同的尺寸。index
index
size
from z3 import *x = BitVec('x', 32)
y = BitVec('y', 32)
n = BitVec('n', 32)
s = Solver()
s.add(x >= 0)
s.add(ULT(x * 0x2300, 292 * 0x230))
s.add(y == 0x10 * (x + 1))
s.add(y >= 0)
s.add(UGE(y, 292))
s.add(n == 0x230 * (292 - 0x10 * x))
s.add(UGE(n, 0x3000))
s.add(ULE(n, 0x4000))
print(s.check())
m = s.model()
x_val = None
for d in m.decls():
if d.name() == 'x':
x_val = int(str(m[d]))
print("%s = %s" % (d.name(), m[d]))
触发漏洞的概念验证代码会导致堆栈缓冲区溢出 0x35c0 字节,到达为堆栈分配的内存的末尾,并触发崩溃:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1d00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x6804d03, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7392 prefer-ca=7392
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemMove+0x54/0x68>
[HM] <GetCardALLByIndexV2>+0x1ac/0x2ec
[HM] Dump task states END
[HM]
genOffPayCodeSeedParam
¶命令 (ID #0x20046) 调用的函数中存在堆栈缓冲区溢出。genOffPayCodeSeedParam
CmdWalletGenPayCodeSeedParam
uint32_t CmdWalletGenPayCodeSeedParam(void *sessionContext, uint32_t paramTypes,
TEE_Param params[4])
{
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
// [...]
result = genOffPayCodeSeedParam(
buffer,
size,
encAK,
&encAKLen,
encVd,
&encVdLen,
AI,
&AILen);
// [...]
}
在 中,用户控制 (从偏移0xc中提取)。由于在调用 之前没有检查 ,因此可以触发 上的缓冲区溢出,这是 类型为堆栈分配结构内的 42 字节缓冲区。genOffPayCodeSeedParam
aidLen
params[0].memref.buffer
aidLen
TEE_MemMove
seed.aid
seed
pay_code_seed_v2_t
uint32_t genOffPayCodeSeedParam(
params_t *buffer,
uint32_t size,
void *encAK,
uint32_t *encAKLen_p,
void *encVd,
uint32_t *encVdLen_p,
void *AI,
uint32_t *AILen_p)
{
// [...]
// Stack buffer overflow
TEE_MemMove(seed.aid, (char *)buffer + buffer->aidOff, buffer->aidLen);
// [...]
}
在触发此错误的概念验证代码中,我们尝试将 0x10000 字节写入溢出堆栈大小,从而导致堆栈之后的 OOB 写入未映射的内存。
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x662b000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7656 prefer-ca=7656
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemMove+0x44/0x68>
[HM] <genOffPayCodeSeedParam>+0xa0/0x37c
[HM] Dump task states END
[HM]
decodeCRSCert
¶函数中存在任何多个堆栈缓冲区溢出,所有这些都是由相同的易受攻击的模式导致的。decodeCRSCert
该函数从命令 (ID #0x20025) 调用。decodeCRSCert
CmdWalletInitCRSCert
unsigned int CmdWalletInitCRSCert(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
certificationReq = params[0].memref.buffer;
certificationReqLen = params[0].memref.size;
// ...
initAndGenerateCRSPin(certificationReq, certificationReqLen, encryptedPin, &encryptedPinLen);
// ...
}
int initAndGenerateCRSPin(
void *certificationReq,
int certificationReqLen,
void *encryptedPin,
uint32_t *encryptedPinLen_p)
{
// ...
decodeCRSCert(certificationReq, certificationReqLen, crs_cert, &crs_cert_len);
// ...
}
它从参数中提供的 DER 编码的认证请求中提取各种 TLV(因此它完全由用户控制)。certificationReq
params[0].memref.buffer
易受攻击的模式如下:
特定类型的 TLV,此处 ,使用CRS_PUBLIC_KEY_TAG_MODULUS_REMAINDER
ta_bertlv_grp_find
TLV 的值指针是使用ta_bertlv_get_value
TLV 的长度是使用 和 uncheck 检索的ta_bertlv_get_length
值指针用作源,长度用作调用中的大小,其中目标是堆栈缓冲区TEE_MemMove
buf
这种易受攻击的模式会导致教科书堆栈缓冲区溢出,并在整个函数中重复多次。
TEE_Result decodeCRSCert(
void *certificationReq,
uint32_t certificationReqLen,
storage_t *crs_cert,
uint32_t *crs_cert_len_p)
{
// [...]
char plain[2048];
uint8_t buf[2048];
// [...]
tag = ta_bertlv_get_tag(&CRS_PUBLIC_KEY_TAG_HEAD_CRS);
tag_head_crs = ta_bertlv_grp_find(certificationReq, certificationReqLen, tag);
head_crs = ta_bertlv_get_value(tag_head_crs);
len_head_crs = ta_bertlv_get_length(tag_head_crs);
// [...]
tag = ta_bertlv_get_tag(&CRS_PUBLIC_KEY_TAG_SIGNATURE);
tag_signature = ta_bertlv_grp_find(head_crs, len_head_crs, tag);
signature = ta_bertlv_get_value(tag_signature);
len_signature = ta_bertlv_get_length(tag_signature);
// [...]
TEE_MemFill(&pub_key, v10, 0x808);
convertHexToByte(NXP_CA_PUBLIC_RSA_KEY_MOD, pub_key.mod, 0x100);
convertHexToByte(NXP_CA_PUBLIC_RSA_KEY_EXP, pub_key.exp, 6);
pub_key.exp_len = 3;
pub_key.mod_len = 0x80;
plainLen = 0x800;
ret = TA_RsaEncrypt(
&pub_key,
signature,
len_signature,
TEE_ALG_RSA_NOPAD,
plain,
&plainLen);
if (ret) {
/* ... */
// Error path
return 0xFFFF0006;
}
buf_off = 0;
TEE_MemMove(&buf[buf_off], &plain[1], plainLen - 0x16);
buf_off += plainLen - 0x16;
// [...]
tag = ta_bertlv_get_tag(CRS_PUBLIC_KEY_TAG_MODULUS_REMAINDER);
tag_modulus_remainder = ta_bertlv_grp_find(head_crs, len_head_crs, tag);
// User-controlled value
modulus_remainder = ta_bertlv_get_value(tag_modulus_remainder);
// User-controlled size
len_modulus_remainder = ta_bertlv_get_length(tag_modulus_remainder);
TEE_MemMove(&buf[buf_off], modulus_remainder, len_modulus_remainder);
buf_off += len_modulus_remainder;
// [...] similar pattern repeated many times [...]
}
为了访问 中易受攻击的代码路径,我们需要提供一个合法的 RSA 加密 blob ,以便在调用 后通过检查。我们没有针对此漏洞开发概念验证,因为我们没有花费额外的时间来尝试查找或制作这样的 blob。decodeCRSCert
certificationReq
TA_RsaEncrypt
initPayCodeHead
¶以下命令调用的函数中存在基于堆的缓冲区溢出:initPayCodeHead
CmdWalletVerifyPayCodeAuthInfo
(ID #0x20051)
CmdWalletGetFinancePayCode
(ID #0x20052)
CmdWalletGetTrafficPayCode
(ID #0x20048)
CmdWalletGenPayCodeSeedParam
(ID #0x20046)
CmdWalletCheckPayCodeSeedValid
(ID #0x20045)
为了说明此漏洞,我们将采用从命令开始的执行流程。CmdWalletGenPayCodeSeedParam
uint32_t CmdWalletGenPayCodeSeedParam(void *sessionContext, uint32_t paramTypes,
TEE_Param params[4])
{
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
// [...]
result = genOffPayCodeSeedParam(
buffer,
size,
encAK,
&encAKLen,
encVd,
&encVdLen,
AI,
&AILen);
// [...]
}
用户可以控制在偏移0xc处找到的值。acctLen
params[0].memref.buffer
uint32_t genOffPayCodeSeedParam(
params_t *buffer,
uint32_t size,
void *encAK,
uint32_t *encAKLen_p,
void *encVd,
uint32_t *encVdLen_p,
void *AI,
uint32_t *AILen_p)
{
// [...]
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 1);
// [...]
}
此值先传递到,然后传递到,不进行任何检查。initForUnionSeedList
initPayCodeHead
unsigned int initForUnionSeedList(
const void *account,
uint32_t accountLen,
pay_code_list_v2_t *pay_codes,
int a4)
{
// ...
initPayCodeHead(account, accountLen, pay_codes);
// ...
}
initPayCodeHead
最后使用此值作为 的大小参数,导致基于堆的缓冲区溢出。TEE_MemMove
head->data
unsigned int initPayCodeHead_isra_2(const void *account, uint32_t accountLen,
pay_code_list_v2_t *pay_codes)
{
// [...]
TEE_MemMove(head->data, account, accountLen);
// [...]
}
在我们的概念验证代码中,我们尝试将超过堆栈大小的 0x10000 字节写入未映射的内存中,从而导致堆之后的 OOB 写入。
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1d00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0xda6000, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=8210 prefer-ca=8210
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemMove+0x44/0x68>
[HM] <initPayCodeHead.isra.2>+0x4c/0xbc
[HM] Dump task states END
[HM]
isSamePayCodeSeed
¶该函数用于检查支付代码是否具有给定的 as 参数。比较中使用的长度是支付代码的辅助长度和参数的最大值。这允许提供大于 42 的最大辅助长度,从而导致缓冲区过度读取。isSamePayCodeSeed
aid
aidLen
aidLen
int isSamePayCodeSeed(const void *aid, uint32_t aidLen, int seedType, pay_code_seed_v2_t *seed) {
// ...
if (aidLen < seed->aidLen)
seed_aidLen = seed->aidLen;
else
seed_aidLen = aidLen;
return TEE_MemCompare(aid, seed->aid, seed_aidLen) == 0;
}
例如,可以使用命令 (ID #0x20045) 通过用户控制的参数调用此函数。CmdWalletCheckPayCodeSeedValid
unsigned int CmdWalletCheckPayCodeSeedValid(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
aid = params[1].memref.buffer;
aidLen = params[1].memref.size;
seedType = params[2].value.a;
// ...
checkOffPayCodeSeedValid(account, accountLen, aid, aidLen, seedType, &flushFlag);
// ...
}
int checkOffPayCodeSeedValid(
void *account,
uint32_t accountLen,
void *aid,
uint32_t aidLen,
uint8_t seedType,
uint32_t *flushFlag_p)
{
// ...
checkSeedInfoValid_constprop_12(aid, aidLen, seedType, &seed, &pay_codes, flushFlag_p);
// ...
}
unsigned int checkSeedInfoValid_constprop_12(
void *aid,
int aidLen,
int seedType,
pay_code_seed_v2_t **seed_p,
pay_code_list_v2_t *pay_codes,
uint32_t *flushFlag_p)
{
// ...
for (int index = 0; index < pay_codes->head->num; index++) {
if (isSamePayCodeSeed(aid, aidLen, seedType, &pay_codes->seeds[index]))
break;
}
// ...
}
我们的概念验证代码利用此错误来泄露堆内存,逐个暴力破解字节,并使用命令的返回代码区分正确和不正确的字节值。运行它会生成以下输出:seed->aid
00000000: 00 00 00 00 00 00 00 00 00 00 5e ec ed 82 b8 f5
00000010: a3 1d 1f 5c 3b 45 f8 ee 0d e6 1d 52 71 a5 43 75
00000020: fc 57 97 8d 0b 4b 45 94 23 8b ea 80 ce 5a 60 db
00000030: 05 7a 91 ca 1a 50 04 65 53 6f 00 00 00 00 00 00
00000040: 00 00 00 00 00 00 00 00 00 00 1b 0f 01 6b 05 32
00000050: 1c ba 86 e1 91 5d 58 6b a2 e0 00 00 00 00 00 00
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00000270: 00 00 00 00 21 04 00 00 c0 2d 00 00 a8 e5 7f 03
00000280: a8 e5 7f 03 1c 24 8f 03 00 00 00 00 00 00 00 00
00000290: 00 00 00 00 21 00 00 00 a1 2d 00 00 a8 e5 7f 03
000002a0: a8 e5 7f 03 00 00 00 00 00 00 00 00 00 00 00 00
000002b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00000380: 00 00 00 00 64 63 2d 01 00 00 00 00 01 00 00 00
00000390: 00 00 00 00 cd ab cd ab 02 00 00 00 00 00 00 00
000003a0: 10 00 00 a0 00 00 00 00 20 00 00 00 ff ff ff ff
000003b0: 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00
000003c0: 00 00 00 00 51 01 00 00 31 00 00 00 00 00 00 00
000003d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000003f0: 00 00 00 00 31 00 00 00 41 2c 00 00 a8 e5 7f 03
00000400: a8 e5 7f 03 00 00 00 00 02 00 00 00 20 02 00 10
00000410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00000440: 00 00 00 00 00 00 00 00 00 00 00 00 08 f8 8a 03
00000450: 00 00 00 00 61 00 00 00 e1 2b 00 00 a8 e5 7f 03
00000460: a8 e5 7f 03 00 00 00 00 00 00 00 00 00 00 00 00
00000470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000004f0: 00 00 00 00 a1 00 00 00 41 2b 00 00 a8 e5 7f 03
00000500: a8 e5 7f 03 00 00 00 00 00 00 00 00 00 00 00 00
00000510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00000600: 00 00 00 00 11 01 00 00 31 2a 00 00 a8 e5 7f 03
00000610: a8 e5 7f 03 57 49 1d f0 cb ec 8a 07 d8 07 c2 83
00000620: 8b df cf f0 91 79 84 20 da 22 ef 53 3d 62 56 b5
00000630: 6b 4e 0a 73 c6 9d 59 ba ed 70 9b c0 bb c8 5d 62
00000640: a0 ae 9b 68 6d e2 de 63 42 c7 50 71 f7 84 db 6c
00000650: 90 44 fb 1c bf 0b ff 15 48 db c3 82 6c d4 b5 2c
00000660: f9 e6 3e 69 ba 2a 4f 52 86 58 7f 5d 13 79 9d 0f
00000670: 8e ec 69 94 ed 25 7c 2b 35 1f 42 a7 9a ab a6 3a
00000680: 6f 9f d8 aa 71 f4 61 f3 3e 83 d8 dc 7a 4f f6 1c
transferV1ToV2Paycode
¶从命令 (ID #0x20046) 一直调用的函数中存在堆缓冲区溢出。transferV1ToV2Paycode
CmdWalletGenPayCodeSeedParam
unsigned int CmdWalletGenPayCodeSeedParam(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
// ...
genOffPayCodeSeedParam(buffer, size, encAK, &encAKLen, encVd, &encVdLen, AI, &AILen);
// ...
}
int genOffPayCodeSeedParam(
params_t *buffer,
uint32_t size,
void *encAK,
uint32_t *encAKLen_p,
void *encVd,
uint32_t *encVdLen_p,
void *AI,
uint32_t *AILen_p)
{
// ...
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 1);
// ...
}
unsigned int initForUnionSeedList(
const void *account,
uint32_t accountLen,
pay_code_list_v2_t *pay_codes,
int should_init)
{
// ...
ret_v1 = TEE_OpenPersistentObject(1, "sec_storage/huawei_wallet/payment_code", 0x26, 1, &object_v1);
// ...
if (ret_v1 == 0 && should_init) {
transferV1ToV2Paycode(object_v1, account, accountLen, pay_codes);
// ...
}
// ...
}
在 中,用户控制从 中提取的值等。将缓冲区与 进行比较,比较的大小是 和 之间的最大值。因此,我们控制控件的比较大小,导致堆缓冲区过度读取 。transferV1ToV2Paycode
accountLen
params[0].memref.buffer
account
pay_codes.head->data
pay_codes.head->acctLen
accountLen
pay_codes.head->data
unsigned int transferV1ToV2Paycode(
TEE_ObjectHandle object,
const void *account,
uint32_t accountLen,
pay_code_list_v2_t *pay_codes_v2)
{
// ...
acctLen = pay_codes.head->acctLen;
if (accountLen >= acctLen)
acctLen = accountLen;
if (TEE_MemCompare(pay_codes.head->data, account, acctLen)) { /* ... */ }
}
不幸的是,无法轻松演示此问题。这是因为通话需要之前保存过 v1 支付代码,而当前版本的 TA 不允许保存 v1 支付代码。但是应该可以通过加载旧版本的 TA 来访问此代码路径,使用它来保存 v1 支付代码,然后加载当前版本并触发传输。transferV1ToV2Paycode
CmdWalletGenPayCodeSeedParam
¶命令处理程序 (ID# 0x20046) 中有多个指针,可以任意控制这些指针,并导致 OOB 访问。CmdWalletGenPayCodeSeedParam
uint32_t CmdWalletGenPayCodeSeedParam(void *sessionContext, uint32_t paramTypes,
TEE_Param params[4])
{
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
// [...]
result = genOffPayCodeSeedParam(
buffer,
size,
encAK,
&encAKLen,
encVd,
&encVdLen,
AI,
&AILen);
// [...]
}
在 中,用户控制以下值:genOffPayCodeSeedParam
acct
偏移量 0x14 inbuffer
acctLen
偏移量 0x18 inbuffer
aidOff
偏移量 0x1c inbuffer
aidLen
偏移量 0x20 inbuffer
使用这些值,可以计算两个指针:
acct = buffer + acctOff
aid = buffer + aidOff
uint32_t genOffPayCodeSeedParam(
params_t *buffer,
uint32_t size,
void *encAK,
uint32_t *encAKLen_p,
void *encVd,
uint32_t *encVdLen_p,
void *AI,
uint32_t *AILen_p)
{
// [...]
// OOB Access
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 1);
// [...]
// OOB Access
TEE_MemMove(seed.aid, (char *)buffer + buffer->aidOff, buffer->aidLen);
// [...]
}
由于从不检查从输入缓冲区检索到的长度和偏移量,因此指针可以由用户控制,这会导致在后续函数调用中进行 OOB 访问(即 和),如上图所示。TEE_Param
initForUnionSeedList
genOffPayCodeSeedParam
触发此 bug 的概念证明代码会导致在访问越界值时,地址处的读取访问导致崩溃。0x4eadeeef = 0xdeadbeef + 0x70003000
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x4eadeeef, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7542 prefer-ca=7542
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemCompare+0x84/0xbc>
[HM] <initForUnionSeedList>+0x354/0x5f0
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
CmdWalletSavePayCodeSeed
¶在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletSavePayCodeSeed
saveOffPayCodeSeed
unsigned int CmdWalletSavePayCodeSeed(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
// ...
if (/* ... */ || buffer->twLen > 0x28) { /* ... */ }
// ...
return saveOffPayCodeSeed(buffer);
}
在 中,将提取以下值:saveOffPayCodeSeed
acct
长度和起始价buffer->acctLen
buffer->acctOff
acct
长度和起始价buffer->acctLen
buffer->acctOff
signText
长度和起始价buffer->signTextLen
buffer->signTextOff
signResult
长度和起始价buffer->signResultLen
buffer->signResultOff
aid
长度和起始价buffer->aidLen
buffer->aidOff
tw
长度和起始价buffer->twLen
buffer->twOff
TEE_Result saveOffPayCodeSeed(params_t *buffer) {
// ...
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 0);
// ...
verifySignWithRSA(
buffer->server_type,
(char *)buffer + buffer->signTextOff,
buffer->signTextLen,
(char *)buffer + buffer->signResultOff,
buffer->signResultLen);
// ...
for (int index = 0; index < pay_codes.head->num; index++) {
isSamePayCodeSeed(
(char *)buffer + buffer->aidOff,
buffer->aidLen,
buffer->seedType,
&pay_codes.seeds[index]);
// ...
}
// ...
TEE_MemMove(&seed->tw, (char *)buffer + buffer->twOff, buffer->twLen);
// ...
}
由于从不检查各种偏移量和长度值(除了 ),因此可以使 、 、 指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,稍后在(从 调用):buffer->twLen
acct
signText
signResult
aid
tw
params[0].memref.buffer
calc_hash
verifySignWithRSA
int verifySignWithRSA(int server_type, void *signValue, int signValueLen, void *signRes, int signResLen) {
// ...
calc_hash(signHash, 0x20u, TEE_ALG_SHA256, signValue, signValueLen);
// ...
}
TEE_Result calc_hash(void *hash, uint32_t a2, uint32_t algo, const void *data, uint32_t data_len) {
// ...
TEE_DigestDoFinal(operation, data, data_len, hash, hashLen);
// ...
}
触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x4eadeeef, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7421 prefer-ca=7421
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <SHA256_Update>+0xe4/0x114
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
CmdWalletSetPayCodeAuthInfo
¶在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletSetPayCodeAuthInfo
setPayCodeAuthInfo
unsigned int CmdWalletSetPayCodeAuthInfo(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
if (/* ... */ || buffer->twLen > 0x28 ) { /* ... */ }
return setPayCodeAuthInfo(buffer);
}
在 中,将提取以下值:setPayCodeAuthInfo
acct
长度和起始价buffer->acctLen
buffer->acctOff
signResult
长度和起始价buffer->signResultLen
buffer->signResultOff
tw
长度和起始价buffer->twLen
buffer->twOff
unsigned int setPayCodeAuthInfo(params_t *buffer) {
// ...
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 1);
// ...
verifySignWithRSA(
buffer->server_type,
signText,
signTextLen,
(char *)buffer + buffer->signResultOff,
buffer->signResultLen);
// ...
TEE_MemMove(&head->data[0x24], (char *)buffer + buffer->twOff, buffer->twLen);
// ...
calc_hash(hash, 0x20u, TEE_ALG_SHA256, (char *)buffer + buffer->twOff, buffer->twLen);
// ...
}
由于从不检查各种偏移量和长度值(除了 ),因此可以使其指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,稍后在(从 调用):buffer->twLen
acct
signResult
tw
params[0].memref.buffer
initPayCodeHead
initForUnionSeedList
unsigned int initForUnionSeedList(
const void *account,
uint32_t accountLen,
pay_code_list_v2_t *pay_codes,
int a4)
{
// ...
initPayCodeHead_isra_2(account, accountLen, pay_codes);
// ...
}
unsigned int initPayCodeHead_isra_2(const void *account, uint32_t accountLen, pay_code_list_v2_t *pay_codes) {
// ...
TEE_MemMove(head->data, account, accountLen);
// ...
}
触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x4eadeeef, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=6258 prefer-ca=6258
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemCompare+0x84/0xbc>
[HM] <initForUnionSeedList>+0x354/0x5f0
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM]
CmdWalletGetTrafficPayCode
¶在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletGetTrafficPayCode
getOffPayCode
unsigned int CmdWalletGetTrafficPayCode(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
UnionPayCode = params[1].memref.buffer;
UnionPayCodeLen = params[1].memref.size;
flushFlag = params[2].value.a;
// ...
getOffPayCode(
buffer,
size,
UnionPayCode,
&UnionPayCodeLen,
outTK,
&outTKLen,
UFK,
&UFKLen,
tw,
&twLen,
&flushFlag);
// ...
}
在 中,将提取以下值:getOffPayCode
acct
长度和起始价buffer->acctLen
buffer->acctOff
tw
长度和起始价buffer->twLen
buffer->twOff
aid
长度和起始价buffer->aidLen
buffer->aidLen
unsigned int getOffPayCode(
params_t *buffer,
int size,
void *UnionPayCode,
uint32_t *UnionPayCodeLen_p,
void *outTK,
uint32_t *outTKlen_p,
void *UFK,
uint32_t *UFKLen_p,
void *tw,
uint32_t *twLen_p,
uint32_t *flushFlag_p)
{
// ...
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 0);
// ...
checkAuthValid_constprop_13(
buffer->AuthType,
(char *)buffer + buffer->twOff,
buffer->twLen,
0,
0,
outTK,
outTKlen_p,
&pay_codes);
// ...
checkSeedInfoValid_constprop_12((char *)buffer + buffer->aidOff, buffer->aidLen, buffer->seedType, &seed, &pay_codes, flushFlag_p);
// ...
}
由于从不检查各种偏移量和长度值,因此可以使其指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,后面的 (called from ),如前所述。acct
tw
aid
params[0].memref.buffer
initPayCodeHead
initForUnionSeedList
触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x4eadeeef, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7679 prefer-ca=7679
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemCompare+0x84/0xbc>
[HM] <initForUnionSeedList>+0x354/0x5f0
[HM] Dump task states END
[HM]
CmdWalletGetFinancePayCode
¶在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletGetFinancePayCode
getOffPayCode
unsigned int CmdWalletGetFinancePayCode(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
UnionPayCode = params[1].memref.buffer;
UnionPayCodeLen = params[1].memref.size;
outTK = params[2].memref.buffer;
outTKlen = params[2].memref.size;
flushFlag = params[3].value.a;
// ...
getOffPayCode(
buffer,
size,
UnionPayCode,
&UnionPayCodeLen,
outTK,
&outTKlen,
UFK,
&UFKLen,
tw,
&twLen,
&flushFlag);
// ...
}
在 中,将提取以下值:getOffPayCode
acct
长度和起始价buffer->acctLen
buffer->acctOff
tw
长度和起始价buffer->twLen
buffer->twOff
aid
长度和起始价buffer->aidLen
buffer->aidLen
unsigned int getOffPayCode(
params_t *buffer,
int size,
void *UnionPayCode,
uint32_t *UnionPayCodeLen_p,
void *outTK,
uint32_t *outTKlen_p,
void *UFK,
uint32_t *UFKLen_p,
void *tw,
uint32_t *twLen_p,
uint32_t *flushFlag_p)
{
// ...
initForUnionSeedList((char *)buffer + buffer->acctOff, buffer->acctLen, &pay_codes, 0);
// ...
checkAuthValid_constprop_13(
buffer->AuthType,
(char *)buffer + buffer->twOff,
buffer->twLen,
0,
0,
outTK,
outTKlen_p,
&pay_codes);
// ...
checkSeedInfoValid_constprop_12((char *)buffer + buffer->aidOff, buffer->aidLen, buffer->seedType, &seed, &pay_codes, flushFlag_p);
// ...
}
由于从不检查各种偏移量和长度值,因此可以使其指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,后面的 (called from ),如前所述。acct
tw
aid
params[0].memref.buffer
initPayCodeHead
initForUnionSeedList
触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x4eadeeef, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7373 prefer-ca=7373
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemCompare+0x84/0xbc>
[HM] <initForUnionSeedList>+0x354/0x5f0
[HM] Dump task states END
[HM]
CmdWalletVerifyPayCodeAuthInfo
¶在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletVerifyPayCodeAuthInfo
verifyPayCodeAuthInfo
unsigned int CmdWalletVerifyPayCodeAuthInfo(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
buffer = (params_t *)params[0].memref.buffer;
size = params[0].memref.size;
tkOut = params[1].memref.buffer;
tkOutLen = params[1].memref.size;
acct = (char *)buffer + buffer->acctOff;
acctLen = buffer->acctLen;
AV = (char *)buffer + buffer->signTextOff;
AVLen = buffer->signTextLen;
SignRes = (char *)buffer + buffer->signResultOff;
SignResLen = buffer->signResultLen;
authType = buffer->AuthType;
// ...
verifyPayCodeAuthInfo(acct, acctLen, authType, AV, AVLen, SignRes, SignResLen, tkOut, &tkOutLen);
// ...
}
在 中,将提取以下值:verifyPayCodeAuthInfo
acct
长度和起始价buffer->acctLen
buffer->acctOff
AV
长度和起始价buffer->AVLen
buffer->AVOff
SignRes
长度和起始价buffer->SignResLen
buffer->SignResLen
unsigned int verifyPayCodeAuthInfo(
void *acct,
uint32_t acctLen,
int authType,
void *AuthValue,
uint32_t AuthValueLen,
void *signResult,
uint32_t signResultLen,
void *tkOut,
uint32_t *tkOutLen_p)
{
// ...
initForUnionSeedList(acct, acctLen, pay_codes, 1);
// ...
checkAuthValid_constprop_13(
authType,
AuthValue,
AuthValueLen,
signResult,
signResultLen,
tkOut,
tkOutLen_p,
pay_codes);
// ...
}
由于从不检查各种偏移量和长度值,因此可以使其指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,后面的 (called from ),如前所述。acct
AV
SignRes
params[0].memref.buffer
initPayCodeHead
initForUnionSeedList
触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef
SendSetStatusCmd
¶命令 (ID #0x20023) 和 (ID #0x20008) 调用的函数中存在越界访问。如下图所示,第三个值用作 的索引。但是,对此值没有边界检查,因此可以指定导致 OOB 访问的任意值。SendSetStatusCmd
CmdWalletActivateCardByBiometricsId
CmdWalletDisablePosTrade
TEE_Param
seReaderHandleList
uint32_t CmdWalletDisablePosTrade(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// [...]
ret = DisablePosTrade(aid, aid_len, rsp, &rsp_len, params[2].value.a);
// [...]
}
uint32_t DisablePosTrade(void *aid, uint32_t aid_len, void *rsp, uint32_t *rsp_len_p, int nameIndex) {
// [...]
ret = SendSetStatusCmd_constprop_10(aid, aid_len, 0, rsp, rsp_len_p, &failed, nameIndex);
// [...]
}
uint32_t SendSetStatusCmd(void *cmd, int cmd_len,
int status_type, void *rsp, uint32_t *rsp_len_p, char *failed,
int nameIndex)
{
// [...]
ret = TEE_SEReaderOpenSession(seReaderHandleList[4 * nameIndex], &se_sess_handle);
// [...]
}
此漏洞可使用概念证明代码触发,该代码在访问越界值时导致地址崩溃。0x6d608e8
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2200000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x6d608e8, fault_code: 0x92000005
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7766 prefer-ca=7766
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <SendSetStatusCmd.constprop.10+0x1f0/0x54c>
[HM] <SendSetStatusCmd.constprop.10>+0x1e4/0x54c
[HM] Dump task states END
[HM]
CmdWalletGetCardByIndex
¶(ID #0x20010) 函数中存在参数缓冲区溢出。在成功的情况下,堆栈变量被复制到输出参数缓冲区中。副本的大小为 0x2304 字节,但该函数从未检查输出参数缓冲区是否足够大。CmdWalletGetCardByIndex
cards
params[2]
unsigned int CmdWalletGetCardByIndex(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
TEE_MemMove(params[2].memref.buffer, &cards, 0x2304);
params[2].memref.size = 0x2304;
// ...
}
可以使用概念验证代码来触发此 bug。它会导致访问位于参数输出缓冲区之后的无效地址:
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2200000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70006303, fault_code: 0x92000047
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7862 prefer-ca=7862
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <TEE_MemMove+0x54/0x68>
[HM] <CmdWalletGetCardByIndex>+0xac/0x1d8
[HM] Dump task states END
[HM]
CmdWalletApplyEnableAndDisableCardToI2C
¶命令处理程序 (ID #0x20022) 中有多个指针,可以任意控制这些指针,并导致缓冲区过度读取。CmdWalletApplyEnableAndDisableCardToI2C
在 中,用户控制以下值:CmdWalletApplyEnableAndDisableCardToI2C
aid_len
偏移量 0xc ininbuf
signValueLen
偏移量 0x10 ininbuf
signResultLen
偏移量 0x14 ininbuf
challengeLen
偏移量 0x18 ininbuf
使用这些值,可以计算出四个指针:
aid = inbuf + 0x20
signValue = aid + aid_len + 1
signResult = signValue + signValueLen + 1
challenge = signResult + signResultLen + 1
uint32_t CmdWalletApplyEnableAndDisableCardToI2C(
void *sessionContext, uint32_t paramTypes, TEE_Param params[4])
{
inbufLen = params[0].memref.size;
inbuf = params[0].memref.buffer;
tokenLen = params[2].memref.size;
token = params[2].memref.buffer; // Values taken directly from the input param buffer.
server_type = inbuf[0];
auth_type = inbuf[1];
activate = inbuf[2];
aid = (char *)inbuf + 0x20;
aid_len = inbuf[3];
signValue = aid + aid_len + 1; /* Arbitrary pointer */
if (auth_type == 2 && activate == 1) {
signValueLen = inbuf[4];
signResult = signValue + signValueLen + 1; /* Arbitrary pointer */
signResultLen = inbuf[5];
challenge = signResult + signResultLen + 1; /* Arbitrary pointer */
challengeLen = inbuf[6];
} else {
// [...]
}
// [...]
ret = checkValidforActivateCard(
server_type,
auth_type,
activate,
aid,
aid_len,
signValue,
signValueLen,
signResult,
signResultLen,
1,
&valid);
// [...]
ret = encryptPinAndChallenge(challenge, challengeLen, hash, &hashLen);
// [...]
}
由于从不检查 和 值,并且可以指向内存中的任何位置(相对于 ),这会导致后续函数调用中的缓冲区过度读取。例如,后面的 (由 和 调用):signValueLen
signResultLen
challengeLen
signResult
challenge
params[0].memref.buffer
verifySignWithRSA
checkValidOfTransSrvReq
checkValidforActivateCard
int checkValidforActivateCard_part_4(
int server_type,
int auth_type,
int no_sign,
void *aid,
int aid_len,
void *signValue,
int signValue_len,
void *signResult,
int signResult_len,
char should_identify,
_BYTE *valid_p)
{
// ...
if (auth_type == 2) {
// ...
checkValidOfTransSrvReq(
server_type,
signValue,
signValue_len,
signResult,
signResult_len,
1);
// ...
}
// ...
}
int checkValidOfTransSrvReq(
int server_type,
void *signValue,
int signValueLen,
void *signResult,
unsigned int signResLen,
char needDelRandom)
{
// ...
verifySignWithRSA(server_type, signValue, signValueLen, signResult, signResLen);
// ...
}
int verifySignWithRSA(int server_type, void *signValue, int signValueLen, void *signRes, int signResLen) {
// ...
TA_RsaVerifyDigest(&key, signHash, 0x20, signRes, signResLen);
// ...
}
此漏洞可使用概念证明代码触发,该代码在访问越界值时导致地址处的读取访问导致崩溃。0x70006000
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1d00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70006000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7908 prefer-ca=7908
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] Dump task states END
[HM]
CmdWalletActivateCardByBiometricsId
¶在函数中,用户控制的值是从第一个参数输入缓冲区中提取的,特别是:CmdWalletActivateCardByBiometricsId
的长度和起始时间aid
aid_len = inbuf[3]
inbuf + 0x20
的长度和起始时间signValue
signValueLen = inbuf[4]
aid + aid_len + 1
的长度和起始时间signResult
signResultLen = inbuf[5]
signValue + signValueLen + 1
unsigned int CmdWalletActivateCardByBiometricsId(
void *sessionContext,
uint32_t paramTypes,
TEE_Param params[4])
{
// ...
inbuf = params[0].memref.buffer;
inbufLen = params[0].memref.size;
rsp = params[1].memref.buffer;
rspLen = params[1].memref.size;
token = params[2].memref.buffer;
tokenLen = params[2].memref.size;
// ...
server_type = inbuf[0];
auth_type = inbuf[1];
activate = inbuf[2];
aid = (char *)inbuf + 0x20;
aid_len = inbuf[3];
signValue = aid + aid_len + 1;
signValueLen = inbuf[4];
signResult = signValue + signValueLen + 1;
signResultLen = inbuf[5];
nameIndex = inbuf[7];
// ...
checkValidforActivateCard(
server_type,
auth_type,
1,
aid,
aid_len,
signValue,
signValueLen,
signResult,
signResultLen,
1,
&valid);
// ...
SendSetStatusCmd_constprop_10(aid, aid_len, 1, rsp, &rspLen, &failed, nameIndex);
// ...
}
由于从不检查 和 值,因此可以指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,后面的 (由 和 调用),如前所述。signValueLen
signResultLen
signResult
params[0].memref.buffer
verifySignWithRSA
checkValidOfTransSrvReq
checkValidforActivateCard
触发此 bug 的概念证明代码会导致在访问越界值时,地址0x70007000处的读取访问导致崩溃。
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1d00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70007000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7991 prefer-ca=7991
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] Dump task states END
[HM]
CmdWalletVerifySwipeCard
¶在函数中,用户控制的值是从第一个参数输入缓冲区中提取的,特别是:CmdWalletVerifySwipeCard
的长度和起始时间signValue
signValueLen = inbuf[4]
inbuf + 0x20
的长度和起始时间signResult
signResultLen = inbuf[5]
signValue + signValueLen + 1
unsigned int CmdWalletVerifySwipeCard(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
inbuf = params[0].memref.buffer;
inbufLen = params[0].memref.size;
token = params[1].memref.buffer;
tokenLen = params[1].memref.size;
server_type = inbuf[0];
auth_type = inbuf[1];
signValue = (char *)inbuf + 0x20;
signValueLen = inbuf[4];
signResult = signValue + signValueLen + 1;
signResultLen = inbuf[5];
// ...
checkValidforActivateCard_part_4(
server_type,
auth_type,
1,
should_identify,
should_identify,
signValue,
signValueLen,
signResult,
signResultLen,
should_identify,
&valid);
// ...
}
由于从不检查 和 值,因此可以指向内存中的任意位置(相对于 ),这会导致在后续函数调用中进行 OOB 访问。例如,后面的 (由 和 调用),如前所述。signValueLen
signResultLen
signResult
params[0].memref.buffer
verifySignWithRSA
checkValidOfTransSrvReq
checkValidforActivateCard
触发此 bug 的概念证明代码会导致在访问越界值时,地址处的读取访问导致崩溃。0x70006000
[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x70006000, fault_code: 0x92000007
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM] name=[TA_HuaweiWallet] tid=40 is-idle=0 is-curr=0
[HM] state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM] aff[0]=ff
[HM] flags=1000 smc-switch=0 ca=7601 prefer-ca=7601
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] Dump task state
我们验证了这些漏洞是否影响了以下设备:
麒麟990:P40 专业版 (ELS)
请注意,其他型号可能已受到影响。
名字 | 严厉 | CVE漏洞 | 补丁 |
---|---|---|---|
堆缓冲区溢出initPayCodeHead | 危急 | 不适用(*) | 固定 |
堆栈缓冲区溢出decodeCRSCert | 危急 | 不适用(*) | 固定 |
堆栈缓冲区溢出genOffPayCodeSeedParam | 危急 | 不适用(*) | 固定 |
堆栈缓冲区溢出GetCardALLByIndexV2 | 危急 | 不适用(*) | 固定 |
来电者验证不完整 | 危急 | 不适用(*) | 固定 |
OOB 访问SendSetStatusCmd | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletVerifyPayCodeAuthInfo | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletGetFinancePayCode | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletGetTrafficPayCode | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletSetPayCodeAuthInfo | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletSavePayCodeSeed | 中等 | 不适用(*) | 固定 |
OOB 访问CmdWalletGenPayCodeSeedParam | 中等 | 不适用(*) | 固定 |
堆缓冲区过度读取transferV1ToV2Paycode | 中等 | 不适用(*) | 固定 |
堆缓冲区过度读取isSamePayCodeSeed | 中等 | 不适用(*) | 固定 |
参数缓冲区过度读取CmdWalletVerifySwipeCard | 低 | 不适用 | 固定 |
参数缓冲区过度读取CmdWalletActivateCardByBiometricsId | 低 | 不适用 | 固定 |
参数缓冲区过度读取CmdWalletApplyEnableAndDisableCardToI2C | 低 | 不适用 | 固定 |
参数缓冲区溢出CmdWalletGetCardByIndex | 低 | 不适用 | 固定 |
(*)华为关于APP漏洞的中、高、严重声明:
它们通过AppGallery升级来解决。通常,不会为此类漏洞分配 CVE 编号。
31年2022月<>日,华为PSIRT收到漏洞报告。
07年2022月<>日 - 华为PSIRT确认该漏洞报告。
从 30 年 2022 月 19 日至 2023 年 <> 月 <> 日 - 我们定期交换有关公告发布的信息。
二进制漏洞(更新中)
其它课程
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
恶意软件开发(更新中)
还有很多免费教程(限学员)
更多详细内容添加作者微信