Talos Vulnerability Report

TALOS-2022-1646

Mitsubishi Electric Corporation MELSEC iQ-FX5U webserver session identifier generation authentication bypass vulnerability

January 18, 2023
CVE Number

CVE-2022-40267

SUMMARY

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.

CONFIRMED VULNERABLE VERSIONS

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

PRODUCT URLS

MELSEC iQ-F FX5U - https://www.mitsubishielectric.com/fa/products/cnt/plcf/items/index.html

CVSSv3 SCORE

7.1 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N

CWE

CWE-342 - Predictable Exact Value from Previous Values

DETAILS

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.

TIMELINE

2022-10-27 - Initial Vendor Contact

2022-11-02 - Vendor Disclosure

2023-01-17 - Vendor Patch Release

2023-01-18 - Public Release

Credit

Discovered by Matt Wiseman of Cisco Talos.