二进制漏洞分析-24.华为TrustZone TA_HuaweiWallet漏洞
2023-12-16 11:0:35 Author: 安全狗的自我修养(查看原文) 阅读量:8 收藏

 

-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_execAddCaller_CA_apkTA_CreateEntryPoint

我们注意到 HuaweiWallet TA 没有调用这两个函数。因此,我们相信任何 APK 都可以与此 TA 对话。

堆栈缓冲区溢出GetCardALLByIndexV2

函数中存在堆栈缓冲区溢出,该溢出是从命令 (ID #0x20010) 调用的。此缓冲区溢出是函数中多个整数溢出/下溢的结果。GetCardALLByIndexV2CmdWalletGetCardByIndex

unsigned int CmdWalletGetCardByIndex(void *sessionContext, uint32_t paramTypes, TEE_Param params[4]) {
// ...
card_list_t cards;
// ...
GetCardALLByIndexV2(params[3].value.a, &cards);
// ...
}

此函数以整数形式作为参数,表示要复制到其另一个参数中的哪0x10卡片。indexcards

  • 在 ,检查以确保其为正值。[1]index

  • 在 ,将 乘以 0x2300 以计算对文件的读取偏移量。此处可能会发生整数溢出。[2]index

  • 在 ,将 乘以 0x10 以计算请求的最后一张卡的编号。此处可能会发生整数溢出。如果此数字小于 ,则执行一些计算。[3]index+1amount

  • 在 ,要返回的卡数的计算方法是减去 乘以 0x10。减法可能发生整数下溢,乘法时可能发生整数上溢。[4]countamountindex

  • 在 ,要从文件中读取的数据的大小是通过将 乘以 0x230 来计算的。此处可能会发生整数溢出。[5]index

  • 在 ,从堆中分配字节,用零填充,然后用文件的内容填充。[6]buffersize

  • 在 处,对预期大小和读取大小执行检查,如果它们不同,则打印一条消息,但继续执行。[7]

  • 在 ,将 (其所有字节)复制到参数中,如果大于 0x2304,则触发堆栈缓冲区溢出(堆栈帧中的堆栈变量也是如此)。[8]buffersizecardssizecardCmdWalletGetCardByIndex

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 字节。通过改变约束条件,可以获得多个工作值,从而产生不同的尺寸。indexindexsize

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) 调用的函数中存在堆栈缓冲区溢出。genOffPayCodeSeedParamCmdWalletGenPayCodeSeedParam

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 字节缓冲区。genOffPayCodeSeedParamaidLenparams[0].memref.bufferaidLenTEE_MemMoveseed.aidseedpay_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) 调用。decodeCRSCertCmdWalletInitCRSCert

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(因此它完全由用户控制)。certificationReqparams[0].memref.buffer

易受攻击的模式如下:

  • 特定类型的 TLV,此处 ,使用CRS_PUBLIC_KEY_TAG_MODULUS_REMAINDERta_bertlv_grp_find

  • TLV 的值指针是使用ta_bertlv_get_value

  • TLV 的长度是使用 和 uncheck 检索的ta_bertlv_get_length

  • 值指针用作源,长度用作调用中的大小,其中目标是堆栈缓冲区TEE_MemMovebuf

这种易受攻击的模式会导致教科书堆栈缓冲区溢出,并在整个函数中重复多次。

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。decodeCRSCertcertificationReqTA_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处找到的值。acctLenparams[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);
// [...]
}

此值先传递到,然后传递到,不进行任何检查。initForUnionSeedListinitPayCodeHead

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_MemMovehead->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 的最大辅助长度,从而导致缓冲区过度读取。isSamePayCodeSeedaidaidLenaidLen

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) 一直调用的函数中存在堆缓冲区溢出。transferV1ToV2PaycodeCmdWalletGenPayCodeSeedParam

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);
// ...
}
// ...
}

在 中,用户控制从 中提取的值等。将缓冲区与 进行比较,比较的大小是 和 之间的最大值。因此,我们控制控件的比较大小,导致堆缓冲区过度读取 。transferV1ToV2PaycodeaccountLenparams[0].memref.bufferaccountpay_codes.head->datapay_codes.head->acctLenaccountLenpay_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

OOB 访问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_ParaminitForUnionSeedListgenOffPayCodeSeedParam

触发此 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]

OOB 访问CmdWalletSavePayCodeSeed

在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletSavePayCodeSeedsaveOffPayCodeSeed

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->acctLenbuffer->acctOff

  • acct长度和起始价buffer->acctLenbuffer->acctOff

  • signText长度和起始价buffer->signTextLenbuffer->signTextOff

  • signResult长度和起始价buffer->signResultLenbuffer->signResultOff

  • aid长度和起始价buffer->aidLenbuffer->aidOff

  • tw长度和起始价buffer->twLenbuffer->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->twLenacctsignTextsignResultaidtwparams[0].memref.buffercalc_hashverifySignWithRSA

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]

OOB 访问CmdWalletSetPayCodeAuthInfo

在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletSetPayCodeAuthInfosetPayCodeAuthInfo

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->acctLenbuffer->acctOff

  • signResult长度和起始价buffer->signResultLenbuffer->signResultOff

  • tw长度和起始价buffer->twLenbuffer->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->twLenacctsignResulttwparams[0].memref.bufferinitPayCodeHeadinitForUnionSeedList

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]

OOB 访问CmdWalletGetTrafficPayCode

在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletGetTrafficPayCodegetOffPayCode

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->acctLenbuffer->acctOff

  • tw长度和起始价buffer->twLenbuffer->twOff

  • aid长度和起始价buffer->aidLenbuffer->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 ),如前所述。accttwaidparams[0].memref.bufferinitPayCodeHeadinitForUnionSeedList

触发此 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]

OOB 访问CmdWalletGetFinancePayCode

在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletGetFinancePayCodegetOffPayCode

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->acctLenbuffer->acctOff

  • tw长度和起始价buffer->twLenbuffer->twOff

  • aid长度和起始价buffer->aidLenbuffer->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 ),如前所述。accttwaidparams[0].memref.bufferinitPayCodeHeadinitForUnionSeedList

触发此 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]

OOB 访问CmdWalletVerifyPayCodeAuthInfo

在 and 函数中,用户控制的值是从第一个参数输入缓冲区中提取的。CmdWalletVerifyPayCodeAuthInfoverifyPayCodeAuthInfo

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->acctLenbuffer->acctOff

  • AV长度和起始价buffer->AVLenbuffer->AVOff

  • SignRes长度和起始价buffer->SignResLenbuffer->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 ),如前所述。acctAVSignResparams[0].memref.bufferinitPayCodeHeadinitForUnionSeedList

触发此 bug 的概念验证代码会导致对用户控制的地址进行读取访问:0x4eadeeef = 0x70003000 + 0xdeadbeef

OOB 访问SendSetStatusCmd

命令 (ID #0x20023) 和 (ID #0x20008) 调用的函数中存在越界访问。如下图所示,第三个值用作 的索引。但是,对此值没有边界检查,因此可以指定导致 OOB 访问的任意值。SendSetStatusCmdCmdWalletActivateCardByBiometricsIdCmdWalletDisablePosTradeTEE_ParamseReaderHandleList

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 字节,但该函数从未检查输出参数缓冲区是否足够大。CmdWalletGetCardByIndexcardsparams[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);
// [...]
}

由于从不检查 和 值,并且可以指向内存中的任何位置(相对于 ),这会导致后续函数调用中的缓冲区过度读取。例如,后面的 (由 和 调用):signValueLensignResultLenchallengeLensignResultchallengeparams[0].memref.bufferverifySignWithRSAcheckValidOfTransSrvReqcheckValidforActivateCard

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

  • 的长度和起始时间aidaid_len = inbuf[3]inbuf + 0x20

  • 的长度和起始时间signValuesignValueLen = inbuf[4]aid + aid_len + 1

  • 的长度和起始时间signResultsignResultLen = 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 访问。例如,后面的 (由 和 调用),如前所述。signValueLensignResultLensignResultparams[0].memref.bufferverifySignWithRSAcheckValidOfTransSrvReqcheckValidforActivateCard

触发此 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

  • 的长度和起始时间signValuesignValueLen = inbuf[4]inbuf + 0x20

  • 的长度和起始时间signResultsignResultLen = 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 访问。例如,后面的 (由 和 调用),如前所述。signValueLensignResultLensignResultparams[0].memref.bufferverifySignWithRSAcheckValidOfTransSrvReqcheckValidforActivateCard

触发此 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

  • ()


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247490626&idx=1&sn=8257610f0a8ed33e0723423c768e30bd&chksm=c06e0679a1dda8b4ebb85411174d1017b8b8e5d71cb7bf992faf9473e5d7bc297736368570c8&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh