CVE-2022-40267
An authentication bypass vulnerability exists in the webserver session identifier generation functionality of the Mitsubishi Electric Corporation’s MELSEC iQ-F FX5U v1.240. A specially crafted HTTP request can lead to session cookie leak. An attacker can send a series of HTTP requests to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Mitsubishi Electric Corporation MELSEC iQ-F FX5U v1.240
MELSEC iQ-F FX5U - https://www.mitsubishielectric.com/fa/products/cnt/plcf/items/index.html
7.1 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N
CWE-342 - Predictable Exact Value from Previous Values
The iQ-F FX5U is one of several members of the iQ-F series of Programmable Logic Controllers from Mitsubishi. The FX5U comes with built-in processor, power supply, ethernet and 16 I/O points. The PLC can be configured to host several network services, such as an HTTP Server, FTP Server, FTP Client, MODBUS/TCP interface and several Mitsubishi specific protocols.
The authentication flow for the web server begins with the user navigating to /system/Log-in.html
. On this page is a standard authentication prompt requesting a username and password. When those values are submitted, Javascript on the page initiates two POST requests, the first to /cgi/GetRndNum.cgi
and the second to /cgi/login.cgi
.
The first request to GetRndNum
is required to collect a 32-byte long random number that will be effectively used as a client-side salt. This request is unauthenticated and appears as follows:
POST /cgi/GetRndNum.cgi HTTP/1.1
Host: 192.168.3.250
Content-Length: 6
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: http://192.168.3.250
Referer: http://192.168.3.250/system/Log-in.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: REDIRECTURL=/system/Main.html
Connection: close
NUM=32
The response appears as follows:
HTTP/1.0 200 OK
Set-Cookie: SESSIONID=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; httponly;
Content-Type: application/json; charset=UTF-8
Content-Length: 97
X-XSS-Protection: 1; mode=block;
Content-Security-policy: reflected-xss block;
X-Frame-Options: SAMEORIGIN;
X-Content-Type-Options: nosniff;
{"RET":"0000","NO":"2","DATA":"19D88B218959E3EFD65B3EDBFB8E9A77C1B22DD2696EA5F5C78EE65E3AC3B6BD"}
Once the client has the 32 bytes of random data, it calculates the HMAC-SHA256(password, DATA)
and submits an authentication request with the supplied username, the resulting hash and the NO
field from the DATA response.
POST /cgi/login.cgi HTTP/1.1
Host: 192.168.3.250
Content-Length: 90
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Origin: http://192.168.3.250
Referer: http://192.168.3.250/system/Log-in.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: REDIRECTURL=/system/Main.html
Connection: close
USER=myusername&HASH=cf44b7fcc3abfd6c12b6736ff9f76eacdc250d3e8d141efb3461d308734768ac&NO=2
Within the /cgi/login.cgi
handler, the NO
field is used to fetch the 32 bytes of random data that were used by the client to calculate the HMAC-SHA256 hash. If the hashes match, then the user is successfully authenticated and a session identifier is generated and returned via the SESSIONID
cookie.
HTTP/1.0 200 OK
Set-Cookie: SESSIONID=77DE206B0FEAD9D2382F50B60F9EF1F2; path=/; httponly;
Set-Cookie: REDIRECTURL=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; httponly;
Content-Type: application/json; charset=UTF-8
Content-Length: 40
X-XSS-Protection: 1; mode=block;
Content-Security-policy: reflected-xss block;
X-Frame-Options: SAMEORIGIN;
X-Content-Type-Options: nosniff;
{"RET":"0000","LOC":"/system/Main.html"}
With the authentication flow understood, we now focus on how the random 32 bytes of DATA
returned from /cgi/GetRndNum.cgi
and the random SESSIONID
are correlated.
The function responsible for handling HTTP POST requests destined for /cgi/GetRndNum.cgi
is located at offset 0xffdf25a3
in firmware v1.240. Within this function is a call to a function we will refer to as generate_random_buffer
, which is located at offset 0xffdf1287
. This function’s signature is void generate_random_buffer(char n, int32_t *dest)
, and it will populate an array of n
integers located at dest
with random values. It does so using a linear congruential generator which is reseeded with a known value prior to the generation of the next random number.
A decompilation of the relevant random number generation functions are included below, with annotations:
void set_rand_seed(int32_t seed) {
g_rand_seed = seed;
}
int16_t rand() {
// Returns 15 of the upper 16 bits of the next integer in the LCG stream
g_rand_seed = g_rand_seed * m + c;
return g_rand_seed >> 0x10 & 0x7fff;
}
void generate_rand_buffer(char n, int32_t *dest) {
int rand_num;
if (n != 0) {
for (int i = 0; i < n; i++) {
[1] Note that the LCG is reseeded for each iteration from a global random seed buffer
set_rand_seed(*g_rand_seed_buff[i]);
[2] Generate a 32-bit value from multiple 15-bit values
rand_num = rand() + (rand() << 0xf) + (rand() << 0x1e);
[3] Note that future iterations of the LCG will be seeded with this result
g_rand_seed_buff[i] = rand_num;
dest[i] = rand_num;
}
}
}
Given that rand
generates 15 bits of random data, it is called three times (at [2]
) in order to generate a full 32 bits of random data. That value, stored in rand_num
, is moved to long-term storage (at [3]
) in the global g_rand_seed_buff
, which is an array of eight 32-bit integers. In future calls to generate_rand_buffer
, values from this global array will be used to seed the LCG (at [1]
). Knowledge of the contents of the global seed buffer would allow an attacker to synchronize themselves with the state of the RNG.
As indicated earlier, this generate_random_buffer
function is used by the /cgi/GetRndNum.cgi
function handler to generate 32 bytes of random data to be returned in the DATA
field. This function is also used by the HTTP session identifier generator after an authentication occurs successfully. The vulnerability arises from the fact that previous random values are used as seeds for future generated output, and that an unauthenticated user can cause /cgi/GetRndNum.cgi
to disclose random values, thereby providing the attacker with the seeds used to generate future random numbers. An unauthenticated attacker who submits a request to /cgi/GetRndNum.cgi
can use the provided DATA
value to predict future valid SESSIONID
values which could, at some point, become valid when an authentic user logs into the webserver.
2022-10-27 - Initial Vendor Contact
2022-11-02 - Vendor Disclosure
2023-01-17 - Vendor Patch Release
2023-01-18 - Public Release
Discovered by Matt Wiseman of Cisco Talos.