A few days ago a new variant of an ICS-capable malware known as Industroyer has been employed during a cyber-attack conducted against industrial control systems (ICS) responsible for the management and control of power plants subsystems in Ukraine.

This new version has been named Industroyer2 and represents the evolution of a malware family already observed in December 2016 working against similar targets. This type of attack was reported after a long series of other attacks targeting Ukrainian organizations and entities which generally involved the use of wipersbackdoors and DDoS attacks.

Its peculiarity is that it is in fact the first to have been clearly targeted ICS infrastructures and to employ ICS-capable malware.

INSIGHTS

I had a look at the file identified with the following characteristics:

TypeValue
MD57c05da2e4612fca213430b6c93e76b06
SHA1fdeb96bc3d4ab32ef826e7e53f4fe1c72e580379
SHA256d69665f56ddef7ad4e71971f06432e59f1510a7194386e5f0e8926aea7b88e00

This is a 37.00 Kb in size 32-bit x86 Portable Executable (PE) compiled on Wednesday 23 March 2022 10:07:29 as evidence reported following

Basically speaking, the malware is designed to read some embedded configuration entries in order to craft IEC-104 Application Service Data Unit (ASDU) messages that allow it to interact with the remote targeted units (reachable as internal IP addresses) over TCP protocol.

Taking a look at the execution flow, we can see that this piece of malware handles two optional parameters from the command line which are -t and -o as evidence from the code extraction below

-o allows to write a debug file on the filesystem (by default it’s printed on the console) while -t allows to delay execution by referencing the current SystemTime.

After parsing the parameters provided via the CLI, the malware starts to retrieve the information from the embedded configuration entries

These entries appear as the image following

Each configuration entry starts with an internal IP address (in the image above obscured in the last two octets) and a port, followed by four numbers (may be some sort of operating flags) a process name and a path (in this case D:\OIK\DevCounter).

The last chunck consists of information used to construct the message intended to be send to the ASDUs. For each of the entries found the malware goes to terminate the process PServiceControl.exe.

The process name is hardcoded within the executable.

At this point it goes to enumerate processes again in order to find the process name corresponding to the one specified in the configuration entry (PService_PPD.exe).

After recovering the path D:\OIK\DevCounter and the file name PService_PPD.exe it’s going to rename the file present under this path by adding the .MZ extension to it.

Most likely this is done to prevent any task designed for the process’s ‘always-on‘ from re-launching it. For each embedded configuration a thread is then created in order to implement IEC-104 communication protocol.

The IEC-104 extends the IEC-101 in a way that it can be transmitted over a TCP/IP network. So in each such thread the malware will attempt to communicate with the specified IP address using the protocol described in the IEC-104 standard after to have iterated through the ASDU entries in the configuration block.

In IEC-104 each object in the ASDU has an Information Object Address. Basically, the primary purpose of this action is to connect to the specified IP address and send crafted ASDU messages resulting in Information Object Address state being set to either ON or OFF.

INDUSTROYER vs INDUSTROYER2

By doing a comparative analysis of INDUSTROYER (its IEC-104 module) and INDUSTROYER2 it’s possibile to note a lot similarities above all in the operating logic despite the fact that the latest samples certainly show substantial differences if compared with 2016 ones.

Among the technical similarities at the code level it’s possibile to observe overlaps in the functions dedicated to the management, composition and control of ASDU messages.

The new executable in some cases joins together some strings in respect to previous sample optimizing in some cases the global execution process but in general the strings that refer to the debug messages are the same allowing old YARA rules to match the new sample as well.

According to the first first attribution of the family shared by ESET researchers and in the light of the further technical evidence extracted in this article, the threat in question is to be linked with the threat actor known as Sandworm (aka BlackEnergy).

CONCLUSIONS

I can definitely say that the Industroyer malware family is a sophisticated piece of malware used against industrial control systems (ICS). The findings reported here are in line with previous operations already attributed by the security community to Russian APT groups and to the best of my current knowledge this is the first ICS-capable malware used since the beginning of the conflict between Russia and Ukraine after a long series of attacks that have seen the use of wipersbackdoors, different early and middle-stage implants and (D)DoS attacks.

The use of this type of malware should alert about the potential of Russia-State linked APT groups to focus their efforts against industrial control systems (ICS) and their willingness to use this kind of “digital weapons” to cause disruption of critical services.

It’s important to consider the attackers could adapt this malware to other similar targets and that any intrusion against targets of this type should be considered extremely dangerous and complex to mitigate in relation to the presence of protocols born decades ago without taking the security factor into consideration.

DETECTION

YARA
rule Sandworm_Industroyer2_76222_00001 : RUSSIAN THREAT GROUP {
meta:
author = “Emanuele De Lucia”
description = “Strings-based threat detection rule for INDUSTROYER2”
tlp = “white”
date = “2022-04-15”
hash1 = “d69665f56ddef7ad4e71971f06432e59f1510a7194386e5f0e8926aea7b88e00”
strings:
$ = “PServiceControl.exe”
$ = “Sent=x%X | Received=x%X”
$ = “Cause: %s (x%X) | Telegram type: %s (x%X)”
$ = “Length:%u bytes | “
$ = “Unknown APDU format !!!”
$ = “%02hu:%02hu:%02hu:%04hu”
condition: (uint16(0) == 0x5a4d and all of them)
}