JuiceBox 40充电桩探究

文摘   2024-09-23 18:10   安徽  

以下是在JuiceBox 40充电桩上进行的研究总结。我们发现了一个漏洞,在比赛中,我们成功利用了CVE-2024-23938,在充电上执行了任意代码。


硬件

就硬件而言,JuiceBox 40相对简单。有几个主要组件:

  • Silicon Labs WGM160PX22KGA3 应用处理器 + WiFi模块

  • Silicon Labs MGM13S12A 蓝牙/低功耗蓝牙(BLE)处理器

  • Atmel ATMega328P 微控制单元(MCU) - 我们推测这是负责充电和安全控制的单元

  • Atmel M90E36A 电能计量集成电路(IC)

我们专注于由WGM160P处理器托管的软件作为比赛的主要目标,并没有深入研究MGM13S12A前端。


Reconnaissance

    在对JuiceBox进行目标评估期间,我们发现了Reddit上的一些帖子,讨论了充电器的问题以及如何通过同一网络下的设备可访问的远程终端来修复这些问题。此外,我们还发现了YouTube上的一些视频,车主们记录了他们对设备的修复和修改,以及其他包含同一供应商其他型号内部结构照片的帖子。例如,在iFixit上有一篇关于JuiceBox EVSE的文章,解释了如何更换设备中的继电器,这为我们提供了JuiceBox系列可能的主要硬件组件的线索。文章中的这张照片展示了我们寻找的有用信息——一个标有“ZENTRI”和“AMW106”的大型屏蔽模块:

简而言之,我们发现了很多有用的信息。最有趣的是,显然存在一个开放的管理员界面,网络上的任何其他设备都可以与之交互。

在我们初步探索结束时,我们得出了以下主要结论,这些结论可以用于进一步调查:

  1. JuiceBox 40的旧版本基于一个名为AMW106的WiFi模块,由Zentri制造。这些版本运行ZentriOS。

  2. JuiceBox 40的新版本可能基于一个后续的WiFi模块,即WGM160P。这些版本运行ZentriOS的后续版本,称为Gecko OS。

  3. AMW106和WGM160P平台的开发模块和套件可以从许多经销商那里轻易获得。

  4. ZentriOS和Gecko OS都有许多功能(例如远程终端功能),这些功能似乎可以在JuiceBox 40的生产环境中访问。



Getting closer to the firmware

我们开始仔细研究ZentriOS和Gecko OS的文档,重点是更新过程是如何实现的。

我们还希望找到一种方法,如果可能的话,下载固件。我们从文档中学到了很多,主要要点如下:

  1. 这两个操作系统都包括一个核心内核和“内置插件”部分,此外还有一个可选的应用程序,开发者可以将其与特定构建捆绑在一起。

  2. 两个系统都有一个完全管理的设备认证和更新机制,由Zentri设备管理服务(DMS)支持。

  3. 两个操作系统都被设计为既可以作为开发平台,也可以作为附加的即插即用解决方案。后者意味着该平台可以作为一种“连接性”附加组件,与一个独立的应用程序处理器结合使用。


我们决定进一步探索Zentri DMS,并很快了解到可以注册一个账户并为特定平台创建测试固件包,而不需要实际设备。我们创建了一个测试的WGM160P模块包,并将它的固件版本设置为Gecko OS的最新稳定基础映像(当时是4.2.7)。之后,我们可以看到构成固件的“文件”列表,以及一个JSON文件,指定了这些文件在内部闪存中的布局。


Development setup
我们购买WGM160P Starter Kit (SLWSTK6121A)。该套件带有一个WGM160P开发模块和一个基础板,通过USB连接暴露了多个接口:

我们很快就能用Gecko OS Studio将其运行起来。在设置过程中,我们下载了针对Gecko OS 4.2.7的SDK——当然了,它包含了核心内核和插件部分的对象文件。

user@birdie 4.2.7-11064 % find . -name '*.a'./tools/toolchains/gcc/arm/osx/libgcc.a./hardware/platforms/gecko_os/wgm160p/libs/gcc/gecko_os_libraries/plugins_core/lib_network_protocols_websocket.a./hardware/platforms/gecko_os/wgm160p/libs/gcc/gecko_os_libraries/plugins_core/lib_network_util_arp_lock.a..../hardware/platforms/gecko_os/wgm160p/libs/gcc/gecko_os_libraries/kernel_shared/gecko_os_kernel_chips_efx32_peripherals_crc.a./hardware/platforms/gecko_os/wgm160p/libs/gcc/gecko_os_libraries/kernel_shared/gecko_os_kernel_core_dump.a...



Vulnerability discovery (CVE-2024-23938)
在分析基础映像仅几天后,我们就发现了一个漏洞。这是一个Gecko OS系统消息记录组件中的错误,我们可以使用OS的远程终端功能随时触发。

Gecko OS system messages

Gecko OS,如同许多嵌入式操作系统一样,提供了操作系统的基本预期功能集,如硬件/软件接口、调度和资源管理。然而,不同于许多嵌入式操作系统,由于其作为IoT应用的交钥匙解决方案的设计理念,Gecko OS提供了许多高级功能。例如,它有一个配置Web界面,位于一个用于将设备连接到用户WiFi网络的引导WiFi接入点设置组件之上。它还提供了各种应用级功能的高级API,如HTTP、TLS,以及TCP客户端和服务器实现等等,这些都被以某种形式内置到了操作系统(或其子组件)中。

处理这些“高级”动作涉及许多活动部件,在这些交互过程中自然会有一些时候需要提供状态更新。这些更新可能以多种形式有用,例如,对于用户来说是以人类可读的格式,或者是对于其他解析文本格式的辅助应用处理器。为了这个目的,Gecko OS包含了一个系统消息记录设施,当某些事件发生时,该设施会将可定制的消息写入所使用的任何日志接口(串行、网络等)。根据Gecko OS系统.msg变量的文档,有各种消息类型(及其相应的事件)会被记录下来:


Message NameDefault Value

initialized

[@tReady]

stream_closed

[@tClosed: @c]

stream_failed

[@tOpen failed]

stream_opened

[@tOpened: @c]

stream_opening

[@tOpening: @h]

sleep

[@tSleep]

wlan_failed

[@tJoin failed]

wlan_joined

[@tAssociated]

wlan_joining

[@tAssociating to @s]

wlan_leave

[@tDisassociated]

softap_joined

[@t@m associated]

softap_leave

[@t@m disassociated]

ethernet_starting

[@tEthernet Starting]

ethernet_started

[@tEthernet Started]

ethernet_stopped

[@tEthernet Stopped]

ethernet_failed

[@tEthernet Failed]

这些消息可以通过设置Gecko OS的system.msg变量并指定所需的消息名称和消息文本来定义。例如,要将初始化消息的日志文本改为“Rise and shine”而不是默认消息,可以发出如下命令:

set system.msg initialized "Rise and shine"

此外,系统消息支持一小部分标签,以向记录的消息中添加动态信息。可以将这些系统消息视为模板,其中消息的一部分是字符串字面量,另一部分是占位符变量。某些标签被所有消息类型支持,比如@t标签,每次出现都会被替换成时间戳。与此同时,有些标签只被少数几种消息类型支持。例如,@c标签可用于打印流句柄,这仅对stream_closed和stream_opened消息有意义。以下是根据Gecko OS文档列出的各标签可用性:


TagDescriptionTag is available for …
@t

Timestamp

Can be set for all messages, but displays a value only for ethernet messages.

@s

SSID

WLAN messages

@c

Stream handle

stream_closed, stream_opened

@h

Connection host/port

stream_failed, stream_opening

@m

Client MAC Address

softap_joined, softap_leave


基于上述内容,假设我们想要记录一条消息,指示正在为HTTP请求打开流时的连接主机/端口对、一个静态消息和时间戳。为此,我们可以发出如下命令:

set system.msg stream_opening "[@t] Connecting to @h"

以此类推。主要限制是系统消息模板的最大长度为32字节(包括终止的NUL字节)。然而,由于标签表示的动态数据可能比标签本身占用的空间要长,因此实际打印的消息可能会远超32字节。


Out of bounds write

最初,我们是在寻找“经典”的漏洞,涉及通常的嫌疑函数,如带有攻击者控制的数据和计数的memcpy调用;或是snprintf调用中返回值被误用的情况。后者让我们进入了一个看似负责将动态数据应用于系统消息模板并记录结果的例程:

void print_system_msg(int msg_id, system_msg_arg *args){  char cVar1;  char **ppcVar2;  int iVar3;  byte *snrpintf_generic_arg;  char *dst;  char *template_;  size_t n;  uint snprintf_uint_arg;  byte local_190;  undefined scratch_buf_2 [35];  byte scratch_buf_1 [132];  char formatted_message_buffer [192];  undefined message_buffer_end [4];  system_message_entry *message_list;  char *fmt;  byte format_tag;
message_list = (system_message_entry *)system_message_list; template_ = message_list[msg_id].message_format; if (message_list[msg_id].message_format[0] != '\0') { dst = formatted_message_buffer; while (cVar1 = *template_, cVar1 != '\0') { if (cVar1 == '@') { format_tag = template_[1]; if (format_tag == 'm') { ppcVar2 = &args->value2; args = (system_msg_arg *)&args->value1; FUN_0002a018(*ppcVar2,dst); dst = dst + 0x11; } else if (format_tag < 'n') { if (format_tag == 'c') { snrpintf_generic_arg = (byte *)args->value2; args = (system_msg_arg *)&args->value1; fmt = "Running : %u";snprintf_into_dst: iVar3 = snprintf_signed_checked (dst,(int)(message_buffer_end + -(int)dst),fmt + 0xe, snrpintf_generic_arg); dst = dst + iVar3; } else if (format_tag == 'h') { snprintf_uint_arg = args->value1; iVar3 = snprintf_signed_checked (dst,(int)(message_buffer_end + -(int)dst),"%s",args->value2); args = args + 1; dst = dst + iVar3; if (snprintf_uint_arg != 0) { fmt = dst + 1; *dst = ':'; iVar3 = snprintf_signed_checked (fmt,(int)(message_buffer_end + -(int)fmt),"%u",snprintf_uint_arg); dst = fmt + iVar3; } } } else { if (format_tag == 's') { n = args->value1; memcpy(scratch_buf_2,args->value2,n); local_190 = (byte)n; args = args + 1; escape_hex(scratch_buf_1,&local_190); snrpintf_generic_arg = scratch_buf_1; fmt = "Web browser : %s"; goto snprintf_into_dst; } if ((format_tag == 't') && (iVar3 = print_timestamp_to_string(scratch_buf_1,1), iVar3 == 0)) { memcpy(dst,scratch_buf_1,10); dst[0xb] = '|'; dst[10] = ' '; dst[0xc] = ' '; memcpy(dst + 0xd,scratch_buf_1 + 0xb,8); dst[0x15] = ':'; dst[0x16] = ' '; dst = dst + 0x17; *dst = '\0'; } } template_ = template_ + 2; } else { *dst = cVar1; template_ = template_ + 1; dst = dst + 1; } } log_message(formatted_message_buffer,(int)dst - (int)formatted_message_buffer); } return;}

该函数通过消息ID(msg_id)和一系列参数调用,这些参数根据系统消息的类型可能与消息相关。msg_id是对内部闪存中配置段存储的默认Gecko OS系统消息模板列表的索引。

函数为生成的消息分配了192字节的栈缓冲区。然后,它遍历模板中的字符,对于每个@字符,检查它是否是已知的模板标签,并相应填充。其他字符则直接复制到结果缓冲区。该循环在到达模板末尾时结束,而不检查结果缓冲区已写入的字符数是否超出界限。

在处理不同标签时,有几个问题值得关注,我们将逐一深入分析。首先,让我们看看在处理时间戳的@t标签时最突出的问题:

if ((format_tag == 't') && (iVar3 = print_timestamp_to_string(scratch_buf_1,1), iVar3 == 0)) {    memcpy(dst,scratch_buf_1,10);    dst[0xb] = '|';    dst[10] = ' ';    dst[0xc] = ' ';    memcpy(dst + 0xd,scratch_buf_1 + 0xb,8);    dst[0x15] = ':';    dst[0x16] = ' ';    dst = dst + 0x17;    *dst = '\0';}
在上面的代码片段中,该例程首先将当前时间写入一个临时缓冲区,然后通过几次memcpy操作将内容拷贝到由dst指向的位置,并写入一些格式化字符。之后,它简单地将dst递增17h/23d个位置并写入一个NUL字节。在整个过程中,它从未检查过是否写入的结果超出了formatted_message_buffer的范围。
考虑到消息最多可以包含31个可用字符的文本,而@t标签每次使用2个字符,因此我们可以在一条消息中最多使用15个@t标签。如果我们这样做,并考虑到每个@t标签会产生23字节的输出,这意味着我们可以使用这15个@t标签写出23 * 15 = 345字节的文本。345远远大于192,这意味着我们可以很容易地超出栈上分配的输出缓冲区范围,进入保存的寄存器和更高的栈帧。
该例程实现中的另一个特点是替换值如何通过args参数传递。该参数预期是一个数组,其中包含将用于替换消息模板中除@t之外的占位符标签的值。提供的args数组与期望匹配的标签之间没有关联,因此在模板中指定了参数和标签出现的隐式顺序。一旦使用了一个参数,函数就会继续使用数组中的下一个元素,以此类推。这通常是没问题的,因为特定消息类型的专有标签不允许与其他标签(除了@t)组合使用。然而,当模板中有多个相同的标签出现时,就会出现功能性问题。调用者不会提供多个相同参数来匹配模板中使用的标签数目。这一特点非常重要,因为它为我们利用技术的关键成分之一提供了条件,我们将在后面详细介绍这一点。

Controlling saved PC
观察栈布局,该例程写入的缓冲区正好距离保存的PC(程序计数器)232字节。通过一些计算,很明显我们只需要使用10个@t标签,产生230字节的输出,再加上2个字面量,就可以开始覆盖保存的PC。这允许我们除了字面量@/40h和NUL字节之外,用任意数据覆盖PC,因为这两个字面量表示消息的结尾,并不会被子程序写入formatted_message_buffer。
通常情况下,这可能不是什么大问题,因为我们或许可以通过ROP小工具链来实现,假定我们可以在保存的PC之后写入足够的数据。然而,我们仅限于31字节,而10个@t标签加上2个字面量已经占用了22字节,因此我们只剩下9字节。这意味着我们只能完全覆盖PC和一个额外的保存寄存器。这看起来非常受限,并且需要一个非常特定的ROP小工具,而且我们也想避免必须针对特定构建调整利用方案。

Pivoting to configuration section
经过更多调查后,我们找到了另一条路径。具体来说,我们意识到Gecko OS有一个变量允许我们在一个非常可预测的闪存位置写入16个任意字节(该位置也被映射为可执行区域)。通过更改设备的配置加密密钥,即system.security_key变量,我们可以在闪存的配置段中存储16字节的shellcode。该段的起始位置可以在0x2000或0x5000之间切换,每次设备配置改变时都会切换。这可能是为了调节闪存的磨损,我们不得不在最终的利用中解决这个问题,但实际上第一次尝试成功的概率是50%,第二次则是100%,所以我们认为这样的几率足够好。
然而,我们遇到了另一个问题。为了将值0x2XXX或0x5XXX写入栈上的保存PC,最高两位必须是NUL。但这并不适用于设备上运行的任何代码,因为所有的可执行代码都从不以NUL作为第二位最高有效字节的位置开始。另外,该函数在处理模板时除了使用@t标签时不写入任何NUL字节。理论上,我们可以使用@t标签部分覆盖PC并将其第二位最高有效字节设置为NUL,但这也不允许将PC设置到所需的范围内。
尽管看起来像是死胡同,但我们还不想放弃这个漏洞。因此,我们决定花些时间来构思一个可行的利用路径。

Using every bit of machinery
经过几小时在草稿板和子函数功能之间的反复思考,我们设法找到了一种增加PC控制的方法。要做到这一点,我们必须利用该子函数处理不同标签的方式以及它调用的函数导致的一些微妙副作用。

Host/connection tag
如果我们能够越界,使用@t标签在一个期望的位置写入一个NUL字节,然后将dst指针回退,那么我们就可以写入一个任意的字面量,再跟上一个NUL字节。但这可能吗?
答案是肯定的,通过结合使用主机/连接标签@h和@t标签。以下是处理@h的代码段:
else if (format_tag == 'h') {    snprintf_uint_arg = args->value1;    iVar3 = snprintf_signed_checked                        (dst,(int)(message_buffer_end + -(int)dst),"%s",args->value2);    args = args + 1;    dst = dst + iVar3;    if (snprintf_uint_arg != 0) {        fmt = dst + 1;        *dst = ':';        iVar3 = snprintf_signed_checked                        (fmt,(int)(message_buffer_end + -(int)fmt),"%u",snprintf_uint_arg);        dst = fmt + iVar3;    }}

回想一下,dst是指向输出字符串当前位置的指针,而message_buffer_end则是在输出字符串缓冲区末尾的下一个位置。代码非常直接,snprintf被调用以将字符串值写入dst,最大长度为message_buffer_end - dst。之后,snprintf的返回值(对于POSIX兼容的实现,这是如果缓冲区足够大它将写入的字节数)被无差别地加到了dst上。单凭这一点听起来就像是一个漏洞,如果提供的字符串args->value2比缓冲区的长度大得多怎么办?更重要的是,如果我们遇到dst > message_buffer_end的情况,就像我们在处理@t参数时滥用漏洞那样,又会怎样?

实际上,这个snprintf的实现有点不同:

int snprintf_signed_checked(char *dst,int maxn,char *fmt,...){  undefined4 *puVar1;  int res;  // ...
puVar1 = (undefined4 *)DAT_20000984; if (maxn < 0) { res = -1; *puVar1 = 0x8b; } else { // ... } return res;}

如果提供的maxn参数是一个负数,那么它会返回-1并设置一些错误代码而不执行任何其他操作。在print_system_msg中并没有对此条件进行错误处理!事实上,在处理@h标签的情况下,如果dst已经超出缓冲区范围,则最终结果是dst被递减!此外,如果dst仍在范围内,但是提供的替换值大于缓冲区大小,那么这个snprintf实现将以预期的方式行为,并返回它将要写的字节数,这意味着dst将被递增到超出范围。后一种情况特别有趣,因为它意味着即使启用了栈保护(canary),利用仍然是可能的。


Putting it all together
我们终于找到了一个可行的利用路径,允许我们使用配置段的部分作为shellcode存储。这是我们想到的PoC(概念验证)示例:
set system.msg stream_opening @t@t@t@t@h@t@h@hAB

其中A和B是配置段中下一阶段地址的低位字节。我们会通过使用http_head命令并带上特定长度的“域名”来发起HTTP请求,从而触发利用链:
http_head aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa


一旦请求被触发,Gecko OS将会生成一个stream_opening系统日志消息,最终这条消息会传给print_system_msg处理。下面的图表显示了例程在处理日志消息时栈帧的变化情况:


Exploitation

带着漏洞和利用路径,我们着手开发我们的利用链。事实证明,拥有WGM160P开发者套件是非常有用的,原因有几个。首先,我们能够轻松地部署和测试不同的场景,使用Gecko OS Studio连接到我们的测试模块。其次,也许最重要的是,我们能够通过测试模块上的SWD接口简单地检查副作用来调试我们的利用代码。后者是无法(轻易)在生产模块上做到的,比如JuiceBox使用的模块,因为那些模块是被锁定的。

平台的主要卖点之一是整个更新周期和设备认证参数是由厂商管理的,反过来,应用设计师可以通过Zentri DMS来管理这些参数。然而,这意味着生产单元实际上不需要任何形式的调试能力,使其成为一个安全(且更安全的选择)从一开始就禁用所有不必要的接口。

除此之外,没有什么缓解措施阻碍了利用开发,因此获得控制流控制是一项简单的工作。使开发过程稍微复杂一点的是我们被迫使用的多阶段以及设备本身的测试限制。


Testing on production

不管生产模块上缺乏JTAG/SWD接口,我们需要在事件前对设备进行利用链测试,以确保它确实能工作。自然而然,由于设备上部署的应用代码与操作系统之间的交互作用,在我们的开发设置中并未体现出来,因此我们遇到了一些问题。然而,我们找到了一种方法来了解发生了什么,以便我们可以根据需要进行调整。

Gecko OS提供了一种报告操作故障的机制,比如硬故障。如果触发了故障处理器,它会将几个关键寄存器的值存储到闪存中,然后重启到一个安全的状态。这些故障可以通过使用faults_print命令来列出:

0: Hard Fault Exception, PC:0x42424242 LR:0x0003F8AB HFSR:0x40000000 CFSR:0x00000001 MMFAR:0x00000000 BFAR:0x000000001: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C22: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C23: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C24: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C25: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C26: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C27: Hard Fault Exception, PC:0x000123A0 LR:0x00077C6B HFSR:0x40000000 CFSR:0x00008200 MMFAR:0x00000000 BFAR:0x400AE2C2

这些列表提供了足够多的信息,使我们能够在JuiceBox上继续我们的利用开发。嗯,直到我们遇到了安全模式。


重置JuiceBox

简而言之,安全模式是Gecko OS为应对系统持续异常行为而实施的一种恢复机制。默认情况下,当发生8次故障时,会触发安全模式。当系统处于安全模式时,大多数特性都是禁用的,包括WiFi连接,并且只有有限数量的命令可以通过串行接口使用。

当然,在开发我们的利用过程中,我们无意间进入了安全模式。如果我们知道安全模式的存在并在达到8次故障限制之前使用faults_reset命令,这种情况是可以避免的。

无论如何,我们不得不找到一种方式退出安全模式,回到正常的(非安全^W)操作模式。一个问题在于,默认情况下,用于与安全模式交互的串行口被连接到了Atmel ATMega328P充电控制器作为两个处理器之间的通信通道。这意味着只要Atmel控制器在运行,我们就无法发出必要的命令来退出WGM160P的安全模式。

因此,我们必须禁用Atmel芯片,为此我们选择了简单的解决方案,即拉低其nRESET引脚。这样就把它置于编程模式,并保持串行口的畅通,以便我们可以发出必要的命令:


Staging the exploit

我们的最终利用共使用了三个阶段:

  1. 初始的16字节shellcode启动器,存储在配置段中作为system.security_key,负责触发下一阶段。

  2. 较大的第二阶段shellcode块,存储在系统消息中,除了我们需要的那个!这些存储在内核数据内存区域,并由第一阶段可以到达的静态位置引用。此外,这一阶段的shellcode唯一限制是不能包含NUL字节或@符号,因为它们可能不符合给定消息的允许标签。此阶段负责读取并执行最终的有效载荷。

  3. 最后——有效载荷,作为一个文件保存在设备的闪存文件系统中。这只需使用远程终端下载一个文件并在设置阶段将其存储到闪存即可。

在我们前往比赛的所有准备好的利用中,这是迄今为止最复杂的,并且有最多的移动部件。


影响

就设计而言,JuiceBox的充电控制器与连通性处理器是分离的。因此,很可能对充电过程的损害是微乎其微的。然而,我们尚未充分研究连通性处理器与充电控制器之间的串行协议,以排除物理/电气损坏的可能性。

此外,蓝牙模块暴露的BLE服务提供了访问Gecko OS命令行的第二个接口,这使得获取设备连接的WiFi网络的凭证成为可能。这可以通过发动deauth攻击来实现,这将导致JuiceBox重新启动并进入配置模式,重新启用BLE服务。由于BLE连接不需要认证,攻击者可以使用它来检索WiFi网络的凭证,并让设备重新连接。之后,他们有可能利用充电在网络中获得隐蔽立足点或对同一网络中的其他设备发动进一步攻击。





安全脉脉
我们致力于提高车联网安全的意识,推动行业发展,保护车辆和驾驶者免受潜在威胁的影响。在这里可以与车联网安全领域的专家和爱好者分享知识、深入思考、探讨标准法规、共享工具和讨论车联网热点事件。
 最新文章