CVE-2021-39994 漏洞CVE-2021-39994 SMC SE 工厂检查 OOB 访问
如果你是想谈业务合作,直接翻到底联系作者。
不闲聊,直接说重点,我能不能做,你有没有预算,相关先介绍(我介绍技术、或者方案产品,你介绍需求带预算)就完事。
云桌面开发相关: 虚拟化(usb、usb透传、显示器、网卡、磁盘、声卡、摄像头)模块。
安全产品(dlp/edr/沙箱)开发相关:文件单/双缓冲透明加解密、网络防火防墙、所有通用外设管控(用户层版/驱动层版)模块、其它管理(恶意进程、模块等)。
通用开发相关:注入、hook、产品方案编写与设计、非黑灰产逆向、具体单独小功能编写。
如果你想系统学习二进制漏洞技能,往最后翻,或者直接翻到底联系作者。
在之前一篇关于华为安全管理程序内部的博客文章中,我们详细介绍了Android供应商引入新技术以增强其设备安全性的动机。我们解释说,安全管理程序可以通过利用 ARMv8-A 架构的虚拟化扩展来监视 Android 内核。特别是,这些扩展引入了 EL2,这是一个比 EL1 的内核和 EL0 的用户空间更具特权的异常级别。
ARM TrustZone 是这些安全功能中的另一个。它由系统范围和硬件强制分离组成。所有软件和硬件都被划分为不受信任的正常世界和受信任的安全世界。由于禁止非安全软件访问安全资源,因此两个世界之间的所有通信都需要通过专用组件,即安全监视器,该组件可以使用安全监视器调用 (SMC) 指令进行调用。
安全监控器在 EL3 上运行,这是 TrustZone 引入的新的最高权限级别。它对设备具有完全控制权,并且是唯一允许执行某些特权操作的组件,特别是:
它管理关键外围设备,例如加密引擎、电子保险丝和 RPMB 分区;
它充当正常环境和安全环境之间的桥梁,在 Android 内核和受信任的操作系统之间转发请求。
与三星类似,华为选择将其安全监控实现基于ARM可信固件(ATF),这是ARM提供的开源参考实现。第三方功能可以通过 EL3 运行时服务框架轻松集成到 ATF 中,华为使用该框架实现额外的 SMC 处理程序组。在我们的安全评估中,我们主要关注这些自定义 SMC 处理程序,因为它们受到的审查少于开源处理程序。
虽然我们本可以深入探讨运行时服务的主题,但 Fernand Lone Sang 在他的“逆向工程三星 S6 SBOOT”博客文章中给出的解释仍然具有相关性。我们邀请读者阅读第一部分的“服务描述符”部分和第二部分的“运行时服务初始化”部分。
本文的其余部分描述了我们在华为安全监控实施中发现的两个漏洞。
安全元件(SE),称为HISEE,是海思设备的关键外设。因此,它只能被安全世界访问,因此安全监视器需要充当某种直通,让内核与它交互。
在内核方面,大多数通信都是由使用 SMC (0xC5000020) 中的“hisee”驱动程序执行的。此 SMC 定义了多个命令,这些命令对应于下面的枚举。drivers/hisi/hisee/hisee.c
HISEE_FN_MAIN_SERVICE_CMD
se_smc_cmd
▸ drivers/hisi/hisee/hisee.h
enum se_smc_cmd {
// ...
CMD_WRITE_RPMB_KEY,
CMD_SET_LCS_SM,
CMD_SET_STATE,
CMD_GET_STATE,
CMD_APDU_RAWDATA,
CMD_FACTORY_APDU_TEST,
CMD_HISEE_CHANNEL_TEST,
CMD_HISEE_VERIFY_KEY,
CMD_HISEE_WRITE_CASD_KEY,
// ...
};
这些命令使用 send_smc_process
功能发送到安全监视器。
▸ drivers/hisi/hisee/hisee.c
int send_smc_process(const struct atf_message_header *p_message_header,
phys_addr_t phy_addr, unsigned int size,
unsigned int timeout, enum se_smc_cmd smc_cmd)
{
// ...
ret = atfd_hisee_smc((u64)HISEE_FN_MAIN_SERVICE_CMD,
(u64)smc_cmd, (u64)phy_addr, (u64)size);
// ...
}
举个例子,让我们看看内核如何使用 write_apdu_command_func
函数发送原始 APDU 命令。如果尚未分配大小为 4096 字节的共享内存缓冲区,则此函数通过调用 来分配它。然后,它设置位于此共享缓冲区开头的 16 字节结构的字段,最后在其之后复制 APDU 命令字节。dma_alloc_coherent
atf_message_header
▸ drivers/hisi/hisee/hisee.c
static int write_apdu_command_func(const char *apdu_buf, unsigned int apdu_len)
{
// ...
if (!hisee_data_ptr->apdu_command_buff_virt)
hisee_data_ptr->apdu_command_buff_virt =
(void *)dma_alloc_coherent(hisee_data_ptr->cma_device,
SIZE_4K,
&hisee_data_ptr->apdu_command_buff_phy,
GFP_KERNEL);
// ...
p_message_header = (struct atf_message_header *)(uintptr_t)hisee_data_ptr->apdu_command_buff_virt;
set_message_header(p_message_header, CMD_APDU_RAWDATA);
// ...
apdu_len = (apdu_len > HISEE_APDU_DATA_LEN_MAX) ?
HISEE_APDU_DATA_LEN_MAX : apdu_len;
ret = memcpy_s(hisee_data_ptr->apdu_command_buff_virt + HISEE_ATF_MESSAGE_HEADER_LEN,
SIZE_4K - HISEE_ATF_MESSAGE_HEADER_LEN,
(void *)apdu_buf, (size_t)apdu_len);
// ...
image_size = HISEE_ATF_MESSAGE_HEADER_LEN + apdu_len;
p_message_header->test_result_phy =
(u32)(hisee_data_ptr->apdu_command_buff_phy + SIZE_2K);
p_message_header->test_result_size = (unsigned int)SIZE_1K;
ret = send_smc_process(p_message_header,
hisee_data_ptr->apdu_command_buff_phy, image_size,
HISEE_ATF_GENERAL_TIMEOUT, CMD_APDU_RAWDATA);
// ...
ret = memcpy_s(hisee_data_ptr->apdu_ack.ack_buf,
HISEE_APDU_DATA_LEN_MAX + 1,
(hisee_data_ptr->apdu_command_buff_virt + SIZE_2K),
(size_t)p_message_header->test_result_size);
// ...
hisee_data_ptr->apdu_ack.ack_len = p_message_header->test_result_size;
// ...
}
标头结构包含 SMC 的命令、返回代码以及安全监视器应复制响应数据的缓冲区的物理地址和大小。在 write_apdu_command_func
中,我们可以看到,响应数据(最大 1024 字节)应复制到共享缓冲区本身的偏移量 2048 处。atf_message_header
HISEE_FN_MAIN_SERVICE_CMD
▸ drivers/hisi/hisee/hisee.h
/* message header between kernel and atf */
struct atf_message_header {
/*
* atf cmd execute type, such as otp, cos, sloader at all,
* kernel set and atf read it
*/
unsigned int cmd;
/*
* atf cmd execute result indication, use a magic value to
* indicate success, atf set it and check in kernel
*/
unsigned int ack;
/* tell atf store the result to this buffer when doing channel test */
unsigned int test_result_phy;
/* tell atf the size of buffer when doing channel test */
unsigned int test_result_size;
};
在安全监控端,SMC 由 hisee_smc_handler
功能处理。如果命令使用共享内存缓冲区作为参数(如 write_apdu_command_func
调用的命令),则在将该命令分派给实际处理程序之前,应由 se_smc_addr_check
函数检查其物理地址和大小。HISEE_FN_MAIN_SERVICE_CMD
CMD_APDU_RAWDATA
uintptr_t hisee_smc_handler(
uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags) {
// Do the arguments (shared memory buffer address and size) need to be checked?
if (x1 - 0xb > 0x35 || ((1 << (x1 - 0xb)) & 0x2003e000002041) == 0) {
// Ensure the shared memory buffer is in the CMA region.
if (!se_smc_addr_check(x2, x3)) {
debug_print("\nse smc addr error\n");
goto ERROR;
}
// Ensure it is bigger than the size of the header structure.
if (x3 < 0x10) {
debug_print("\nse smc size error\n");
goto ERROR;
}
}
// Find the corresponding command handler and call it.
for (i = 0; i != 25; ++i) {
if (x1 == se_smc_cmd_handlers[i].cmd) {
handler_t handler = se_smc_cmd_handlers[i].handler;
if (handler)
return handler(x1, x2, x3, handle);
}
}
debug_print("\n%s: unknown cmd %x\n", "hisee_smc_handler", x1);
}
se_smc_addr_check
函数可确保共享内存缓冲区完全位于 CMA 区域 (0x40000000-0x50000000) 中。正如我们刚才所看到的,它是从 hisee_smc_handler
调用的,但在需要时也可以直接从 SMC 处理程序调用。
uint64_t se_smc_addr_check(uint64_t addr, uint64_t size) {
// Retrieve (once) the CMA region base address and size.
if (!cma_info_read) {
if (get_cma_info(&g_cma_addr, &g_cma_size)) {
debug_print("\nget cma info fail\n\r");
return 0;
}
cma_info_read = 1;
} // Ensure address and address+size are within this region.
if (g_cma_addr <= addr && addr <= addr + size)
return addr + size <= g_cma_addr + g_cma_size;
return 0;
}
未检查参数并因此不应使用的命令列表如下:
编号 | 名字 |
---|---|
0x0B | CMD_GET_STATE |
0x11 | CMD_HISEE_FACTORY_CHECK |
0x18 | - |
0x30 | CMD_HISEE_POWER_ON |
0x31 | CMD_HISEE_POWER_OFF |
0x32 | CMD_SMX_PROCESS_STEP1 |
0x33 | CMD_SMX_PROCESS_STEP2 |
0x34 | CMD_SMX_GET_EFUSE |
0x40 | CMD_HISEE_GET_EFUSE_VALUE |
但是,在查看 se_factory_check
时,我们注意到它使用了它的参数。这可以通过分配给 set_message_header
函数的共享内存缓冲区地址和大小来证明。CMD_HISEE_FACTORY_CHECK
uintptr_t se_factory_check(uint64_t cmd, uint64_t addr, uint64_t size, void *handle) {
int32_t ret;
char request[32]; if (addr) {
memset_s(request, sizeof(request), 0, sizeof(request));
set_message_header(addr, size);
*(uint32_t *)(request + 0x00) = 0xd7;
*(uint32_t *)(request + 0x04) = 0;
*(uint32_t *)(request + 0x08) = 0x13d8;
*(uint32_t *)(request + 0x0c) = 0;
*(uint32_t *)(request + 0x10) = 0x13d8;
*(uint64_t *)(request + 0x18) = 0x1422bbc0;
ret = se_cmd_mailbox_send(request);
}
// ...
}
set_message_header
将共享内存缓冲区的地址和大小保存到稍后将使用的全局变量中。
void set_message_header(uint64_t addr, uint64_t size) {
g_msg_hdr_addr = addr;
g_msg_hdr_size = size;
}
由于缺少对 se_smc_addr_check
for 的调用,内核可以传递 和 值,以便共享缓冲区位于 CMA 区域之外。具体而言,它可以传递安全监视器地址空间内的地址。CMD_HISEE_FACTORY_CHECK
addr
size
现在我们需要看看这些值在哪里使用,以知道可以做什么。通过查看对 的交叉引用,我们找到了在处理 SE 对请求的回复时所采用的代码路径。当 SE 处理完请求后,安全监视器会在其邮箱中收到回复,并调用相应的处理程序,该处理程序se_chip_test_ack
for 。g_msg_hdr_addr
CMD_HISEE_FACTORY_CHECK
然后,se_chip_test_ack
调用send_ack
,其中包含响应代码和 0xC 字节的响应数据。
uint64_t se_chip_test_ack(char* reply) {
uint32_t code;
uint32_t data[3]; if (reply) {
// Copy the response data into a local buffer.
data[0] = *(uint32_t *)(reply + 0x4);
data[1] = *(uint32_t *)(reply + 0xc);
data[2] = *(uint32_t *)(reply + 0x10);
// Convert the status word into a response code.
if (reply[1] == 0xa3) {
code = 0xccaa;
} else {
debug_print("\nhisee test result %x %x %x!\n",
data[0], data[1], data[2]);
code = 0xcc55;
}
// Send the response and its data to the kernel.
send_ack(code | 0xaabb0000, data, 0xc);
return 0;
} else {
debug_print("\n%s: para error!\n", "se_chip_test_ack");
return 0xFFFFFFFF;
}
}
send_ack
执行以下操作:
它将标头的字段设置为响应代码;ack
它将其字段设置为test_result_size
MIN(g_msg_hdr_size, data_size)
;
它将响应数据(如果有)复制到 ,前提是内核提供的缓冲区传递se_smc_addr_check
。test_result_phy
void send_ack(uint32_t code, uint64_t data_addr, uint32_t data_size)
{
uin32_t resp_buf;
uin32_t resp_buf_len; // Get the shared memory buffer saved in g_msg_hdr_addr.
message_header = (struct atf_message_header*)g_msg_hdr_addr;
if (g_msg_hdr_addr) {
// Set the return code.
message_header->ack = code;
// Get the response buffer's address and size.
resp_buf = message_header->test_result_phy;
resp_buf_len = MIN(g_msg_hdr_size, data_size);
message_header->test_result_size = resp_buf_len;
// If there is any response data, copy it to the response buffer if the check of se_smc_addr_check passes.
if (data_addr && resp_buf && se_smc_addr_check(resp_buf, resp_buf_len)
&& memcpy_s(resp_buf, resp_buf_len, data_addr, resp_buf_len)) {
debug_print("\n%s: memcpy err\n", "send_ack");
} else {
arm_gic_raise_softirq(8);
}
} else {
debug_print("\n%s: null pointer err\n", "send_ack");
}
}
因此,如果我们在安全监视器的地址空间内设置一个地址,它将在 at 和(在调用函数中始终介于 0x0 和 0xC 之间的值)在 。g_msg_hdr_addr
X
code
0xAABBCC55
X+0x4
data_size
X+0xC
我们将在漏洞利用部分看到如何使用此漏洞来构建漏洞利用,从而从 NS-EL1 在 EL3 中实现可靠的代码执行。
在本节中,我们将描述漏洞利用和我们构建的不同原语,以从内核获取 EL3 中的任意代码执行。
该漏洞利用的第一步是禁用 CMA 白名单,以便对 se_smc_addr_check
的调用始终返回 true。这样,在send_ack
中,我们将能够使用受控的目标地址到达对memcpy_s
的呼叫。
为此,我们触发漏洞两次,一次更改为 0xC(使用值),第二次更改为 0xAABBCC55(使用值)。这会将允许的地址范围从 0x40000000-0x50000000 转换为 0xC-0xAABBCC61,从而可以将安全监视器地址空间中的地址用作memcpy_s
的目标。g_cma_addr
data_size
g_cma_size
code
通过第三次,来自 SE 的响应数据将被复制到我们选择的目标地址:CMD_HISEE_FACTORY_CHECK
7c 02 10 0b 57 57 57 13 b6 01 00 00
此外,还可以通过将memcpy_s
副本设置为小于 0xC 字节的值来使副本小于 字节。请注意,由于签入hisee_smc_handler
,无法直接使用命令参数执行此操作。在我们的漏洞利用中,我们设置为 1,以便只有第一个字节 (0x7c) 被写入我们的任意位置。g_msg_hdr_size
data_size
size < 0x10
g_msg_hdr_size
然后,此原语用于劫持数据部分中存在的许多函数指针之一,以控制安全监视器的执行流。这个想法是找到一个可以从 SMC 轻松访问的功能指针,一旦我们更改其值,它将指向一个有趣的小工具。这在很大程度上取决于安全监视器的版本。作为参考,在我们的测试设备上,它是 .v1.5(debug):ab5a980
我们选择修改运行时服务的 SMC 处理程序指针之一。bl31_secap_svc
ops_t bl31_secap_smc_handlers[] = {
/* 0x14230238: */ {0xca000001, func_14204a1c},
/* 0x14230248: */ {0xca000002, func_14204a14},
/* 0x14230258: */ {0xca000008, func_14205144},
};
我们针对的 SMC 处理程序指针位于地址 0x14230238,并指向位于 0x14204A1C 的函数。我们之所以选择这个,是因为0x14204A7C有一个有趣的小工具,可以让我们调用任意函数:BLR X2
0x14204a7c: ldr x2, [x2,#0xb8]
0x14204a80: cbnz x2, loc_14204aa0
...
0x14204aa0: blr x2
注意:我们将分支到的值来自 dereferencing ,因此我们需要让 X2 指向一些可从安全监视器读取的内存,最好是我们可以从内核控制的内存。例如,我们可以为此重用共享内存缓冲区。X2+0xB8
现在让我们详细介绍一下如何利用这个小工具来构建更好的基元。
我们劫持的函数指针在 (0x14205150) 中使用,它只是 中 SMC 处理程序的包装器。从 NS-EL1 传递的参数(除 之外)将在调用站点移入 -。bl31_secap_handler
bl31_secap_smc_handlers
smc_fid
X0
X3
0x14205264: mov x2, x3
0x14205268: mov x0, x22
0x1420526c: mov x1, x21
0x14205270: mov x3, x4
0x14205274: blr x6 /* address of the blr x2 gadget */
查看调用处理程序(现在是我们的小工具)的程序集,我们可以看到我们控制了以下寄存器:BLR X2
X0
== X22
X1
== X21
X2
X3
== X4
X2
必须包含 at offset 0xB8 的内存地址,即我们在调用该小工具后将跳转到的第二个小工具的地址。现在我们只需要找到一个有趣的小工具,它使用我们控制的任何其他寄存器来增强我们当前的原语。BLR X2
我们将从找到一个任意写入小工具开始。
在像监视器一样大的代码库中,找到一个将值从寄存器写入地址的小工具并不难。但是,我们需要考虑到这样一个事实,即原始 SMC 处理程序调用站点的分支是指令,这意味着我们将丢失寄存器中的原始返回地址。尽管如此,我们可以通过查找具有相同尾声(或至少具有相同堆栈帧大小)的小工具来检索存储在堆栈帧中的返回地址。这样,我们就可以干净地返回到运行时服务调度程序。BLR
LR
bl31_secap_handler
下面的结语在返回之前向堆栈添加了 0x50 个字节:bl31_secap_handler
0x1420528c: ldp x19, x20, [sp,#0x10]
0x14205290: ldp x21, x22, [sp,#0x20]
0x14205294: ldp x29, x30, [sp],#0x50
0x14205298: ret
我们正在使用的任意写入小工具,在地址 0x1420CF88 处找到,执行相同的操作,因此将正确返回:
0x1420cf88: str w1, [x0]
0x1420cf8c: csinc w0, w21, wzr, ne
0x1420cf90: ldp x19, x20, [sp,#0x10]
0x1420cf94: ldp x21, x22, [sp,#0x20]
0x1420cf98: ldp x23, x24, [sp,#0x30]
0x1420cf9c: ldp x29, x30, [sp],#0x50
0x1420cfa0: ret
在这个阶段,一旦我们将 SMC 处理程序指针替换为小工具的地址,我们现在可以使用以下参数从内核调用相应的 SMC,并开始使用我们的任意写入:BLR X2
X0
:被劫持的SMC ID(0xCA000001);
X1
:我们要写入的地址;
X2
:我们要写的值;
X3
:包含任意写入小工具地址的缓冲区。
该漏洞的下一步是通过再次修改 的 SMC 处理程序指针来获得更好的读/写基元。bl31_secap_svc
SMC 0xCA000001(地址 0x14230238)的处理程序指针更改为 0x14205E74,这是一个任意写入小工具:
0x14205e74: str x0, [x1]
0x14205e78: ret
SMC 0xCA000002(地址 0x14230248)的处理程序指针更改为 0x142013F4,这是一个任意读取小工具:
0x142013f4: ldr w0, [x0,x1]
0x142013f8: ret
在这个阶段,我们有了稳定的读/写原语,现在可以开始执行任意代码了。
为了在安全监视器中执行代码,我们的目标是修补 SMC 处理程序的代码。但是,由于代码部分被映射为只读,因此我们不能直接这样做。为了绕过 WXN 机制,我们决定以页表为目标,并以读写方式再次映射安全监视器。这样,我们就可以从可写映射中更改安全监视器的代码,并将更改镜像到可执行映射中。
此阶段的第一步是查找与监视器的代码和数据部分相对应的第三级 EL3 页表。为此,我们根据安全监视器映射的物理地址查找它们的描述符(即我们知道监视器的物理地址范围从 0x14200000 开始)。下面给出了页表中相应描述符的转储。
/* monitor code section */
0x14251000: c3 07 20 14 00 00 00 00 c3 17 20 14 00 00 00 00 .. ....... .....
0x14251010: c3 27 20 14 00 00 00 00 c3 37 20 14 00 00 00 00 .' ......7 .....
0x14251020: c3 47 20 14 00 00 00 00 c3 57 20 14 00 00 00 00 .G ......W .....
[...]
0x14251150: c3 a7 22 14 00 00 00 00 c3 b7 22 14 00 00 00 00 ..".......".....
0x14251160: c3 c7 22 14 00 00 00 00 c3 d7 22 14 00 00 00 00 ..".......".....
0x14251170: c3 e7 22 14 00 00 00 00 c3 f7 22 14 00 00 00 00 .."......."...../* monitor data + rodata section */
0x14251180: 43 07 23 14 00 00 40 00 43 17 23 14 00 00 40 00 C.#[email protected].#...@.
0x14251190: 43 27 23 14 00 00 40 00 43 37 23 14 00 00 40 00 C'#[email protected]#...@.
0x142511a0: 43 47 23 14 00 00 40 00 43 57 23 14 00 00 40 00 CG#[email protected]#...@.
[...]
0x14251310: 43 27 26 14 00 00 40 00 43 37 26 14 00 00 40 00 C'&[email protected]&...@.
0x14251320: 43 47 26 14 00 00 40 00 43 57 26 14 00 00 40 00 CG&[email protected]&...@.
0x14251330: 43 67 26 14 00 00 40 00 47 76 26 14 00 00 40 00 Cg&[email protected]&...@.
/* unused descriptors */
0x14251340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x14251350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x14251360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
[...]
0x142516d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x142516e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x142516f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
从上面可以看出,在监视器之后有多个未使用的描述符。可以通过将监视器的描述符复制到此未使用的空间,将其字段更改为读/写并设置其标志来实现双重映射。这样,我们将从虚拟地址0x14268000开始对监视器进行读写映射。现在,我们可以随心所欲地修补监视器代码。AP
PXN
剩下要做的最后一件事是修改另一个 SMC 处理程序指针,以执行我们放置在内存中某个位置的 shellcode。在我们的漏洞利用中,我们选择将最后一个处理程序指针(用于 SMC 0xCA000008)更改为 0x14200000,然后在相应的双映射地址处写入我们的 shellcode。至此,我们拥有劫持安全监视器并在 EL3 上执行代码所需的一切。bl31_secap_smc_handlers
步骤0:白名单仍然有效的初始状态。它阻止内核传递位于 CMA 区域之外的缓冲区,但易受攻击的命令除外。CMD_HISEE_FACTORY_CHECK
步骤1:我们触发漏洞两次,设置为 0xC 和设置为 0xAABBCC55。这将禁用 CMA 白名单,包括 send_ack
使用的响应数据缓冲区。g_cma_addr
g_cma_size
步骤2:我们可以让memcpy_s
调用将响应数据的字节写入任意地址。我们寻找一个函数指针,可以指向一个有趣的小工具,例如任意函数调用。
步骤3:我们第三次触发该漏洞:响应数据的第一个字节被写入 SMC 处理程序指针的 LSB,使其指向我们的任意调用小工具。我们寻找一个临时的写作小工具。
步骤4:我们将寄存器指向的任意写入小工具的地址放在内存中,并调用被劫持的 SMC。这有效地执行了任意调用小工具,即调用任意写入小工具。x2
步骤5:在调用被劫持的 SMC 时,我们设置其他寄存器,以便将任意写入小工具的地址写入 SMC 处理程序指针。现在可以直接调用任意写入小工具。
步骤6:我们调用两次劫持的 SMC,将读取小工具的地址写入另一个 SMC 处理程序指针。我们现在有稳定的读/写原语,可用于执行代码。
步骤7:我们使用 read 原语来定位 EL3 页表。在其中一个页面中,我们找到了映射虚拟地址的描述符0x14200000,即安全监视器的基址。
步骤8:由于 WXN 机制的原因,我们无法直接使代码段可写。相反,我们通过复制映射代码和数据部分的描述符来创建物理内存的第二个映射。
步骤9:在编写副本时,我们更改描述符的 and 位,以使映射可写。现在,我们可以使用 write 原语修补安全监视器的代码,并在 EL3 上执行代码。AP
PXN
我们已验证该漏洞是否影响了以下设备:
麒麟810:P40 精简版 (JNY)
请注意,其他型号可能已受到影响。
此漏洞的编号为 CVE-2021-39994,并在 2022 年 2 月的安全更新中进行了修补。
2021年8月09日 - 向华为PSIRT发送漏洞报告。
2021年8月26日 - 华为PSIRT确认该漏洞报告。
2022 年 2 月 1 日 - 此问题已在 2022 年 2 月更新中修复。
二进制漏洞(更新中)
其它课程
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
恶意软件开发(更新中)
还有很多免费教程(限学员)
更多详细内容添加作者微信