WHITE PAPER ## Industroyer vs. Industroyer2: Evolution of the IEC 104 Component AUTHORS ##### Giannis Tsaraias Ivan Speziale ----- ## About Nozomi Networks Labs ###### Nozomi Networks Labs is dedicated to reducing cyber risk for the world’s industrial and critical infrastructure organizations. Through its cybersecurity research and collaboration with industry and institutions, it helps defend the operational systems that support everyday life. The Labs team conducts investigations into industrial device vulnerabilities and, through a responsible disclosure process, contributes to the publication of advisories by recognized authorities. To help the security community with current threats, they publish timely blogs, research papers and free tools. The Threat Intelligence and Asset Intelligence services of Nozomi Networks are supplied by ongoing data generated and curated by the Labs team. To find out more, and subscribe to updates, visit nozominetworks/labs ----- ## Table of Contents ###### 1. Introduction to Industroyer & Industroyer2 4 2. Industroyer & Industroyer2: The Evolving Source Code 5 2.1 Breaking Down the Samples 5 2.2 v2 Station Configuration 6 2.3 v2 IOA Configuration 7 2.4 v2 Command-line Parameters 8 2.5 v2 IEC 104 Interaction 9 2.6 Main Thread Spawning 12 2.7 TESTFR Frame Inserted in v2 13 2.8 Start/Stop Data Transfer Activation 14 2.9 Prepare/Send Station Command 15 2.10 Use of Streaming SIMD Extensions (SSE) Instructions 16 2.11 Parse_packet_and_log Function 16 3. Summary 18 4. Addendum: YARA Rule for Industroyer2 18 6. References and Related Reading 19 ----- ## 1. Introduction to Industroyer & Industroyer2 Industroyer2 is the latest evolution of the notorious malware that was first deployed by threat actor Sandworm in Ukraine in 2016. As documented by ESET, this new artifact was used in the context of a broader operation against Ukrainian organizations in 2022.[1] The Industroyer artifacts retrieved in 2016 consisted of components targeting multiple industrial control system (ICS) protocols, specifically: y IEC 60870-5-101, y IEC 60870-5-104, y IEC 61850, y OPC DA. Industroyer2, however, focuses only on IEC 60870-5-104 (IEC 104), which is just an update to the Industroyer component targeting the same protocol. This observation leads us to believe that, depending on the operational requirements, the threat actors’ implementation of these ICS protocols is part of a broader framework of capabilities that is selectively packaged into a specific deliverable. In this paper, Nozomi Networks Labs analyzes the Operational Technology (OT) capabilities of Industroyer2, discusses the major changes between Industroyer and Industroyer2, and analyzes how the codebase has evolved over time. A noteworthy characteristic of Industroyer deployments is the lack of any stealthy measures in the binaries. One plausible hypothesis is that the threat actor, having already compromised the target environment and performed advanced reconnaissance, is not concerned about potential security controls. A second hypothesis is that due to time constraints, the operators would not have time to simultaneously obfuscate their activity and improve their posture in the environment by the time of malware delivery. Given the resources and expertise of the threat actor, we believe this scenario to be less likely. Nevertheless, it is clear that Sandworm is not concerned about different Industroyer versions being attributed to the same actor through comparison of the target artifacts. The takeaway for security teams is that advanced threat actors are continuously refining their OT capabilities to adapt to different operational scenarios. In the current threat landscape it’s paramount to detect and respond to sophisticated attackers before they reach OT system—their ability to analyze the targeted environment and modify its status was demonstrated once more with Industroyer2. ----- ## 2. Industroyer & Industroyer2: The Evolving Source Code #### 2.1 Breaking Down the Samples In this section, we present a series of evidence that collectively and strongly supports the thesis that the two binaries, Industroyer and Industroyer2, were compiled from the same evolving source code. Throughout our analysis, we will refer to the first version of Industroyer as “v1”, which corresponds to sample ``` 7907dd95c1d36cf3dc842a1bd804f0db511a0f68f4b3 d382c23a3c974a383cad (104.dll). We will refer to ``` Industroyer2 as “v2”, which corresponds to sample ``` d69665f56ddef7ad4e71971f06432e59f1510a7194386 e5f0e8926aea7b88e00. ``` The screenshot below (Figure 1) compares similar functionalities in the binaries. The decompiled code of v1 is presented on the left while the matching part of v2 is shown on the right. **Figure 1 - Example comparison between Industroyer v1 (left) and v2 (right).** The syntax of the configuration is the most obvious visual difference between the two versions of the malware. However, this refactor is largely irrelevant for the internal structure of the executables. In both cases the configuration is normalized into a matching data structure, called main_config in our analysis, that is then used throughout the code. As described by ESET, Industroyer v1 uses a classic INI configuration file that is passed as an argument to the Crash export of 104.dll.[2] Meanwhile, the Industroyer v2 sample that we analyzed hardcodes its configuration inside the binary in the form of a string. Below, we present the possible properties for the hardcoded station and Information Object Address (IOA) configurations embedded in the analyzed binary. ----- #### 2.2 v2 Station Configuration The following screenshot (Figure 2) shows the first hardcoded station configuration embedded in the analyzed binary of v2. The sample embeds configurations for three different IP addresses in total. **Figure 2 - Station Configuration.** Below (Figure 3), we present the possible properties for the hardcoded station and IOA configurations embedded in the analyzed binary of v2. |Property|Acceptable Values|Purpose| |---|---|---| |Target IP|IP address|IP of the station to connect to| |Target port|Port number|Port of the station to connect to| |ASDU|Integer|Application Service Data Unit address| |Operation mode|Boolean|0 (Interaction with hardcoded IOA), 1 (Range mode)| |Switch for process manipulation|Boolean|0 (Disable), 1 (Enable)| |Reserved parameter|Boolean|-| |Process name|String|Name of the process to be killed| |Rename|Boolean|0 (Don't rename), 1 (Rename)| |Folder name|String|Folder name where the process targeted for killing and renaming is stored| |Sleep time in minutes|Integer|Initial sleep time, used to add a delay before interacting with a station| |Sleep time in seconds #1|Integer|Sleep time to use when Invert SCO/DCO On/Off is set| |Station index|Integer|Configuration station index to delay| |Sleep time in seconds #2|Integer|Sleep time before STOPDT for the previously used station index| |Initial SCO/DCO On/Off State|Boolean|0 (Initial state On), 1 (Initial state Off)| |Invert SCO/DCO On/Off|Boolean|If set, it will interact with each IOA again, with SCO/DCO On/Off inverted| |IOA count|Integer|Number of IOA following header| **Figure 3 - Target Configuration.** ----- #### 2.3 v2 IOA Configuration An IOA is used to address one specific piece of data within a station. IOA configurations typically differ from station to station. In the screenshot below (Figure 4) you can see the IEC-104 testbed traffic using the first station configuration. **Figure 4 - IEC 104 testbed traffic using first station configuration.** The table below (Figure 5) shows the configurable IOA properties. **Property** **Acceptable Values** **Purpose** IOA Integer Information Object Address Single/Double command Boolean 0 (Double command), 1 (Single command) SCO/DCO Select/Execute Boolean 0 (Execute), 1 (Select) SCO/DCO On/Off Boolean 0 (Off), 1 (On) Priority Integer Index Integer IOA entry index in the configuration list **Figure 5 - IOA Configuration.** |Property|Acceptable Values|Purpose| |---|---|---| |IOA|Integer|Information Object Address| |Single/Double command|Boolean|0 (Double command), 1 (Single command)| |SCO/DCO Select/Execute|Boolean|0 (Execute), 1 (Select)| |SCO/DCO On/Off|Boolean|0 (Off), 1 (On)| |Priority|Integer|-| |Index|Integer|IOA entry index in the configuration list| ----- #### 2.4 v2 Command-line Parameters While v1 included a separate component to load and launch payloads contained in different Dynamic-link Libraries (DLLs), the v2 sample provides the user with the ability to set certain command-line options. As shown in Figure 6, two command-line flags are supported by the v2 executable; namely, -o and -t. The -o flag can be used to store the execution output log into a file instead of printing it to standard output. The -t flag can be used to perform a delayed execution. For example, running the program with -t 10 as an argument at 1:08 PM will cause a time delay of approximately two minutes before the executable spawns its main thread at 1:10 PM. **Figure 6 - Command-line argument handling.** ----- #### 2.5 v2 IEC 104 Interaction After terminating PServiceControl.exe, and ``` PService_PPD.exe (based on the configuration) being ``` renamed with .MZ appended to its name, the v2 sample begins IEC 104 interaction. **Figure 7 - Process termination and file renaming.** ----- The default operation mode (0) set in the station configurations present in our sample produces the following series of commands: y TESTFR y STARTDT y C_IC_NA_1 (100) y For each IOA configuration: ‐ `C_SC_NA_1 (45) or C_DC_NA_1 (46) command,` depending on the Single/Double command field in the configuration y STOPDT If the operation mode is set to 1 instead, the sample expects to find a starting index and an ending index following the station configuration, which is then used as a range of IOAs to iterate through. In this case, the following series of commands are generated in our testbed: y TESTFR y STARTDT y C_IC_NA_1 (100) y For each IOA in the range start_index → end_index: ‐ `C_SC_NA_1 (45) with SCO Off and Execute` y STOPDT y TESTFR y STARTDT y C_IC_NA_1 (100) y For each IOA in the range start_index → end_index: ‐ `C_DC_NA_1 (46) with DCO Off and Select` ‐ `C_DC_NA_1 (46) with DCO Off and Execute` y STOPDT In Figures 8a and 8b, we show both Single and Double commands for range modes starting with 2 and ending with 9: **Figure 8a - Range mode with start index 2 and end index 9, Single commands.** ----- **Figure 8b - Range mode with start index 2 and end index 9, Single commands.** ----- #### 2.6 Main Thread Spawning The main thread of both samples contains the code responsible for issuing the malicious IEC 104 packets. In v1, the main thread is spawned from the Crash export, while in v2 the execution starts from the regular PE entry point. In both cases the configuration is parsed before reaching this stage (Figure 9). **Figure 9 - Main thread spawning.** Beginning with this function, the usage of a structure dubbed main_config in our decompilation (Figure 10), becomes pervasive throughout the codebase. In both the samples this structure operates as the main glue between the configuration and the rest of the code, independently from the configuration format used. The way in which main_config is used is a strong indicator of how the two executables were compiled from the same source code and updated over time. **Figure 9 -** `main_config structure definition.` ----- #### 2.7 TESTFR Frame Inserted in v2 ``` TESTFR frames in IEC 104 are used between the controlling ``` station and the controlled station to periodically check the status of a connection and eventually detect communication problems. After having established a TCP connection, Industroyer v1 begins emitting STARTDT frames. This marks the beginning of a data transfer from the controlling station to the controlled station. **Figure 11 - Main thread comparison.** Industroyer v2, instead, takes the extra step of sending a TESTFR frame as we can also observe in the traffic dump (Figure 12). **Figure 12 -** `TESTFR frame in Industroyer v2` ----- #### 2.8 Start/Stop Data Transfer Activation The functions responsible for creating and sending STARTDT and STOPDT frames are essentially the same across the two executables. We can spot minor differences in the way dynamic memory is allocated, but the only functional difference is a sleep timeout. In v1, it is customizable through the configuration, and in v2 is hardcoded to one second for both the functions. **Figure 13 -** `STARTDT frame creation.` We can also observe how the invocation of function ``` parse_received_packet varies slightly between v1 and v2 ``` of the malware. From a functional perspective, the most important update is the ability to reply to TESTFR activation commands with TESTFR confirmation frames. **Figure 14 -** `STOPDT frame creation.` ----- #### 2.9 Prepare/Send Station Command The function named in our decompilation as ``` iec104_prepare_and_send_station_command (Figure 15) ``` is found in both versions of the malware with similar semantics. Nevertheless, we can appreciate how in v2 the function can receive more IEC 104 parameters to properly customize the packet payload. **Figure 15 - Function iec104_prepare_and_send_station_command invocation.** A plausible reason for this v2 function is that at first the developers designed an abstraction that satisfied the initial requirements, which over time changed to incorporate more flexibility. This is also the general feeling that an analyst gets when assessing the evolution of this codebase. A first rough version is developed to achieve a specific goal and it eventually morphed into a full-fledged framework to surgically manipulate IEC 104 payloads. ----- #### 2.10 Use of Streaming SIMD Extensions (SSE) Instructions Some of the IEC 104 commands are assembled from a bytes template that is hardcoded in the binaries. The curiosity is that in v1 these bytes are handled with x86 SSE instructions, while in v2 regular non-SSE instructions are used instead. This is typically due to the threat actor choosing different optimization settings upon compilation (Figure 16). **Figure 16 - Different compiler optimizations between the v1 and v2.** #### 2.11 Parse_packet_and_log Function The function dubbed Parse_packet_and_log used in the malware provides some basic dissection of the packets received from the controlled station in response to the issued IEC 104 commands. We discovered an interesting typo introduced in Industroyer v2 (line 164) where the ``` STOPDT con string is logged rather than the correct STOPDT act, as found in Industroyer v1 (Figure 17). ``` Although this typo does not have functional consequences, it is an interesting artifact that can seldom be found in a refactored codebase. **Figure 17 - Function Parse_packet_and_log.** ----- There are a couple of functions used in Parse_packet_and_log which map a code (cause and typeid) to a verbose string description. For unknown reasons, the body of these functions has been removed from the v2 executable. It is extremely unlikely that this is due to the fear of being detected, as there are no such precautions throughout the malware. We speculate that this might be due to some pre processor directives. **Figure 18 - Function Parse_packet_and_log.** ----- ## 3. Summary We conducted a comparative analysis of the artifact known as Industroyer2 against the first deployment of the same toolkit. The evidence presented strongly suggests that the threat group is updating the codebase over time to meet operational requirements as they evolve. Additionally, we provided a thorough breakdown of the configuration format used by Industroyer2, illustrating the different options available to customize the behavior of the IEC 104 payload. Finally, we want to highlight a major difference between advanced threat actors and more ordinary adversaries. Sophisticated operators can not only compromise targets in-depth to reach the OT network, but they also have the technical capabilities to analyze the targeted environment and craft custom tools to manipulate OT systems. ## 4. Addendum: YARA Rule for Industroyer2 Below is a YARA rule for Industroyer2: ``` 1 // Created by Nozomi Networks Labs 2 3 rule industroyer2_nn { 4 5 meta: 6 author = "Nozomi Networks Labs" 7 name = "Industroyer2" 8 description = "Industroyer2 malware targeting power grid components." 9 actor = "Sandworm" 10 hash = "D69665F56DDEF7AD4E71971F06432E59F1510A7194386E5F0E8926AEA7B88E00" 11 12 strings: 13 $s1 = "%02d:%lS" wide ascii 14 $s2 = "PService_PPD.exe" wide ascii 15 $s3 = "D:\\OIK\\DevCounter" wide ascii 16 $s4 = "MSTR ->> SLV" fullword wide ascii 17 $s5 = "MSTR <<- SLV" fullword wide ascii 18 $s6 = "Current operation : %s" 19 $s7 = "Switch value: %s" 20 $s8 = "Unknown APDU format !!!" 21 $s9 = "Length:%u bytes |" 22 $s10 = "Sent=x%X | Received=x%X" 23 $s11 = "ASDU:%u | OA:%u | IOA:%u |" 23 $s12 = "Cause: %s (x%X) | Telegram type: %s (x%X)" 25 26 condition: 27 5 of them 28 } ``` ----- ## 6. References and Related Reading 1. **[“Industroyer2: Industroyer reloaded," ESET Research, April 12, 2022.](https://www.welivesecurity.com/2022/04/12/industroyer2-industroyer-reloaded/)** 2. **["WIN32/INDUSTROYER: A new threat for industrial control systems," Cherepanov, A., ESET Research, June 12, 2017.](https://www.welivesecurity.com/wp-content/uploads/2017/06/Win32_Industroyer.pdf)** ###### Related Reading y **["Industroyer2: Nozomi Networks Labs Analyzes the IEC 104 Payload," Nozomi Networks Labs, April 27, 2022.](https://www.nozominetworks.com/blog/industroyer2-nozomi-networks-labs-analyzes-the-iec-104-payload/)** y **["Cyberattack by Sandworm Group (UAC-0082) on Ukrainian energy facilities using malicious programs INDUSTROYER2 and](https://cert.gov.ua/article/39518)** **[CADDYWIPER (CERT-UA # 4435)," CERT-UA, April 12, 2022.](https://cert.gov.ua/article/39518)** ----- # Nozomi Networks ### The Leading Solution for OT and IoT Security and Visibility Nozomi Networks accelerates digital transformation by protecting the world’s critical infrastructure, industrial and government organizations from cyber threats. Our solution delivers exceptional network and asset visibility, threat detection, and insights for OT and IoT environments. Customers rely on us to minimize risk and complexity while maximizing operational resilience. © 2022 Nozomi Networks, Inc. All Rights Reserved. NN-WP-IND-VS-IND2-8.5x11-001 **nozominetworks.com** -----