Read time:00:35
Release date:6.3.2025
Product description
PCAutomotive team verified all the findings from this advisory on the model Nissan Leaf ZE1 manufactured in 2020:
Nissan Leaf ZE1 2020 appearance
The car was equipped with an infotainment ECU (Electronic Control Unit) manufactured by BOSCH:
Nissan infotainment system. Source: pcmag.com
The IVI exposes Wi-Fi (in client mode), Bluetooth, and USB connections. At the same time, the IVI is connected to other in-vehicle ECUs (Electronic Control Units) via a CAN bus. The IVI also has access to private information about the car owner. This makes the IVI an interesting target for would-be attackers.
The CAN bus interface of the IVI is handled by RH850 CPU core, which is responsible for sending/receiving CAN messages to/from corresponding CAN buses connected to the IVI.
The infotainment system installed in Nissan Leaf ZE1 vehicle under assessment had software version 283C30861E. Thus, the research focused on this version of firmware.
Nissan Leaf IVI appearance
Summary
PCAutomotive team identified multiple vulnerabilities that allow an attacker to obtain full control over the IVI via 1-time Bluetooth access. From the IVI, it was possible to control multiple car body functions:
- Steering wheel control (at speed lower than ~10 km/h)
- Opening/closing central door lock
- Folding in and out rear-view mirrors
- Opening/closing car windows.
In addition, it was possible to control less critical functions such as wipers, washers, and horn.
An attacker could also leverage full control over the IVI to:
- Exfiltrate driver’s private data, such as location
- Eavesdrop via in-vehicle microphone
- Control the IVI screen
- Control in-vehicle sound system.
The impact demonstration video is available on YouTube.
The list of vulnerabilities identified by PCAutomotive team:
CVE ID | Title | CVSS 3.1 Score |
---|---|---|
CVE-2025-32056 | Anti-theft bypass for infotainment ECU | 4.0 (Medium) |
CVE-2025-32057 | Misconfigured SSL/TLS communication of Redbend service for infotainment ECU | 6.5 (Medium) |
CVE-2025-32058 | Stack overflow in processing requests over INC interface on RH850 side of infotainment ECU | 9.3 (Critical) |
CVE-2025-32059 | Stack overflow leading to RCE in Bluetooth stack of infotainment ECU [0] | 8.8 (High) |
CVE-2025-32060 | Absence of Linux kernel module signature verification on infotainment ECU | 6.7 (Medium) |
CVE-2025-32061 | Stack overflow leading to RCE in Bluetooth stack of infotainment ECU [1] | 8.8 (High) |
CVE-2025-32062 | Stack overflow leading to RCE in Bluetooth stack of infotainment ECU [2] | 8.8 (High) |
CVE-2025-32063 | Enabling SSH server on infotainment ECU | 6.8 (Medium) |
Additionally, the IVI appeared vulnerable to the following already known security issues:
CVE ID | Title | CVSS 3.1 Score |
---|---|---|
CVE-2017-7932 | Stack overflow in HAB of i.MX 6 leading to the Secure Boot Bypass on infotainment ECU | 6.0 (Medium) |
CVE-2017-9832 | Integer overflow leading to code execution in processing MTP on infotainment ECU | 6.8 (Medium) |
Disclosure timeline
Date | Description |
---|---|
02.08.2023 | PCAutomotive sends the advisory to Nissan Cybersecurity Team |
09.08.2023 - 11.12.2023 | Email discussion about the findings’ criticality |
04.01.2024 | PCAutomotive sends a video demonstration of the full attack chain; asks about CVE registration; notifies about publication plans |
26.01.2024 | Nissan Cybersecurity Team confirms the vulnerabilities; starts planning their mitigations; notifies us to register CVE by ourselves; accepted the publication plans |
25.04.2024 | PCAutomotive requests CVE registration from MITRE |
19.05.2024 | MITRE forwards us to Bosch PSIRT |
10.09.2024 | PCAutomotive sends Bosch PSIRT a request to register CVE |
11.09.2024 | Bosch PSIRT responds that they didn’t receive any information about vulnerabilities from Nissan Cybersecurity Team |
12.09.2024 | PCAutomotive notifies Nissan Cybersecurity Team about the communication with Bosch PSIRT |
23.09.2024 | PCAutomotive sends the advisory to Bosch PSIRT |
06.11.2024 | PCAutomotive notifies Bosch PSIRT about the publication plans |
11.03.2025 | Bosch PSIRT accepts the publication, declines to register CVE and forward us to ASRG |
18.03.2025 | PCAutomotive requests CVE registration from ASRG |
03.04.2025 | ASRG registered CVE for vulnerabilities |
Technical details
CVE-2025-32056: Anti-theft bypass for infotainment ECU
Description
When disconnected from an original vehicle, the IVI activates a special anti-theft protection mechanism.
When the head unit is switched on, it sends a diagnostic CAN message with CAN-ID 0x71e
and waits for a specific response from CAN-ID 0x72e
. Messages from the head unit differ at every power cycle, and depending on them, the expected response changes. If the head unit receives a correct answer, it sends another message with CAN-ID 0x71e
and data 24c76c9a9889ffff
, then it waits for a response with CAN-ID 0x72e
and the same data 24c76c9a9889ffff
, after which the anti-theft check will be passed.
If an incorrect response is received, the head unit goes into anti-theft with status RED. If no response is received – anti-theft with status GREEN.
Example of triggered anti-theft
For example, in the traffic from the vehicle, one can see the following messages:
CAN-ID | Message |
---|---|
0x71e (Head Unit) | 14 03 f05bb5 17 ffff |
0x72e | 14 c826e381 66 ffff |
0x71e | 24 c76c9a98 89 ffff |
0x72e | 24 c76c9a98 89 ffff |
Another example of the CAN traffic:
CAN-ID | Message |
---|---|
0x71e | 14 06 f05bb5 1a ffff |
0x72e | 14 e907c2a0 66 ffff |
0x71e | 24 c76c9a98 89 ffff |
0x72e | 24 c76c9a98 89 ffff |
The following list of CAN message correspondences can be compiled (sorted by seed number):
0x71e | 0x72e |
---|---|
14 00 f05bb5 14 ffff | 14 50505050 54 ffff |
14 01 f05bb5 15 ffff | 14 efefefef d0 ffff |
14 02 f05bb5 16 ffff | 14 10101010 54 ffff |
14 03 f05bb5 17 ffff | 14 3d3d3d3d 08 ffff |
14 04 f05bb5 18 ffff | 14 7a7a7a7a fc ffff |
14 05 f05bb5 19 ffff | 14 d6d6d6d6 6c ffff |
14 06 f05bb5 1a ffff | 14 1c1c1c1c 84 ffff |
14 07 f05bb5 1b ffff | 14 90909090 54 ffff |
14 08 f05bb5 1c ffff | 14 a2a2a2a2 9c ffff |
14 09 f05bb5 1d ffff | 14 fdfdfdfd 08 ffff |
14 0a f05bb5 1e ffff | 14 1f1f1f1f 90 ffff |
14 0b f05bb5 1f ffff | 14 bdbdbdbd 08 ffff |
14 0c f05bb5 20 ffff | 14 51515151 58 ffff |
14 0d f05bb5 21 ffff | 14 71717171 d8 ffff |
14 0e f05bb5 22 ffff | 14 abababab c0 ffff |
14 0f f05bb5 23 ffff | 14 fefefefe 0c ffff |
14 10 f05bb5 24 ffff | 14 f0f0f0f0 d4 ffff |
14 11 f05bb5 25 ffff | 14 6c6c6c6c c4 ffff |
14 12 f05bb5 26 ffff | 14 e3e3e3e3 a0 ffff |
14 13 f05bb5 27 ffff | 14 41414141 18 ffff |
14 14 f05bb5 28 ffff | 14 bebebebe 0c ffff |
14 15 f05bb5 29 ffff | 14 25252525 a8 ffff |
14 16 f05bb5 2a ffff | 14 5c5c5c5c 84 ffff |
14 17 f05bb5 2b ffff | 14 d7d7d7d7 70 ffff |
14 18 f05bb5 2c ffff | 14 42424242 1c ffff |
14 19 f05bb5 2d ffff | 14 bbbbbbbb 00 ffff |
14 1a f05bb5 2e ffff | 14 26262626 ac ffff |
14 1b f05bb5 2f ffff | 14 08080808 34 ffff |
14 1c f05bb5 30 ffff | 14 d5d5d5d5 68 ffff |
14 1d f05bb5 31 ffff | 14 9d9d9d9d 88 ffff |
14 1e f05bb5 32 ffff | 14 4a4a4a4a 3c ffff |
14 1f f05bb5 33 ffff | 14 a1a1a1a1 98 ffff |
14 20 f05bb5 34 ffff | 14 50505050 54 ffff - loop |
The messages in the listing and traffic samples above are separated into pieces by whitespaces, following the format of those messages. More specifically, messages with CAN-ID 0x71e
have the following format:
Function | Seed | Const0 | Const1 | Const2 | Chksum | ConstFF | ConstFF |
---|---|---|---|---|---|---|---|
14 | 01 | f0 | 5b | b5 | 15 | ff | ff |
The checksum field “Chksum” is calculated as follows: (0x14 + 0x01 + 0xF0 + 0x5B + 0xB5) && 0x0FF = 0x15
.
Messages with CAN-ID 0x72e
have a slightly different format:
Function | Res0 | Res1 | Res2 | Res3 | Chksum | ConstFF | ConstFF |
---|---|---|---|---|---|---|---|
14 | ef | ef | ef | ef | d0 | ff | ff |
The checksum for messages with CAN-ID 0x72E
is calculated in the same way as for messages with CAN-ID 0x71E
.
There are 32 requests from the head unit in total. It is possible to reveal all 32 corresponding responses by sniffing CAN traffic or by pre-calculating the values, which will bypass the protection.
Credits
The vulnerability was identified by Polina Smirnova from PCAutomotive Security Assessment team.
CVE-2025-32057: Misconfigured SSL/TLS communication of Redbend service for infotainment ECU
Description
The IVI uses Redbend service for provisioning and firmware updates. For that, IVI initiates SSL connection as client to the Redbend server on the internet.
Critical information on the IVI (such as private keys for SSL) is stored in the wrapped mode - encrypted by CAAM (Cryptographic Accelerator and Assurance Module) crypto device. Therefore, in the SSL handshake logic to unwrap (decrypt) the private keys, the mechanism of callbacks inside the OpenSSL engine is used. Because of a logic issue based on the setup configuration and flow of callbacks, a self-signed certificate of the back-end server can be used for HTTPS communication.
An attacker can act as a legit Redbend back-end server and provide fake provisioning data and updates.
There are two functions inside the app_redbend_out.out
which perform certificate verification during the handshake process:
sub_46754
(assigned inSSL_CTX_set_cert_verify_callback
) - validates a certificate by a host name.sdc_ecm_load_client_cert
(it's a part ofsdc-ecm
engine assigned inSSL_CTX_set_client_cert_engine
) - validates a server certificate by the local storage and then loads a client one.
Function sub_46754
looks the following way:
int __fastcall sub_46754(int a1, const char *a2)
{
int v4; // r0
int v5; // r0
int v6; // r0
int v8; // r0
v4 = _vdmfile__(
"/home/umi1cob/samba/views/umi1cob_AI_PRJ_RN_AIVI_18.12V10.vws/ai_ota/nissan_ncg3/swmc/redbend/sdk/source/comm/v"
"dm_comm_pl_conn.c");
VDM_logDebug_Func(v4, 1434, 3, "%s", "SDCECM_verify_callback");
v5 = _vdmfile__(
"/home/umi1cob/samba/views/umi1cob_AI_PRJ_RN_AIVI_18.12V10.vws/ai_ota/nissan_ncg3/swmc/redbend/sdk/source/comm/v"
"dm_comm_pl_conn.c");
VDM_logError_Func(v5, 1435, 3, "SDCECM_verify_callback: HostName: %s", a2);
if ( X509_check_host(*(_DWORD *)(a1 + 8), a2, 0, 0, 0) == 1 )
{
v8 = _vdmfile__(
"/home/umi1cob/samba/views/umi1cob_AI_PRJ_RN_AIVI_18.12V10.vws/ai_ota/nissan_ncg3/swmc/redbend/sdk/source/comm"
"/vdm_comm_pl_conn.c");
VDM_logDebug_Func(v8, 1444, 3, "%s", "HostName verification success");
return 0;
}
else
{
ENGINE_finish(gpSdcEcmEngine);
v6 = _vdmfile__(
"/home/umi1cob/samba/views/umi1cob_AI_PRJ_RN_AIVI_18.12V10.vws/ai_ota/nissan_ncg3/swmc/redbend/sdk/source/comm"
"/vdm_comm_pl_conn.c");
VDM_logError_Func(v6, 1441, 3, "%s", "HostName verification failed");
return 1;
}
}`
For a self-signed certificate, it calls ENGINE_finish
(turns off the engine) and returns 0. There are notes regarding this API function, which is used for handler registration:
callback should return 1 to indicate verification success and 0 to indicate verification failure.
In server mode, a return value of 0 leads to handshake failure. In client mode, the behaviour is
as follows. All values, including 0, are ignored if the verification mode is SSL_VERIFY_NONE.
Otherwise, when the return value is less than or equal to 0, the handshake will fail.
The SSL engine runs in a client mode, hence, in case of SSL_VERIFY_NONE
all the security measures can be bypassed using a self-signed certificate.
By default, the SSL_VERIFY_NONE
is used:
Programming considerations
1. To use this function, you must include the library that is specified in the prototype in your makefile.
2. The default value for the verify mode is SSL_VERIFY_NONE when the CTX structure is created. You must issue the SSL_set_verify function or the SSL_CTX_set_verify function with the SSL_VERIFY_PEER option if you want to authenticate remote peers when SSL sessions are started.
As a result, an attacker can perform a MiTM attack:

The result of a performed MiTM attack
Credits
The vulnerability was identified by Radu Motspan from PCAutomotive Security Assessment team.
CVE-2025-32058: Stack overflow in processing requests over INC interface on RH850 side of infotainment ECU
Description
CSM (CAN Proxy on IMX6 – main CPU of the IVI running Linux OS) service uses INC interface to broadcast signal messages to the CAN busses. In fact, the signal messages are CAN messages, but their CAN IDs are restricted. RH850 can send messages with CAN IDs that are hardcoded in the firmware.
During the processing of the signal messages over the INC interface, there is a stack-based overflow vulnerability. It allows an attacker to perform code execution on RH850 module. Moreover, using this vulnerability an attacker with code execution on the IVI can send raw CAN messages over IDT and MPDT buses. An attacker can fully control the ID and content of a CAN message.
In case of requests to the NET_BROADCAST_PORT
of inc-scc
interface, the following handler is used:
0002b420 00 00 00 00 00 SCC_HANDLER_DESC [15]
00 00 00 00 00
00 00 00 00 00
0002b420 00 00 00 00 ddw 0h field0_0x0
0002b424 00 00 00 00 addr 00000000 field1_0x4
0002b428 00 00 00 00 addr 00000000 field2_0x8
0002b42c 00 00 00 00 addr 00000000 field3_0xc
0002b430 08 9a 06 00 addr prepareNetBroadcast pfnPrepareIn
0002b434 6c 99 06 00 addr fillNetBroadcast pfnFillInput
0002b438 46 99 06 00 addr processNetBroadcast pfnProcessRe
0002b43c b0 99 06 00 addr FUN_000699b0 field7_0x1c
0002b440 fe 99 06 00 addr FUN_000699fe field8_0x20
0002b444 44 ae df fe addr DAT_fedfae44 field9_0x24 = ??
Responsibilities of these functions:
prepareNetBroadcast
: prepares a global buffer for an incoming request:
undefined4 prepareNetBroadcast(undefined4 param_1,int param_2)
{
undefined4 uVar1;
if ((*(ushort *)(param_2 + 4) < 0x65) && (DAT_fede6a60 == '\0')) {
gwNetBroadcastEnd = 0;
gpNetBroadcastPacket1 = gsNetBroadcastPacket;
uVar1 = 0;
gpNetBroadcastPacket2 = (dword *)gsNetBroadcastPacket;
DAT_fede6a60 = '\x01';
gwBroadcastPacketLength = *(ushort *)(param_2 + 4);
}
else {
uVar1 = 1;
}
return uVar1;
}
fillNetBroadcast
: copies data from a request to the previously prepared buffer:
int fillNetBroadcast(undefined4 param_1,undefined4 *param_2)
{
bool bVar1;
int in_r10;
int iVar2;
bVar1 = 99 < (uint)gwNetBroadcastEnd + (uint)*(ushort *)(param_2 + 1);
iVar2 = (uint)bVar1 + in_r10 * (uint)!bVar1;
if (!bVar1) {
memcpy(gpNetBroadcastPacket2,(undefined4 *)*param_2,(uint)*(ushort *)(param_2 + 1));
iVar2 = 0;
gwNetBroadcastEnd = gwNetBroadcastEnd + *(ushort *)(param_2 + 1);
gpNetBroadcastPacket2 = (dword *)((int)gpNetBroadcastPacket2 + (uint)*(ushort *)(param_2 + 1));
}
return iVar2;
}
processNetBroadcast
->processNetBroadcastInternal
In case of data packet type (0x50), the packet data should be copied on the stack buffer with the size of 0x20 bytes.
void processNetBroadcastInternal(undefined4 param_1,char i_dPacketSize,char *i_pPacket)
{
...
iVar6 = FUN_00068fa6();
if (iVar6 == 1) {
if (cRamfedf9834 == '\x01') {
if (*i_pPacket == 0x50) {
_local_30 = 0;
uStack_2c = 0;
uStack_28 = 0;
uStack_24 = 0;
pdVar7 = &local_38;
dID = *(undefined4 *)(i_pPacket + 8);
local_38 = 0;
local_34 = 0;
uVar5 = (uint)(byte)(i_dPacketSize - 0xdU);
uVar8 = 0;
if (uVar5 != 0) {
pcVar9 = i_pPacket + uVar5 + 0xc;
do {
cVar1 = *pcVar9;
pcVar9 = pcVar9 + -1;
uVar8 = uVar8 + 1;
*(char *)pdVar7 = cVar1; // here copied on stack
pdVar7 = (dword *)((int)pdVar7 + 1);
} while (uVar8 < uVar5);
}
iVar6 = FUN_0005f360(dID,&local_38,i_dPacketSize - 0xdU,i_pPacket[0xc]);
if (-1 < iVar6) {
return;
}
...
}
However, the previous step allows a transfer request with a data length that can exceed 0x20 bytes. As a result, a stack-based buffer overflow occurs.
Credits
The vulnerability was identified by Radu Motspan from PCAutomotive Security Assessment team.
CVE-2025-32059, CVE-2025-32061, CVE-2025-32062: Multiple stack overflows in Bluetooth stack of infotainment ECU leading to RCE
Description
Notice: The vulnerabilities CVE-2025-32059, CVE-2025-32061 and CVE-2025-32062 are similar.
Hands-Free control (HFP) is the entity responsible for Hands-Free unit specific control signalling; this signalling is an AT command based. HFP is one of L2CAP Upper Layers usually named as Profiles.
Hands-free profile architecture
A common scenario would be a mobile phone used together with a wireless headset or a Head Unit. The headset will connect to the mobile phone and can be used to place and receive phone calls. The HFP defines two roles, that of an Audio Gateway (HFP-AG) and a Hands-Free unit:
- Audio Gateway (AG) – This is the device that is the gateway of the audio, both for input and output. Typical devices acting a- s Audio Gateways are cellular phones.
- Hands-Free unit (HF) – This is the device acting as the Audio Gateway’s remote audio input and output mechanism. It also provides some remote control means.

Hands-free protocol stack
Therefore, HFP is used to mirror the audio from the AG to HF, plus some extra controlling messages to manage the communication process. Upon a user action or an internal event, either the HF or the AG may initiate a Service Level Connection establishment procedure. A Service Level Connection establishment requires the existence of a RFCOMM connection, that is, a RFCOMM data link channel between the HF and the AG.
When an RFCOMM connection has been established, the Service Level Connection Initialization procedure shall be executed. First, in the initialization procedure, the HF shall send the AT+BRSF=<HF supported features>
command to the AG to both notify the AG of the supported features in the HF, as well as to retrieve the supported features in the AG using the +BRSF
result code.

Service level connection establishment
HFP supports various standard AT commands in correspondence to Hands-Free Profile Bluetooth® Profile Specification. However, some vendors might add extra AT commands which are not included in the specification. Nissan Leaf infotainment ECU is not an exception here. A few additional AT commands were discovered such as +ANDROID, +APLSIRI, +APLNRSTAT
and others.
PCAutomotive specialists believe that these commands are somehow related to the mobile Nissan application.
One of this custom AT command contains multiple buffer overflow vulnerabilities that may lead to RCE (Remote Code Execution) on the infotainment system:
The result of exploitation and gaining a reverse root shell from the IVI
The Bluetooth stack on Nissan Leaf infotainment ECU is implemented in several libraries and executable files.
libevo_stack.so
: The actual low-level Bluetooth stack implementation. Reading data from the baseband on top of UART bus and the further parsing.libevo_btappl.so
: Application-level library that uses libevo_stack.so to issue low-level procedures such as sending SDP requests.evo_hli_socket
: Executable file that starts the BT stack.evo_genivi_bt
: Somehow connected with evo_hli_socket. This file wasn't fully analyzed.- Other libraries.
HFP functionality is in libevo_stack.so
library. The initialization part of the HFP profile will be omitted here as irrelevant to the vulnerabilities presented.
When a new data portion is received, HF_ReceiveDataInd
is executed as an indicator of the incoming data that must be processed.
int __fastcall HF_ReceiveDataInd(RfDlc *dlc, BtvBuf *pkt)
{
......
if ( v11 )
{
len = (unsigned __int16)(*(_WORD *)(v11 + 16) + pkt->len);
v13 = j_BT_pAllocBuffer(len, 1); //
// if it's the first HF frame in the packet, allocate
// a buffer of the corresponding size
v14 = (unsigned __int8)target[0];
v13->pDataCur = v13->data;
v15 = v13;
v16 = &dword_48657E14[133 * v14];
v17 = (char *)memcpy(v13->data, *(const void **)(v16[127] + 12), *(unsigned __int16 *)(v16[127] + 16));
memcpy(&v17[*(unsigned __int16 *)(v16[127] + 16)], pkt->pDataCur, pkt->len);
j_BT_vFreeBuffer(pkt);
j_BT_vFreeBuffer((BtvBuf *)dword_48657E14[133 * (unsigned __int8)target[0] + 127]);
v4 = (unsigned __int8)target[0];
v5 = 4 * (unsigned __int8)target[0];
v6 = (unsigned __int8)target[0] << 7;
dword_48657E14[v5 + 127 + v6 + (unsigned __int8)target[0]] = v10;
}
else
{
len = pkt->len;
v15 = pkt;
}
j_BTOS_UnlockMutex(28, dword_48657E14[v5 + 108 + v6 + v4]);
j_GPP_GetParse4HFP((size_t (__fastcall **)(char *, unsigned __int8 *, size_t))&target[1]);
if ( !*(_DWORD *)&target[1] )
goto LABEL_20;
v18 = (*(int (__fastcall **)(RfDlc *, unsigned __int8 *, unsigned int))&target[1])(dlc, v15->pDataCur, len);//
// HF_ParseRsp
......
}
The incoming frame is copied to the buffer allocated via j_BT_pAllocBuffer.
Then, when a frame is ready, HF_ParseRsp
is called to parse the received data. The mentioned HF_ParseRsp
is responsible for AT commands responses handling contained in the packet. The buffer overflow vulnerability is in the custom +ANDROID
AT command. Here is the code snippet with the relevant parts of the code:
size_t __fastcall HF_ParseRsp(RfDlc *dlc, unsigned __int8 *rxbf, size_t rxlen)
{
...
unsigned __int16 probe_lens[2]; // [sp+4Ch] [bp-D4h] BYREF
...
size_t params[10]; // [sp+8Ch] [bp-94h] BYREF
char v86[68]; // [sp+B4h] [bp-6Ch] BYREF
.............
if ( j_CmpBuffer(rxbf + 2, "+ANDROID:") )
{
if ( rxbf != (unsigned __int8 *)-11 )
{
v30 = rxbf + 10;
v31 = 0;
space_len = 0;
do
{
v33 = *++v30;
++v31;
if ( v33 != ' ' )
break;
space_len = (unsigned __int8)v31;
}
while ( cmdLenAdv - 11 > v31 );
v34 = j_CmpBuffer(&rxbf[space_len + 11], "probe");
if ( v34 )
{
v35 = (unsigned __int8)(space_len + 17);
probe_params = 0;
probe_bf = &rxbf[v35];
proble_len = cmdLenAdv - v35;
v78 = 0;
probe_lens[0] = 0;
probe_lens[1] = 0;
memset(tmp_params, 0, 13);
memset(params, 0, 13);
LOWORD(tmp_lens) = 0;
v80 = 0;
if ( j_AC_IsOpenLayer(32) )
j_AlpsCloud_LogPrint(2u, 32829, 7395, 2, 0);
j_BT_LocalTraceLogPrintEx(1, 0x803D1CE3, 32, 8, 0, 0);
if ( !probe_bf )
{
v13 = 1;
j_BT_LocalTraceLogPrintEx(16, 0x803D1CE6, 32, 4, 1u, 0);
goto LABEL_27;
}
v37 = j_GetParameters(
probe_bf,
(unsigned __int16)(proble_len - 2),
&probe_params,
probe_lens,
2u);
switch ( v37 )
{
case 0:
LOWORD(v38) = 7405;
goto LABEL_150;
case 2: // we have 2 parameters
if ( (unsigned int)probe_lens[1] - 2 <= 0xC )
{
v40 = probe_lens[0];
memcpy(params, probe_params, probe_lens[0]);//
// BUG: stack buffer overflow
// no boundary checks on the first param
............
}
+ANDROID
might contain various sub-commands such as probe
, handshake
, audiosource
and vds
. The vulnerabilities CVE-2025-32059, CVE-2025-32061 and CVE-2025-32062 exist during processing subcommands probe
, handshake
, audiosource
and vds
correspondently. The vulnerability in probe sub-command will be described.
After the command response +ANDROID
is encountered, all the space characters are skipped. Then, a sub-command string is validated. If it is probe, then proceed to the parameters reading. Parameters are named variables having the following format:
\r +ANDROID <sub-command>="parameter 0","parameter 1",<...>
Double quotes are optional. j_GetParameters
function reads the parameters presented in the sub-command and returns them as the two buffers: probe_params
and probe_lens
. The former contains the pointers to the parameters in the sub-command strings, the latter is their corresponding lengths. Notice that lengths are stored in uint16_t
type, therefore one parameter might be up to 65535 bytes in length.
For a reference, there is the implementation of j_GetParameters
:
int __fastcall GetParameters(
unsigned __int8 *rxbf,
int rxlen,
_DWORD *params,
_WORD *lens,
unsigned __int16 max_param_cnt)
{
bool tmp; // zf
int v10; // r3
unsigned __int8 *curbf; // r0
char param_brkt_cnt; // r9
int param_cnt; // r1
int flag_add; // r12
unsigned __int8 *endbf; // lr
unsigned __int8 *param; // r7
int chr; // t1 MAPDST
unsigned __int16 param_cnt_adv; // r2
int v20; // r2
if ( j_AC_IsOpenLayer(32) )
j_AlpsCloud_LogPrint(2u, 32829, 5851, 2, 2u);
j_BT_LocalTraceLogPrintEx(1, 0x803D16DB, 32, 8, 0, 2u);
tmp = lens == 0;
if ( lens )
tmp = params == 0;
v10 = tmp;
if ( !rxbf )
v10 |= 1u;
if ( v10 )
{
j_BT_LocalTraceLogPrintEx(16, 0x803D16DF, 32, 1, rxlen, 0);
return 0;
}
else
{
if ( !rxlen )
return 0;
curbf = rxbf;
*params = 0;
param_brkt_cnt = 0;
*lens = 0;
param_cnt = 0;
flag_add = 1;
endbf = &curbf[(unsigned __int16)(rxlen - 1) + 1];
while ( 1 )
{
param = curbf;
chr = *curbf++;
if ( chr == 0x22 ) // '"'
break;
if ( chr != 0x2C ) // ','
goto add_chr;
param_cnt_adv = param_cnt + 1;
if ( (param_brkt_cnt & 0xFD) != 0 ) //
// first of all we need to encounter a " symbol to start
// counting the length of a parameter
{
add_chr:
tmp = flag_add == 1;
flag_add = 0;
++lens[param_cnt];
if ( tmp )
params[param_cnt] = param;
if ( curbf == endbf )
return (unsigned __int16)(param_cnt + 1);
}
else
{
param_cnt = param_cnt_adv;
flag_add = 1;
param_brkt_cnt = 0;
tmp = max_param_cnt == param_cnt_adv;
v20 = param_cnt_adv;
if ( tmp )
return 0;
params[param_cnt] = 0;
lens[v20] = 0;
if ( curbf == endbf )
return (unsigned __int16)(param_cnt + 1);
}
}
++param_brkt_cnt;
goto add_chr;
}
}
When parameters are read, they are ready to be copied into params
variable which is a buffer located in the stack frame of the current HF_ParseRsp
function. There are no boundaries check when the copying takes place, hence an attacker can overflow stack buffer and overwrite the return value of the stack frame. In libevo_stack.so
there are no stack canaries presented as a security measure to prevent such kind of attack. Eventually, an attacker can take the execution control and spawn a remote shell.
Such an attack requires an attacker to be paired with the head unit prior to the HFP channel communication. In the case of Nissan Leaf, the pairing process can be accomplished entirely unnoticed by the end-user with no interactions except clicking the Add New
button in the Bluetooth
submenu.
Credits
The vulnerabilities were identified by Mikhail Evdokimov from PCAutomotive Security Assessment team.
CVE-2025-32060: Absence of Linux kernel module signature verification on infotainment ECU
Description
Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system. Nissan Leaf infotainment ECU has Linux as its main Operating System, therefore kernel modules uploading is available there.
The kernel module signing facility cryptographically signs modules during installation and then checks the signature upon loading the module. This allows increased kernel security by disallowing the loading of unsigned modules or modules signed with an invalid key. Module signing increases security by making it harder to load a malicious module into the kernel. The module signature checking is done by the kernel so that it is not necessary to have trusted user-space bits.
The module signing facility is enabled using CONFIG_MODULE_SIG
kernel config line. Due to absence of the kernel module signing facility, a malicious user that have a code execution on behalf of root user, can upload custom kernel modules affecting the entire system.
As an example, an attacker can disable exchnd
built-in kernel module to obtain a reliable debugging feature on the system. This can be done by uploading a custom kernel module that will call an exit
function of exchnd
module, thus disabling it completely.
dmsg with the exchnd module removed log message
Credits
The vulnerability was identified by Mikhail Evdokimov from PCAutomotive Security Assessment team.
CVE-2025-32063: Enabling SSH server on infotainment ECU
Description
There is an ALD service in the infotainment ECU manufactured by BOSCH. Inside, this service can turn off security measures that are legitimately designed by the developers. This functionality can be achieved using a private RSA key. But in this case, using a vulnerability in the configuration, an attacker can force the system to disable the firewall and start the SSH server during the start process. An attacker should have local access to the IVI beforehand.
Initially, all the incoming connections are restricted by the firewall, which is started according to the following config:
[Unit]
Description=Firewall configuration
DefaultDependencies=no
## dynamic partition is needed because of below Condition statements
After=pretty-early.target rbcm-mount-dynamic.target
OnFailure=firewall-emergency.service
#none of the following files should exist
ConditionPathExists=|/var/opt/bosch/dynamic/ald/FWdisabled
ConditionPathExists=|/var/run/ald/FWdisabled
[Service]
RemainAfterExit=yes
Type=oneshot
ExecStart=/etc/network/setup_firewall.sh /etc/network/iptables_cache/ipt_productive.v4 /etc/network/iptables_cache/ipt_productive.v6
[Install]
RequiredBy=firewall.target
Omitting the comments, the firewall will start only if both files do not exist. Otherwise, the firewall will be disabled:
root@MYCAR:~## iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
In the folder /lib/systemd/system
there is a service sshd@.service
registered with the following description:
[Unit]
Description=OpenSSH Per-Connection Daemon (AIVI)
## as the service depends on existing files, the partition need to be available
After=syslog.target rbcm-mount-dynamic.target ald_once.service tty-ssh-checker.service
Wants=tty-ssh-checker.service
DefaultDependencies=no
## this service is protected by ALD!
## it only starts, if FEATURE is either enabled permanently or (usage of |) temporarily
ConditionPathExists=|/var/run/ald/SSHenabled
ConditionPathExists=|/var/opt/bosch/dynamic/ald/SSHenabled
## the existence of the ald folders is ensured by the dependency to ald.service
## in disabled state, the ald folders contain only FEATdisabled files.
## in virgin startup, the ald-folders do not contain FEATdisabled files.
## on update startup, the dyn/ald-folder contains no FEATxxabled files, then, the service shall not start, too.
## NOTE: the ideal of negative logic, referring to "disabled" files does not work out fine, as for features that are temporarily enabled, the dynamic partition contains a "disabled" file.
[Service]
Environment="SSHD_OPTS="
EnvironmentFile=-/etc/default/ssh
ExecStart=-/usr/sbin/sshd -i $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
StandardInput=socket
StandardError=syslog
#########################
## ADIT add-on for debugging:
##
## Specifies whether to send SIGKILL to remaining processes after a
## timeout, if the normal shutdown procedure left processes of the
## service around. Takes a boolean value.
## Defaults to "yes".
#SendSIGKILL=no
## Specifies how processes of this service shall be killed. One of
## control-group, process, none.
## Defaults to "control-group"
## "control-group" all remaining processes in the control group will be terminated
## "process" only the main process itself is killed
## "none" no process is killed.
KillMode=process
According to it, the service will be started if the file/var/opt/bosch/dynamic/ald/SSHenabled
exists.
In file /bin/ald_prepRootLogin.sh
, there is a check for file /var/opt/bosch/dynamic/ald/rootLogindisabled
existence:
#!/bin/bash
## assumption: dynamic partition target reached in systemd!
## assumption: an /etc/shadow, there exists a second shadow file - allowing root
## assumption: the shadow in productive configuration is the default, the rooted one will be overloaded by mount -B.
## assumption: in dynamic partition: existence of rootLoginenabled indicates an unlocked root login
## assumption: in dynamic partition: existence of rootLogindisabled indicates a locked root login
## assumption: in dynamic partition: no rootLoginxxx file => assume an update occured or the firmware is fresh => ALD-udpate will trigger the ALD to repair its state (update) or set it to open (initial flash) => start with locked root login.
## sideeffect: if ALD not started/existing, root login does not get unlocked
## workarounds: TTY can be used to open root login temporarily by "mount -B /etc/shadow /etc/"
## or TTY can be used to "touch /var/opt/bosch/dynamic/ald/FWenabled"
## before start of firewall service, update default settings, use bind-mount
## if dyn/FEATUREdisabled exists, it is disabled, i.e. normal productive config, i.e. best performance needed.
if [ ! -f /var/opt/bosch/dynamic/ald/rootLogindisabled ] || [ -f /var/run/ald/rootLoginenabled ] ; then
## note: FEATUREdisabled indicates FEATURE (root login) is not enabled, i.e. FEATURE is productive/locked/disabled
#For reason of a systemd-service restart: check whether productive rules are already overloaded! If yes, remove the mounts!
mount=`grep "/etc/shadow" /proc/mounts`
if [ -n "${mount}" ]; then
/bin/umount /etc/shadow
fi
## note: missing FEATUREdisabled means root-login-unlock is maybe enabled <=> root login is maybe in unlocked state, or target ALD status is "not yet defined"
if [ -f /var/opt/bosch/dynamic/ald/rootLoginenabled ] || [ -f /var/run/ald/rootLoginenabled ]; then
## now we are sure, the root login unlock is enabled
/bin/mount -B /etc/shadow_w_root /etc/shadow
## else
## target is in ALD status "not yet defined"
## root login unlock is not disabled, nor enabled => original flashing or image.
## root login should be productive first.
## ALD is responsible to open the root login with virgin level change.
fi
fi
Therefore, performing the following actions the SSH server can be accessed from a Wi-Fi network:
rm /var/opt/bosch/dynamic/ald/SSHdisabled
rm /var/opt/bosch/dynamic/ald/rootLogindisabled
touch /var/opt/bosch/dynamic/ald/SSHenabled
touch /var/opt/bosch/dynamic/ald/rootLoginenabled
rm /var/opt/bosch/dynamic/ald/FWdisabled
The preceding description is true for an older version of the Nissan Leaf ZE1 - 2020 IVI (for example version 283c36786r). For a modern firmware (version 283c30861e), the service tty-ssh-checker.service
was added. Internally, the service calls /bin/tty_ssh_level_recheck.sh
script:
#!/bin/bash
Marker_Path=/var/opt/bosch/dynamic/ald
ALD_Level=$(dbus-send --system --dest=com.adit.de.ALD --print-reply /com/adit/de/ALD/level_status org.freedesktop.DBus.Properties.Get string:'com.adit.de.ALD.level_status' string:'level' | sed -n '${s/.* //; p}')
if [ ${ALD_Level} -le 30 ];
then
if [ -f ${Marker_Path}/TTYenabled ];
then
rm ${Marker_Path}/TTYenabled
touch ${Marker_Path}/TTYdisabled
fi
fi
if [ ${ALD_Level} -lt 30 ];
then
if [ -f ${Marker_Path}/SSHenabled ];
then
rm ${Marker_Path}/SSHenabled
touch ${Marker_Path}/SSHdisabled
fi
fi
sync
exit 0
As a result, sshd
service does not start.
The presented patch can be bypassed, by creating a symbolic link SSHdisabled
-> SSHenabled
using the following command:
ln -s SSHenabled SSHdisabled
Eventually, in the script /bin/tty_ssh_level_recheck.sh
, it first deletes SSHenabled
and then performs touch SSHdisabled
. The latter transforms into touch SSHenabled
; therefore, this file will be created again.
Hence, if an attacker adds the controlled Wi-Fi network to the list of favourite Wi-Fi networks, the IVI will connect to it during the startup. After that, an attacker will be able to access the SSH server launched by the IVI after reboot.
Credits
The vulnerability was identified by Radu Motspan from PCAutomotive Security Assessment team.
CVE-2017-7932: Stack Overflow in HAB of i.MX 6 leading to the Secure Boot Bypass on infotainment ECU
Description
The infotainment ECU manufactured by BOSCH is vulnerable to the known bug CVE-2017-7932, which was found by QuarksLab and fully detailed in their blog.
Proof-of-Concept for this vulnerability can be found in usbarmory project.
During the secure boot process, boot arguments from DTB (Device Tree Blob) are passed to the Linux kernel as command line. Further, dm-verity sub-system is used to mount root filesystem. Before loading, the filesystem is checked using the hashes in kernel command line.
Using this vulnerability, an attacker can create a fake DTB on mmcblk1boot0 that bypasses the sign mechanism and modify boot arguments from DTB passed to the Linux kernel. This leads to disabling dm-verity mechanism and modifying the filesystem. Finally, this step allows an attacker to gain persistence on the infotainment ECU.
Through the following value of boot arguments attacker can disable verity checks:
bootargs3 = "console=ttymxc3,115200n8 root=/dev/dm-0 dm=\"vroot,,rw, 0 6262736 verity 1 179:26 179:26 4096 4096 … sha256 … 1 ignore_corruption\" rootwait ro maxcpus=4 printk.time=1 ip=off consoleblank=0 lpj=7905280 quiet oops=panic panic=1 vmalloc=384m dm_verity.prefetch_cluster=131072 dmwait";
Patched version of DTB should be written to /dev/mmcblk1boot0
using the following commands:
echo 0 > /sys/block/mmcblk1boot0/force_ro
dd if=/tmp/mmcblk1boot0_poc of=/dev/mmcblk1boot0 bs=4096 count=8160
echo 1 > /sys/block/mmcblk1boot0/force_ro
After system rebooting, root filesystem can be remounted with write privileges and modified:

Modification of the root file system of the IVI Linux OS
CVE-2017-9832: Integer Overflow leading to code execution in processing MTP on infotainment ECU
Description
The infotainment ECU manufactured by BOSCH is vulnerable to the known bug CVE-2017-9832.
The Media Transfer Protocol (MTP) is an extension to the Picture Transfer Protocol (PTP) communications protocol that allows media files to be transferred automatically to and from portable devices. Media Transfer Protocol allows the transfer of music files on digital audio players and media files on portable media players, as well as personal information on personal digital assistants. According to its specification, the main purpose of MTP is to facilitate communication between media devices with transient connection.
The media player functionality is implemented in executable file /opt/bosch/mediaplayer/bin/ccamediaplayer_out.out
that has various library dependencies. MTP is one of such a library that implements its core functionality and resides in /usr/lib/libmtp.so. libmtp
is an open-source library that implements an MTP initiator, which means it initiate MTP sessions with devices. The devices responding are known as MTP responders. libmtp runs on something with a USB host controller interface, using libusb to access the host controller.
There is a known integer overflow vulnerability under the ID of CVE-2017-9832 which is also present in libmtp.so
in the IVI firmware. An integer overflow vulnerability in ptp-pack.c (ptp_unpack_OPL function) of libmtp
(version 1.1.12 and below) allows attackers to cause a denial of service (out-of-bounds memory access) or remote code execution by inserting a mobile device into a personal computer through a USB cable.
PCAutomotive specialists were able to develop a trigger that takes advantage of the vulnerability and crashes the Head Unit. Furthermore, the resulting trigger could be improved to obtain code execution on the IVI. This was confirmed on test Raspberry Pi device (setup for debugging), which uses the same version of libmtp.so
The vulnerability is triggered automatically when the HU examines the MTP objects tree of the device connected through USB cable.
get_all_metadata_fast
command gets all handles and stuff by FAST directory retrieval which is available by getting all metadata for object 0xffffffff
which simply means "all metadata for all objects". This works on the vast majority of MTP devices (there are exceptions!) and is quite quick.
Each object has a metadata information stored in the property list. The mentioned data is retrieved in ptp_mtp_getobjectproplist
.
uint16_t
ptp_mtp_getobjectproplist (PTPParams* params, uint32_t handle, MTPProperties **props, int *nrofprops)
{
uint16_t ret;
PTPContainer ptp;
unsigned char* opldata = NULL;
unsigned int oplsize;
PTP_CNT_INIT(ptp);
ptp.Code = PTP_OC_MTP_GetObjPropList;
ptp.Param1 = handle;
ptp.Param2 = 0x00000000U; /* 0x00000000U should be "all formats" */
ptp.Param3 = 0xFFFFFFFFU; /* 0xFFFFFFFFU should be "all properties" */
ptp.Param4 = 0x00000000U;
ptp.Param5 = 0xFFFFFFFFU; /* means - return full tree below the Param1 handle */
ptp.Nparam = 5;
ret = ptp_transaction(params, &ptp, PTP_DP_GETDATA, 0, &opldata, &oplsize);
if (ret == PTP_RC_OK) *nrofprops = ptp_unpack_OPL(params, opldata, props, oplsize);
if (opldata != NULL)
free(opldata);
return ret;
}
The variable opldata
is a byte blob fetched from the connected USB device controlled by an attacker. In that case, in ptp_unpack_OPL
function which is called at the end of ptp_mtp_getobjectproplist
, the integer overflow happens:
static inline int
ptp_unpack_OPL (PTPParams *params, unsigned char* data, MTPProperties **pprops, unsigned int len)
{
uint32_t prop_count = dtoh32a(data); // [1]
MTPProperties *props = NULL;
int offset = 0, i;
if (prop_count == 0) {
*pprops = NULL;
return 0;
}
ptp_debug (params ,"Unpacking MTP OPL, size %d (prop_count %d)", len, prop_count);
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
props = malloc(prop_count * sizeof(MTPProperties)); // [2]
if (!props) return 0;
for (i = 0; i < prop_count; i++) {
if (len <= 0) {
ptp_debug (params ,"short MTP Object Property List at property %d (of %d)", i, prop_count);
ptp_debug (params ,"device probably needs DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST_ALL", i);
ptp_debug (params ,"or even DEVICE_FLAG_BROKEN_MTPGETOBJPROPLIST", i);
qsort (props, i, sizeof(MTPProperties),_compare_func);
*pprops = props;
return i;
}
props[i].ObjectHandle = dtoh32a(data); // [3]
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
props[i].property = dtoh16a(data); // [3]
data += sizeof(uint16_t);
len -= sizeof(uint16_t);
props[i].datatype = dtoh16a(data); // [3]
data += sizeof(uint16_t);
len -= sizeof(uint16_t);
offset = 0;
ptp_unpack_DPV(params, data, &offset, len, &props[i].propval, props[i].datatype);
data += offset;
len -= offset;
}
qsort (props, prop_count, sizeof(MTPProperties),_compare_func);
*pprops = props;
return prop_count;
}
uint32_t
value is obtained from the attacker's provided data. There are no integer validations presented in the function.- Property list is allocated as the multiplication of
prop_count
and the size ofMTPProperties
structure. Due to the absence of validations, the integer overflow vulnerability takes place here. It leads to an allocated chunk of a less size than is expected. - Finally, after the allocation, the function iterates over the property list passed to the function and assigns the controlled header values to the allocated chunk. If the target chunk's size is less than the property list size, the heap buffer overflow can happen by the property objects.
Vulnerability chaining
The following figure summarizes the impact achieved and the way how identified vulnerabilities can be chained with each other. Arrows identify vulnerabilities, while rectangles describe achieved impact.

Vulnerability chaining and impact
Vulnerabilities CVE-2025-32059, CVE-2025-32061, CVE-2025-32062 and CVE-2017-9832 allow a would-be attacker to gain root access to the infotainment system either via Bluetooth or USB. With those privileges, a would-be attacker can utilize CVE-2025-32058 to achieve complete control over the RH850 CPU and send arbitrary commands to in-vehicle CAN bus. When combined with CVE-2017-7932 and CVE-2025-32063, this chain becomes even more dangerous, as a would-be attacker will have persistent control over the vehicle and will be able to trigger an attack compromising vehicle owner’s safety at any arbitrary moment in time, even if the IVI ECU will be turned off and on again.
Related publications
PCA made a talk about discovered vulnerabilities at BlackHat Asia 2025.
- Slides are available here about Asia 25/Asia 25 Evdokimov Remote Exploitation of Nissan Leaf
Article tags
nissan
vehicle penetration testing
bosch
infotainment system vulnerability
Latest Advisories
November 22, 2023
Popular tags
security advisory
skoda
pcautomotive
nissan
vehicle penetration testing
bosch
infotainment system vulnerability
vw
mib3 infotainment unit
preh car connect gmbh
Credits
Polina Smirnova
Senior Security Researcher
Radu Motspan
Senior Security Researcher
Mikhail Evdokimov
Senior Security Researcher