Mandiant recently responded to an incident at a critical infrastructure organization where an attacker deployed malware designed to manipulate industrial safety systems. The targeted systems provided emergency shutdown capability for industrial processes. We assess with moderate confidence that the attacker was developing the capability to cause physical damage and inadvertently shutdown operations. This malware, which we call TRITON, is an attack framework built to interact with Triconex Safety Instrumented System (SIS) controllers. We have not attributed the incident to a threat actor, though we believe the activity is consistent with a nation state preparing for an attack.
TRITON is one of a limited number of publicly identified malicious software families targeted at industrial control systems (ICS). It follows Stuxnet which was used against Iran in 2010 and Industroyer which we believe was deployed by Sandworm Team against Ukraine in 2016. TRITON is consistent with these attacks, in that it could prevent safety mechanisms from executing their intended function, resulting in a physical consequence.
Malware Family |
Main Modules |
Description |
TRITON |
trilog.exe |
Main executable leveraging libraries.zip |
library.zip |
Custom communication library for interaction with Triconex controllers. |
Table 1: Description of TRITON Malware
Incident Summary
The attacker gained remote access to an SIS engineering workstation and deployed the TRITON attack framework to reprogram the SIS controllers. During the incident, some SIS controllers entered a failed safe state, which automatically shutdown the industrial process and prompted the asset owner to initiate an investigation. The investigation found that the SIS controllers initiated a safe shutdown when application code between redundant processing units failed a validation check — resulting in an MP diagnostic failure message.
We assess with moderate confidence that the attacker inadvertently shutdown operations while developing the ability to cause physical damage for the following reasons:
- Modifying the SIS could prevent it from functioning correctly, increasing the likelihood of a failure that would result in physical consequences.
- TRITON was used to modify application memory on SIS controllers in the environment, which could have led to a failed validation check.
- The failure occurred during the time period when TRITON was used.
- It is not likely that existing or external conditions, in isolation, caused a fault during the time of the incident.
To know more about Fireeye’s good analysis, follow: https://www.fireeye.com/blog/threat-research/2017/12/attackers-deploy-new-ics-attack-framework-triton.html
If you are just looking for Indicators of Compromise:
First, a YARA rule made by nicholas.carr@itsreallynick:
rule TRITON_ICS_FRAMEWORK
{
meta:
author = “nicholas.carr @itsreallynick”
md5 = “0face841f7b2953e7c29c064d6886523”
description = “TRITON framework recovered during Mandiant ICS incident response”
strings:
$python_compiled = “.pyc” nocase ascii wide
$python_module_01 = “__module__” nocase ascii wide
$python_module_02 = “<module>” nocase ascii wide
$python_script_01 = “import Ts” nocase ascii wide
$python_script_02 = “def ts_” nocase ascii wide
$py_cnames_01 = “TS_cnames.py” nocase ascii wide
$py_cnames_02 = “TRICON” nocase ascii wide
$py_cnames_03 = “TriStation ” nocase ascii wide
$py_cnames_04 = ” chassis ” nocase ascii wide
$py_tslibs_01 = “GetCpStatus” nocase ascii wide
$py_tslibs_02 = “ts_” ascii wide
$py_tslibs_03 = ” sequence” nocase ascii wide
$py_tslibs_04 = /import Ts(Hi|Low|Base)[^:alpha:]/ nocase ascii wide
$py_tslibs_05 = /module\s?version/ nocase ascii wide
$py_tslibs_06 = “bad ” nocase ascii wide
$py_tslibs_07 = “prog_cnt” nocase ascii wide
$py_tsbase_01 = “TsBase.py” nocase ascii wide
$py_tsbase_02 = “.TsBase(” nocase ascii wide
$py_tshi_01 = “TsHi.py” nocase ascii wide
$py_tshi_02 = “keystate” nocase ascii wide
$py_tshi_03 = “GetProjectInfo” nocase ascii wide
$py_tshi_04 = “GetProgramTable” nocase ascii wide
$py_tshi_05 = “SafeAppendProgramMod” nocase ascii wide
$py_tshi_06 = “.TsHi(” ascii nocase wide
$py_tslow_01 = “TsLow.py” nocase ascii wide
$py_tslow_02 = “print_last_error” ascii nocase wide
$py_tslow_03 = “.TsLow(” ascii nocase wide
$py_tslow_04 = “tcm_” ascii wide
$py_tslow_05 = ” TCM found” nocase ascii wide
$py_crc_01 = “crc.pyc” nocase ascii wide
$py_crc_02 = “CRC16_MODBUS” ascii wide
$py_crc_03 = “Kotov Alaxander” nocase ascii wide
$py_crc_04 = “CRC_CCITT_XMODEM” ascii wide
$py_crc_05 = “crc16ret” ascii wide
$py_crc_06 = “CRC16_CCITT_x1D0F” ascii wide
$py_crc_07 = /CRC16_CCITT[^_]/ ascii wide
$py_sh_01 = “sh.pyc” nocase ascii wide
$py_keyword_01 = ” FAILURE” ascii wide
$py_keyword_02 = “symbol table” nocase ascii wide
$py_TRIDENT_01 = “inject.bin” ascii nocase wide
$py_TRIDENT_02 = “imain.bin” ascii nocase wide
condition:
2 of ($python_*) and 7 of ($py_*) and filesize < 3MB
}
Update 28 Dec 2017 – YARA Rule update
/*
* DESCRIPTION: Yara rules to match the known binary components of the HatMan
* malware targeting Triconex safety controllers. Any matching
* components should hit using the “hatman” rule in addition to a
* more specific “hatman_*” rule.
* AUTHOR: DHS/NCCIC/ICS-CERT
*/
/* Globally only look at small files. */
private global rule hatman_filesize : hatman {
condition:
filesize < 100KB
}
/* Private rules that are used at the end in the public rules. */
private rule hatman_setstatus : hatman {
strings:
$preset = { 80 00 40 3c 00 00 62 80 40 00 80 3c 40 20 03 7c
?? ?? 82 40 04 00 62 80 60 00 80 3c 40 20 03 7c
?? ?? 82 40 ?? ?? 42 38 }
condition:
$preset
}
private rule hatman_memcpy : hatman {
strings:
$memcpy_be = { 7c a9 03 a6 38 84 ff ff 38 63 ff ff 8c a4 00 01
9c a3 00 01 42 00 ff f8 4e 80 00 20 }
$memcpy_le = { a6 03 a9 7c ff ff 84 38 ff ff 63 38 01 00 a4 8c
01 00 a3 9c f8 ff 00 42 20 00 80 4e }
condition:
$memcpy_be or $memcpy_le
}
private rule hatman_dividers : hatman {
strings:
$div1 = { 9a 78 56 00 }
$div2 = { 34 12 00 00 }
condition:
$div1 and $div2
}
private rule hatman_nullsub : hatman {
strings:
$nullsub = { ff ff 60 38 02 00 00 44 20 00 80 4e }
condition:
$nullsub
}
private rule hatman_origaddr : hatman {
strings:
$oaddr_be = { 3c 60 00 03 60 63 96 f4 4e 80 00 20 }
$oaddr_le = { 03 00 60 3c f4 96 63 60 20 00 80 4e }
condition:
$oaddr_be or $oaddr_le
}
private rule hatman_origcode : hatman {
strings:
$ocode_be = { 3c 00 00 03 60 00 a0 b0 7c 09 03 a6 4e 80 04 20 }
$ocode_le = { 03 00 00 3c b0 a0 00 60 a6 03 09 7c 20 04 80 4e }
condition:
$ocode_be or $ocode_le
}
private rule hatman_mftmsr : hatman {
strings:
$mfmsr_be = { 7c 63 00 a6 }
$mfmsr_le = { a6 00 63 7c }
$mtmsr_be = { 7c 63 01 24 }
$mtmsr_le = { 24 01 63 7c }
condition:
($mfmsr_be and $mtmsr_be) or ($mfmsr_le and $mtmsr_le)
}
private rule hatman_loadoff : hatman {
strings:
$loadoff_be = { 80 60 00 04 48 00 ?? ?? 70 60 ff ff 28 00 00 00
40 82 ?? ?? 28 03 00 00 41 82 ?? ?? }
$loadoff_le = { 04 00 60 80 ?? ?? 00 48 ff ff 60 70 00 00 00 28
?? ?? 82 40 00 00 03 28 ?? ?? 82 41 }
condition:
$loadoff_be or $loadoff_le
}
private rule hatman_injector_int : hatman {
condition:
hatman_memcpy and hatman_origaddr and hatman_loadoff
}
private rule hatman_payload_int : hatman {
condition:
hatman_memcpy and hatman_origcode and hatman_mftmsr
}
/* Actual public rules to match using the private rules. */
rule hatman_compiled_python : hatman {
condition:
hatman_nullsub and hatman_setstatus and hatman_dividers
}
rule hatman_injector : hatman {
condition:
hatman_injector_int and not hatman_payload_int
}
rule hatman_payload : hatman {
condition:
hatman_payload_int and not hatman_injector_int
}
rule hatman_combined : hatman {
condition:
hatman_injector_int and hatman_payload_int and hatman_dividers
}
rule hatman : hatman {
meta:
author = “DHS/NCCIC/ICS-CERT”
description = “Matches the known samples of the HatMan malware.”
condition:
hatman_compiled_python or hatman_injector or hatman_payload
or hatman_combined
}
Category |
type |
value |
Payload delivery |
filename |
trilog.exe |
Payload delivery |
md5 |
6c39c3f4a08d3d78f2eb973a94bd7718 |
Payload delivery |
filename |
imain.bin |
Payload delivery |
md5 |
437f135ba179959a580412e564d3107f |
Payload delivery |
md5 |
0544d425c7555dc4e9d76b571f31f500 |
Payload delivery |
filename |
inject.bin |
Payload delivery |
md5 |
0face841f7b2953e7c29c064d6886523 |
Payload delivery |
filename |
library.zip |
Payload delivery |
filename |
TS_cnames.pyc |
Payload delivery |
md5 |
e98f4f3505f05bf90e17554fbc97bba9 |
Payload delivery |
filename |
TsBase.pyc |
Payload delivery |
md5 |
288166952f934146be172f6353e9a1f5 |
Payload delivery |
md5 |
27c69aa39024d21ea109cc9c9d944a04 |
Payload delivery |
filename |
TsHi.pyc |
Payload delivery |
md5 |
f6b3a73c8c87506acda430671360ce15 |
Payload delivery |
filename |
TsLow.pyc |
Payload delivery |
md5 |
8b675db417cc8b23f4c43f3de5c83438 |
Payload delivery |
filename |
sh.pyc |
External analysis |
attachment |
Fig4.png |
Payload delivery |
sha1 |
dc81f383624955e0c0441734f9f1dabfe03f373c |
External analysis |
link |
https://www.virustotal.com/file/e8542c07b2af63ee7e72ce5d97d91036c5da56e2b091aa2afe737b224305d230/analysis/1513264635/ |
Payload delivery |
md5 |
6c39c3f4a08d3d78f2eb973a94bd7718 |
Payload delivery |
sha256 |
e8542c07b2af63ee7e72ce5d97d91036c5da56e2b091aa2afe737b224305d230 |
Here is a Source code repository for TRISIS TRITON HATMAN
https://github.com/ICSrepo/TRISIS-TRITON-HATMAN/
https://github.com/ICSrepo/TRISIS-TRITON-HATMAN/tree/master/decompiled_code
References:
https://www.fireeye.com/blog/threat-research/2017/12/attackers-deploy-new-ics-attack-framework-triton.html
Header image from securityzap.com