此通报包含有关以下漏洞的信息:
- CVE-2021-46813 漏洞 GetOCSPResponse 中缺少长度检查
- CVE-2021-46813 漏洞 NOVEL_CHDRM_Copyordecrypt中缺少长度和偏移检查
- CVE-2021-46813 漏洞 NOVEL_CHDRM_SetDRMCertData中缺少长度检查
- CVE-2021-40062 漏洞 缺少长度检查DRM_Secure_Store_Read
- CVE-2021-40056 漏洞 getvaluewithtypeandindex 中缺少长度检查
- CVE-2021-40057 漏洞 Secure_Store_EncryptWrite 和 Secure_Store_PlainWrite 中缺少长度检查
- CVE-2021-40058 漏洞 NOVEL_CHDRM_SetRegisterResData中缺少长度检查
- CVE-2021-40060 漏洞 调用NOVEL_CHDRMw_MemCompare时长度检查缺失/错误
- CVE-2021-46813 漏洞 find_tlv_data中的整数下溢
- CVE-2022-39003 漏洞 getvaluewithtypeandindex 中的 OOB 访问
- HWPSIRT-2022-77114 未经检查的 Malloc 返回值
- HWPSIRT-2021-84851 缺少长度检入pack_tlv_data
- HWPSIRT-2021-40855 调用unpack_tlv_data后缺少长度检查
- HWPSIRT-2021-36582 堆栈/堆/BSS 指针泄漏DRM_AES_Encrypt_xxx
- HWPSIRT-2021-78954 unpack_tlv_data中的整数下溢
我们发现多个漏洞影响华为TASK_PHONE_NOVELCHD可信应用程序。为了使报告简明扼要,我们尝试对类似的漏洞进行重新组合。我们最终得到了下面给出的类别。
- 缺少长度检查导致TEE_Param输出缓冲区的 39 个缓冲区溢出
pack_tlv_data
- 缺少长度检查导致 25 个缓冲区溢出(23 个堆栈分配,2 个堆分配)
DRM_Secure_Store_Read
- 缺少长度检查,导致 13 个堆栈缓冲区溢出
getvaluewithtypeandindex
- 缺少长度检查导致 14 个堆栈缓冲区溢出
GetOCSPResponse
- 缺少长度签入并导致 12 个堆栈缓冲区溢出
Secure_Store_EncryptWrite
Secure_Store_PlainWrite
- 缺少长度和偏移检查,导致:
NOVEL_CHDRM_Copyordecrypt
- 1 堆栈缓冲区溢出
- 6 次 ION 缓冲液 OOB 写入
- 3 个 ION 缓冲液 OOB 读数
- 缺少长度检查导致 2 个堆栈缓冲区溢出
NOVEL_CHDRM_SetDRMCertData
- 缺少长度检查导致:
NOVEL_CHDRM_SetRegisterResData
- 1 堆栈缓冲区溢出
- 1 BSS 缓冲区溢出
- 调用后缺少长度检查,导致 6 堆缓冲区溢出
unpack_tlv_data
- 堆栈/堆/bss 指针泄漏
DRM_AES_Encrypt_xxx
- 整数下溢导致 OOB 读取/缓冲区过度读取
unpack_tlv_data
- 整数下溢导致 OOB 读取/缓冲区过度读取
find_tlv_data
- 未经检查的 malloc 返回值导致 10 个空指针取消引用
缺少长度签入pack_tlv_data
¶
该函数用于将 TLV 数据打包到输出缓冲区中。输出缓冲区的大小由用户控制,在写入之前从不检查,如果缓冲区不够大,无法写入其中的数据,则会导致缓冲区溢出。pack_tlv_data
void
pack_tlv_data
(
uint8_t
type
,
uint8_t
*
value
,
uint32_t
length
,
uint8_t
*
outbuf
,
uint32_t
*
outbuf_size
)
{
outbuf
[
0
]
=
type
;
*
(
uint32_t
*
)(
outbuf
+
1
)
=
bswap32
(
length
);
if
(
length
)
NOVEL_CHDRMw_Memcpy
(
outbuf
+
5
,
value
,
length
);
*
outbuf_size
=
length
+
5
;
}
例如,我们将查看对 in 的易受攻击的调用,这是命令 ID #0x1 的处理程序。pack_tlv_data
NOVEL_CHDRM_GetDeviceID
TEE_Result
TA_InvokeCommandEntryPoint
(
void
*
sessionContext
,
uint32_t
commandID
,
uint32_t
paramTypes
,
TEE_Param
params
[
4
])
{
/* [...] */
if
(
commandID
==
1
)
{
DRM_CheckParamType
(
paramTypes
,
5
,
6
,
0
,
0
);
/* [...] */
NOVEL_CHDRM_GetDeviceID
(
params
[
1
].
memref
.
buffer
,
&
params
[
1
].
memref
.
size
);
}
/* [...] */
}
NOVEL_CHDRM_GetDeviceID
传递输出缓冲区及其大小,而无需事先验证。无论其实际大小如何,这都会写入 0x20 个字节。例如,如果我们提供大小为 0x8 字节的输出缓冲区,则会导致 0x18 字节的缓冲区溢出。TEE_Param
obuf1_addr
obuf1_size
pack_tlv_data
obuf1_addr
TEE_Param
int
NOVEL_CHDRM_GetDeviceID
(
uint8_t
*
obuf1_addr
,
uint32_t
*
obuf1_size
)
{
pack_tlv_data
(
END_OF_CONTENT
,
&
OTPChipIDHex
,
0x20
,
obuf1_addr
,
obuf1_size
);
return
0
;
}
在这个特定示例中,TA 不会崩溃。但是有些调用具有可控大小,然后能够跨越页面边界,导致崩溃。pack_tlv_data
我们确定了 39 个易受攻击的调用,所有调用都写入了输出缓冲区的 OOB:pack_tlv_data
TEE_Param
地址 | 访客 | 冲击 |
---|---|---|
0x1420 | NOVEL_CHDRM_GetDeviceID+20 | 0x20 字节缓冲区溢出params[1] |
0x1540 | NOVEL_CHDRM_GetSerialNumber+10c | n 字节缓冲区溢出params[1] |
0x1638 | NOVEL_CHDRM_GetSecurityReqData+公元前 | 0x10 字节缓冲区溢出params[1] |
0x165c | NOVEL_CHDRM_GetSecurityReqData+E0 | 0x20 字节缓冲区溢出params[1] |
0x169c | NOVEL_CHDRM_GetSecurityReqData+120 | 4 字节缓冲区溢出params[1] |
0x1740 | NOVEL_CHDRM_GetSecurityReqData+1C4 | n 字节缓冲区溢出params[1] |
0x1774 | NOVEL_CHDRM_GetSecurityReqData+1F8 | n 字节缓冲区溢出params[1] |
0x199c | NOVEL_CHDRM_GetSecurityReqData+420 | n 字节缓冲区溢出params[1] |
0x1a44 | NOVEL_CHDRM_GetSecurityReqData+4C8 | n 字节缓冲区溢出params[1] |
0x1d30 | NOVEL_CHDRM_GetSignature+22c | n 字节缓冲区溢出params[1] |
0x2004 | NOVEL_CHDRM_GetAttestationSignature+244 | n 字节缓冲区溢出params[1] |
0x2828 | NOVEL_CHDRM_GetDRMTime+40 | 4 字节缓冲区溢出params[1] |
0x28d0 | NOVEL_CHDRM_GetTAVersion+90 | n 字节缓冲区溢出params[1] |
0x2f90 | NOVEL_CHDRM_GetLicenseReqData+66 8 | 1 字节缓冲区溢出params[1] |
0x30ec | NOVEL_CHDRM_GetLicenseReqData+7C4 | 0x14 字节缓冲区溢出params[1] |
0x3154 | NOVEL_CHDRM_GetLicenseReqData+82摄氏度 | 0x10 字节缓冲区溢出params[1] |
0x3184 | NOVEL_CHDRM_GetLicenseReqData+85摄氏度 | 4 字节缓冲区溢出params[1] |
0x31bc | NOVEL_CHDRM_GetLicenseReqData+894 | n 字节缓冲区溢出params[1] |
0x31e8 | NOVEL_CHDRM_GetLicenseReqData+8C0 | 0x20 字节缓冲区溢出params[1] |
0x35ac | NOVEL_CHDRM_DelLicenseReqData+280 | 1 字节缓冲区溢出params[1] |
0x5134 | NOVEL_CHDRM_SetDRMDataLicenseResData+1ae0 | 1 字节缓冲区溢出params[1] |
0x5ff8 | NOVEL_CHDRM_GetDRMCertData+234 | n 字节缓冲区溢出params[1] |
0x6110 | NOVEL_CHDRM_GetDRMCertData+34C | n 字节缓冲区溢出params[1] |
0x6220 | NOVEL_CHDRM_GetDRMCertData+45摄氏度 | n 字节缓冲区溢出params[1] |
0x6348 | NOVEL_CHDRM_GetDRMCertData+584 | n 字节缓冲区溢出params[1] |
0x644c | NOVEL_CHDRM_GetDRMCertData+688 | n 字节缓冲区溢出params[1] |
0x6554 | NOVEL_CHDRM_GetDRMCertData+790 | n 字节缓冲区溢出params[1] |
0x6e30 | NOVEL_CHDRM_GetRegisterReqData+13c | 1 字节缓冲区溢出params[1] |
0x6ebc | NOVEL_CHDRM_GetRegisterReqData+1C8 | n 字节缓冲区溢出params[1] |
0x7028 | NOVEL_CHDRM_GetRegisterReqData+334 | 2 字节缓冲区溢出params[1] |
0x70f8 | NOVEL_CHDRM_GetRegisterReqData+404 | n 字节缓冲区溢出params[1] |
0x7154 | NOVEL_CHDRM_GetRegisterReqData+460 | 2 字节缓冲区溢出params[1] |
0x724c | NOVEL_CHDRM_GetRegisterReqData+558 | n 字节缓冲区溢出params[1] |
0x7274 | NOVEL_CHDRM_GetRegisterReqData+580 | 20 字节缓冲区溢出params[1] |
0x72d4 | NOVEL_CHDRM_GetRegisterReqData+5e0 | n 字节缓冲区溢出params[1] |
0x7db4 | NOVEL_CHDRM_GetRegisterStatus+124 | n 字节缓冲区溢出params[1] |
0x7df8 | NOVEL_CHDRM_GetRegisterStatus+168 | 1 字节缓冲区溢出params[1] |
0x7f0c | NOVEL_CHDRM_GetRegisterStatus+27c | 1 字节缓冲区溢出params[1] |
0x7fa8 | NOVEL_CHDRM_GetRegisterStatus+318 | 1 字节缓冲区溢出params[1] |
缺少长度签入DRM_Secure_Store_Read
¶
DRM_Secure_Store_Read
使用或 从安全存储中读取数据,具体取决于正在读取的文件。Secure_Store_PlainRead
Secure_Store_EncryptRead
unsigned
int
DRM_Secure_Store_Read
(
void
*
data
,
uint32_t
*
data_len
,
uint32_t
store_type
)
{
if
(
store_type
-
0x10
<=
0x4F
)
{
// drmCipherData
return
Secure_Store_EncryptRead
(
"64726D43697068657244617461.cli"
,
data
,
data_len
,
store_type
,
&
version
);
}
if
(
store_type
-
0x60
<=
0x9F
)
{
// drmPlainData
return
Secure_Store_PlainRead
(
"64726D506C61696E44617461.cli"
,
data
,
data_len
,
store_type
,
&
version
);
}
if
(
store_type
<=
0xF
)
{
// drmCipherData
ret1
=
Secure_Store_EncryptRead
(
"64726D43697068657244617461.cli"
,
data
,
&
outsize
,
store_type
,
&
version
);
*
data_len
=
outsize
;
// drmPlainData
ret2
=
Secure_Store_PlainRead
(
"64726D506C61696E44617461.cli"
,
data
+
outsize
,
&
outsize
,
store_type
,
&
version
);
*
data_len
+=
outsize
;
return
ret1
|
ret2
;
}
// [...]
}
这里将:Secure_Store_EncryptRead
- 读取文件
filename
; - 解密其中的数据;
- 根据值检索 TLV 对象
store_type
; - 将结果复制到输出缓冲区中。
data
unsigned
int
Secure_Store_EncryptRead
(
uint8_t
*
filename
,
void
*
data
,
uint32_t
*
outsize
,
uint8_t
store_type
,
uint32_t
*
version
)
{
/* [...] */
// Reads the file from the file
HiTEE_FlashRead
(
filename
,
&
rpmb_data
,
0x2000
,
&
rpmb_data_size
);
rpmb_data_size
=
_byteswap_ulong
(
rpmb_data
.
size
);
/* [...] */
// In-place RPMB data decryption.
Secure_Store_DataDecrypt
(
rpmb_data
.
data
,
&
rpmb_data_size
);
/* [...] */
// Extracts data from the file according to the `store_type` argument.
unpack_tlv_data
(
store_type
,
rpmb_data
.
data
,
&
rpmb_data_size
,
&
unpacked_tlv_buf_p
,
&
unpacked_tlv_len
);
/* [...] */
// Copies the result into the `data` output buffer provided to the function.
NOVEL_CHDRMw_Memcpy
(
data
,
unpacked_tlv_buf_p
,
unpacked_tlv_len
);
/* [...] */
}
注意:除了解密过程外,执行相同的操作。Secure_Store_PlainRead
输出缓冲区首先作为参数传递给 ,然后传播到 和/或 。但是,从不检查其大小,这可能会导致在将数据复制到其中时缓冲区溢出。data
DRM_Secure_Store_Read
Secure_Store_PlainRead
Secure_Store_EncryptRead
例如,我们将查看对 in 的易受攻击的调用,这是命令 ID #0x17 的处理程序。DRM_Secure_Store_Read
NOVEL_CHDRM_GetSerialNumber
TEE_Result
TA_InvokeCommandEntryPoint
(
void
*
sessionContext
,
uint32_t
commandID
,
uint32_t
paramTypes
,
TEE_Param
params
[
4
])
{
/* [...] */
if
(
commandID
==
0x17
)
{
DRM_CheckParamType
(
paramTypes
,
5
,
6
,
0
,
0
);
/* [...] */
NOVEL_CHDRM_GetSerialNumber
(
params
[
1
].
memref
.
buffer
,
&
params
[
1
].
memref
.
size
);
}
/* [...] */
}
NOVEL_CHDRM_GetSerialNumber
从安全存储中读取类型为 0x60 的文件。读取的数据将写入大小为 2048 的堆栈分配缓冲区。DRM_Secure_Store_Read
int
NOVEL_CHDRM_GetSerialNumber
(
uint8_t
*
obuf1_addr
,
uint32_t
obuf1_size
)
{
/* [...] */
uint8_t
cert
[
2048
];
memset
(
cert
,
0
,
sizeof
(
cert
));
/* [...] */
DRM_Secure_Store_Read
(
cert
,
&
cert_size
,
0x60
);
/* [...] */
}
但是,用户可以调用以将与类型 0x60 对应的值写入安全存储。文件数据和大小来自对输入缓冲区(由用户控制)进行操作的调用。因此,应该可以写入超过 2048 个字节,从而在使用 读取此数据时导致缓冲区溢出。NOVEL_CHDRM_SetDRMCertData
DRM_Secure_Store_Write
unpack_tlv_data
TEE_Param
DRM_Secure_Store_Read
int
NOVEL_CHDRM_SetDRMCertData
(
uint8_t
*
ibuf0_addr
,
uint32_t
ibuf0_size
)
{
/* [...] */
if
(
!
unpack_tlv_data
(
0x16
,
ibuf0_addr
,
&
ibuf0_size
,
&
cert
,
&
cert_size
))
{
DRM_Secure_Store_Write
(
cert
,
cert_size
,
0x60
);
/* [...] */
}
/* [...] */
}
我们确定了 25 个易受攻击的调用,所有这些调用都可能导致缓冲区溢出:DRM_Secure_Store_Read
地址 | 访客 | 冲击 |
---|---|---|
0x149c | NOVEL_CHDRM_GetSerialNumber+68 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x16c8 | NOVEL_CHDRM_GetSecurityReqData+14c | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x16e4 | NOVEL_CHDRM_GetSecurityReqData+168 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x17b4 | NOVEL_CHDRM_GetSecurityReqData+238 | 基于堆栈的缓冲区溢出(如果大小为 4>) |
0x19e8 | NOVEL_CHDRM_GetSecurityReqData+46c | 基于堆栈的缓冲区溢出(如果大小为 8,则>) |
0x1bb0 | NOVEL_CHDRM_GetSignature+交流 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x2344 | NOVEL_CHDRM_VerifySignature+2b8 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x2fe0 | NOVEL_CHDRM_GetLicenseReqData+6b8 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x303c | NOVEL_CHDRM_GetLicenseReqData+714 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x3974 | NOVEL_CHDRM_SetDRMDataLicenseResData+320 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x53c4 | NOVEL_CHDRM_SetDRMCertData+1直流 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x546c | NOVEL_CHDRM_SetDRMCertData+284 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x5f9c | NOVEL_CHDRM_GetDRMCertData+1天8 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x60b0 | NOVEL_CHDRM_GetDRMCertData+2ec | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x61c0 | NOVEL_CHDRM_GetDRMCertData+3FC | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x62d4 | NOVEL_CHDRM_GetDRMCertData+510 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x63f4 | NOVEL_CHDRM_GetDRMCertData+630 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x64f8 | NOVEL_CHDRM_GetDRMCertData+734 | 基于堆的缓冲区溢出(任何大小) |
0x6d98 | NOVEL_CHDRM_GetRegisterReqData+A4 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x6e64 | NOVEL_CHDRM_GetRegisterReqData+170 | 基于堆的缓冲区溢出(如果大小为 256,则>) |
0x7cf4 | NOVEL_CHDRM_GetRegisterStatus+64 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x7d64 | NOVEL_CHDRM_GetRegisterStatus+D4 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x7e28 | NOVEL_CHDRM_GetRegisterStatus+198 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x7e90 | NOVEL_CHDRM_GetRegisterStatus+200 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x7f3c | NOVEL_CHDRM_GetRegisterStatus+2交流 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
缺少长度签入getvaluewithtypeandindex
¶
getvaluewithtypeandindex
分析输入缓冲区以提取具有给定类型和键类型的值。它首先在输入缓冲区内定位值,计算其偏移量和长度。然后,使用 将该值复制到输出缓冲区中。值长度受输入缓冲区大小的限制。从不检查输出缓冲区长度,从而导致潜在的缓冲区溢出。inbuf
NOVEL_CHDRMw_Memcpy
insize
uint32_t
getvaluewithtypeandindex
(
uint32_t
type
,
uint32_t
key_type
,
void
*
inbuf
,
uint32_t
insize
,
void
*
outbuf
,
uint32_t
*
outsize_p
)
{
/* [...] */
/*
* Parses the input buffer `inbuf` to compute `value_size` and `value_offset`.
* A value is then extracted from `inbuf` and written into `outbuf`.
*/
if
(
insize
>=
value_size
+
value_offset
)
{
NOVEL_CHDRMw_Memcpy
(
outbuf
,
inbuf
+
value_offset
,
value_size
);
*
outsize_p
=
value_size
;
return
0
;
}
/* [...] */
}
例如,我们将查看对 in 的易受攻击的调用,这是命令 ID #0xE 的处理程序。getvaluewithtypeandindex
NOVEL_CHDRM_SetDRMDataLicenseResData
在 中,首先从 中提取缓冲区(大小不受限制)并将其复制到 中。然后,此缓冲区用作对 的调用的输入。此调用的输出是大小为 4 的堆栈变量。如上所述,可以将其超过 4 个字节复制到其输出中,从而导致缓冲区溢出。NOVEL_CHDRM_SetDRMDataLicenseResData
ibuf0_addr
data1_p
getvaluewithtypeandindex
OutputProtection
getvaluewithtypeandindex
unsigned
int
NOVEL_CHDRM_SetDRMDataLicenseResData
(
uint8_t
*
ibuf0_addr
,
uint32_t
ibuf0_size
,
uint8_t
*
obuf1_addr
,
uint32_t
*
obuf1_size
)
{
/* [...] */
unpack_tlv_data
(
0xD
,
ibuf0_addr
,
&
ibuf0_size
,
&
data1_p
,
&
data1_size
);
/* [...] */
OutputProtection
=
0
;
OutputProtection_size
=
0
;
getvaluewithtypeandindex
(
4
,
6
,
data1_p
,
data1_size
,
&
OutputProtection
,
&
OutputProtection_size
);
/* [...] */
我们确定了 13 个易受攻击的调用,所有这些调用都可能导致堆栈缓冲区溢出:getvaluewithtypeandindex
地址 | 访客 | 冲击 |
---|---|---|
0x3aa4 | NOVEL_CHDRM_SetDRMDataLicenseResData+450 | 基于堆栈的缓冲区溢出(如果大小为 512,则>) |
0x3b14 | NOVEL_CHDRM_SetDRMDataLicenseResData+4c0 | 基于堆栈的缓冲区溢出(如果大小为 2048,则> |
0x3b84 | NOVEL_CHDRM_SetDRMDataLicenseResData+530 | 基于堆栈的缓冲区溢出(如果大小为 128,则>) |
0x3c3c | NOVEL_CHDRM_SetDRMDataLicenseResData+5e8 | 基于堆栈的缓冲区溢出(如果大小为 128,则>) |
0x3f74 | NOVEL_CHDRM_SetDRMDataLicenseResData+920 | 基于堆栈的缓冲区溢出(如果大小为 > 256) |
0x4188 | NOVEL_CHDRM_SetDRMDataLicenseResData+B34 | 基于堆栈的缓冲区溢出(如果大小为 32,则>) |
0x42c8 | NOVEL_CHDRM_SetDRMDataLicenseResData+C74 | 基于堆栈的缓冲区溢出(如果大小为 32,则>) |
0x443c | NOVEL_CHDRM_SetDRMDataLicenseResData+DE8 | 基于堆栈的缓冲区溢出(如果大小为 32,则>) |
0x4570 | NOVEL_CHDRM_SetDRMDataLicenseResData+F1C | 基于堆栈的缓冲区溢出(如果大小为 32,则>) |
0x48e0 | NOVEL_CHDRM_SetDRMDataLicenseResData+128C | 基于堆栈的缓冲区溢出(如果大小为 32,则>) |
0x4a1c | NOVEL_CHDRM_SetDRMDataLicenseResData+13C8 | 基于堆栈的缓冲区溢出(如果大小为 4>) |
0x4ad0 | NOVEL_CHDRM_SetDRMDataLicenseResData+147C | 基于堆栈的缓冲区溢出(如果大小为 > 256) |
0x4be4 | NOVEL_CHDRM_SetDRMDataLicenseResData+1590 | 基于堆栈的缓冲区溢出(如果大小为 16,则>) |
缺少长度签入GetOCSPResponse
¶
NOVEL_CHDRM_SetDRMCertData
是命令 ID #0xF 的处理程序,该处理程序将输入缓冲区及其大小作为参数。它首先调用从输入缓冲区中提取一个值,该值由用户控制,然后再将其传递给函数。TEE_Param
unpack_tlv_data
GetOCSPResponse
int
NOVEL_CHDRM_SetDRMCertData
(
uint8_t
*
ibuf0_addr
,
uint32_t
ibuf0_size
)
{
/* [...] */
ibuf0_size_cpy
=
ibuf0_size
;
rsp_status_t
rsp_status
;
/* [...] */
if
(
!
unpack_tlv_data
(
0x1C
,
ibuf0_addr
,
&
ibuf0_size_cpy
,
&
data_p
,
&
data_size
))
{
GetOCSPResponse
(
data_p
,
data_size
,
&
rsp_status
);
/* [...] */
}
/* [...] */
}
GetOCSPResponse
解析位于输入缓冲区中的 ASN.1 数据。在此函数中,有许多以下模式的实例:data_p
- 一个调用,它递增数据指针并检索当前标记的长度(无界)
asn1_get_tag
- 调用 that 将标记数据从输入缓冲区复制到输出缓冲区
NOVEL_CHDRMw_Memcpy
rsp_status_p
int
GetOCSPResponse
(
uint8_t
*
data_p
,
uint32_t
data_size
,
rsp_status_t
*
rsp_status
)
{
/* [...] */
asn1_get_tag
(
&
data_p
,
seq0_end
,
&
length
,
OID
);
/* [...] */
NOVEL_CHDRMw_Memcpy
(
&
rsp_status
->
field_14
,
data_p
,
length
);
data_p
+=
length
;
/* [...] */
}
由于标记的长度是在没有任何检查的情况下给出的,这将导致缓冲区溢出(这是堆栈分配的缓冲区)。我们确定了 14 个易受攻击的模式,所有这些模式都会导致堆栈缓冲区溢出:NOVEL_CHDRMw_Memcpy
rsp_status_p
GetOCSPResponse
rsp_status_p
地址 | 访客 | 冲击 |
---|---|---|
0x3a7d0 | GetOCSP响应+1e0 | 基于堆栈的缓冲区溢出 |
0x3a8ec | GetOCSP响应+2fc | 基于堆栈的缓冲区溢出 |
0x3a994 | GetOCSP响应+3a4 | 基于堆栈的缓冲区溢出 |
0x3abc4 | GetOCSP响应+5d4 | 基于堆栈的缓冲区溢出 |
0x3ac84 | GetOCSP响应+694 | 基于堆栈的缓冲区溢出 |
0x3acec | GetOCSP响应+6fc | 基于堆栈的缓冲区溢出 |
0x3ad54 | GetOCSP响应+764 | 基于堆栈的缓冲区溢出 |
0x3ae18 | GetOCSP响应+828 | 基于堆栈的缓冲区溢出 |
0x3aee0 | GetOCSP响应+8f0 | 基于堆栈的缓冲区溢出 |
0x3b084 | GetOCSP响应+a94 | 基于堆栈的缓冲区溢出 |
0x3b138 | GetOCSP响应+b48 | 基于堆栈的缓冲区溢出 |
0x3b1f8 | GetOCSPResponse+c08 | 基于堆栈的缓冲区溢出 |
0x3b2bc | GetOCSP响应+ccc | 基于堆栈的缓冲区溢出 |
0x3b338 | GetOCSP响应+d48 | 基于堆栈的缓冲区溢出 |
缺少长度签入和Secure_Store_EncryptWrite
Secure_Store_PlainWrite
¶
Secure_Store_EncryptWrite
,可以使用用户控制的 和 参数 via 调用,首先作为 TLV 对象打包到大小为 + 5 的堆分配缓冲区中。然后,它将文件的 0x2000 字节读入堆栈分配的结构中。然后,此函数多次表现出相同的易受攻击模式,即调用:DRM_Secure_Store_Write
buffer
buffer_size
buffer
buffer_tlv
buffer_size
rpmb_data
NOVEL_CHDRMw_Memcpy
rpmb_data.data
作为目的地;buffer_tlv
作为来源;buffer_tlv_size
作为长度。
TEE_Result
Secure_Store_EncryptWrite
(
char
*
filename
,
void
*
buffer
,
uint32_t
buffer_size
,
uint32_t
store_type
,
uint32_t
a5
)
{
/* [...] */
rpmb_data_t
rpmb_data
;
/* [...] */
buffer_tlv
=
NOVEL_CHDRMw_Malloc
(
buffer_size
+
5
);
pack_tlv_data
(
store_type
,
buffer
,
buffer_size
,
buffer_tlv
,
&
buffer_tlv_size
);
ret
=
HiTEE_FlashRead
(
filename
,
&
rpmb_data
,
0x2000
,
&
rpmb_data_size
);
if
(
ret
==
0xFFFF7106
)
{
/* [...] */
NOVEL_CHDRMw_Memcpy
(
&
rpmb_data
.
data
[
secure_store_len
],
buffer_tlv
,
buffer_tlv_size
);
secure_store_len
+=
buffer_tlv_size
;
}
/* [...] */
}
由于大小为 0x2000,并且由用户控制,因此会发生堆栈缓冲区溢出。rpmb_data
buffer_tlv_size
注意:包含与 相同的易受攻击的模式。这两个函数的调用方式与DRM_Secure_Store_Read
中的缺失长度检查部分中所述相同。Secure_Store_PlainWrite
Secure_Store_EncryptWrite
总而言之,我们确定了 12 个易受攻击的模式实例,这些实例会导致堆栈缓冲区溢出:
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论