[Home](https://www.fireeye.com/)  [FireEye Blogs](https://www.fireeye.com/blog.html)  [Threat Research](https://www.fireeye.com/blog/threat-research.html)  A Totally Tubular Treatise on TRITON and TriStatio... # A Totally Tubular Treatise on TRITON and TriStation [June 07, 2018 | by Steve Miller, Evan Reese](https://www.fireeye.com/blog/threat-research.html/category/etc/tags/fireeye-blog-authors/cap-steve-miller) ### Introduction [In December 2017, FireEye's Mandiant discussed an incident response involving the](https://www.fireeye.com/services.html) [TRITON framework. The TRITON attack and many of the publicly discussed ICS intrusions](https://www.fireeye.com/blog/threat-research/2017/12/attackers-deploy-new-ics-attack-framework-triton.html) involved routine techniques where the threat actors used only what is necessary to succeed in their mission. For both INDUSTROYER and TRITON, the attackers moved from the IT network to the OT (operational technology) network through systems that were accessible to both environments. Traditional malware backdoors, Mimikatz distillates, remote desktop sessions, and other well-documented, easily-detected attack methods were used throughout these intrusions. Despite the routine techniques employed to gain access to an OT environment, the threat actors behind the TRITON malware framework invested significant time learning about the Triconex Safety Instrumented System (SIS) controllers and TriStation, a proprietary network communications protocol. The investment and purpose of the Triconex SIS controllers leads Mandiant to assess the attacker's objective was likely to build the capability to cause physical consequences. TriStation remains closed source and there is no official public information detailing the ----- FireEye’s Advanced Practices Team was born to investigate adversary methodologies, and to answer these types of questions, so we started with a deeper look at the TRITON’s own Python scripts. Glossary: TRITON – Malware framework designed to operate Triconex SIS controllers via the TriStation protocol. TriStation – UDP network protocol specific to Triconex controllers. TRITON threat actor – The human beings who developed, deployed and/or operated TRITON. ### Diving into TRITON's Implementation of TriStation TriStation is a proprietary network protocol and there is no public documentation detailing its structure or how to create software applications that use TriStation. The current TriStation UDP/IP protocol is little understood, but natively implemented through the TriStation 1131 software suite. TriStation operates by UDP over port 1502 and allows for communications between designated masters (PCs with the software that are “engineering workstations”) and slaves (Triconex controllers with special communications modules) over a network. To us, the Triconex systems, software and associated terminology sound foreign and complicated, and the TriStation protocol is no different. Attempting to understand the protocol from ground zero would take a considerable amount of time and reverse engineering effort – so why not learn from TRITON itself? With the TRITON framework containing TriStation communication functionality, we pursued studying the framework to better understand this mysterious protocol. Work smarter, not harder, amirite? [The TRITON framework has a multitude of functionalities, but we started with the basic](https://www.fireeye.com/blog/threat-research/2017/12/attackers-deploy-new-ics-attack-framework-triton.html) components: ----- TsLow.pyc (Figure 1) contains several pieces of code for error handling, but these also present some cues to the protocol structure. Figure 1: TsLow.pyc function print_last_error() In the TsLow.pyc’s function for print_last_error we see error handling for “TCM Error”. This compares the TriStation packet value at offset 0 with a value in a corresponding array from TS_cnames.pyc (Figure 2), which is largely used as a “dictionary” for the protocol. ----- Figure 2: TS_cnames.pyc TS_cst array. From this we can infer that offset 0 of the TriStation protocol contains message types. This is supported by an additional function, tcm_result, which declares type, size = struct.unpack(' Not set <209> |TS_cnames.TS_names (2017 pyc)|Tr1com40.dll (2006 CPP)| |---|---| |Go to DOWNLOAD mode|<200>| ----- Unk76 Bad message type Unk77 Bad TMI version number Unk78 Module did not respond Unk79 Open Connection: Invalid SAP %d Unsupported message for this TMI Unk81 version Unk83 Wrong command TS_cnames.pyc TS_names and Tridcom.dll (1999 CPP) shared only 151 of 268 combined table strings, showing a much smaller overlap with the seemingly older CPP library. This makes sense based on the context that Tridcom.dll is meant for a Trident controller, not a Tricon controller. It does seem as though Tr1com40.dll and TR1STRS.CPP code was based on older work. We are not shocked to find that the threat actor reversed legitimate code to bolster development of the TRITON framework. They want to work smarter, not harder, too. But after reverse engineering legitimate software and implementing the basics of the TriStation, the threat actors still had an incomplete understanding of the protocol. In TRITON's TS_cnames.pyc we saw "Unk75", "Unk76", "Unk83" and other values that were not present in the tr1com40.DLL strings, indicating that the TRITON threat actor may have explored the protocol and annotated their findings beyond what they reverse |Unk77|Bad TMI version number| |---|---| |Unk78|Module did not respond| |Unk79|Open Connection: Invalid SAP %d| |Unk81|Unsupported message for this TMI version| |Unk83|| |Wrong command|| ----- You can see more of the Trilog and Triconex DLL files on VirusTotal. Item Name MD5 Description Tricom Communcations Tr1com40.dll 069247df527a96a0e048732ca57e7d3d DLL Data1.cab e6a3c93a6d433cbaf6f573b6c09d76c4 Parent of Tr1com40.dll Trilog v4.1.360R 13a3b83ba2c4236ca59aba679941c8a5 RAR Archive of TriLog Trident Communications TridCom.dll 5c2ed617fdec4779cb33c89082a43100 DLL ### Afterthoughts Seeing Triconex systems targeted with malicious intent was new to the world six months ago. Moving forward it would be reasonable to anticipate additional frameworks, such as TRITON, designed for usage against other SIS controllers and associated technologies. If Triconex was within scope, we may see similar attacker methodologies affecting the dominant industrial safety technologies. Basic security measures do little to thwart truly persistent threat actors and monitoring only IT networks is not an ideal situation. Visibility into both the IT and OT environments is critical for detecting the various stages of an ICS intrusion. Simple detection concepts such as baseline deviation can provide insight into abnormal activity. While the TRITON framework was actively in use, how many traditional ICS “alarms” were set off while the actors tested their exploits and backdoors on the Triconex |Item Name|MD5|Description| |---|---|---| |Tr1com40.dll|069247df527a96a0e048732ca57e7d3d|Tricom Communcations DLL| |Data1.cab|e6a3c93a6d433cbaf6f573b6c09d76c4|Parent of Tr1com40.dll| |Trilog v4.1.360R|13a3b83ba2c4236ca59aba679941c8a5|RAR Archive of TriLog| |TridCom.dll|5c2ed617fdec4779cb33c89082a43100|Trident Communications DLL| ----- We believe that we can identify these anomalies in the long run if we strive for increased visibility into ICS technologies. We hope that by holding public discussions about ICS technologies, the Infosec community can cultivate closer relationships with ICS vendors and give the world better insight into how attackers move from the IT to the OT space. We want to foster more conversations like this and generally share good techniques for finding evil. Since most of all ICS attacks involve standard IT intrusions, we should probably come together to invent and improve any guidelines for how to monitor PCs and engineering workstations that bridge the IT and OT networks. We envision a world where attacking or disrupting ICS operations costs the threat actor their cover, their toolkits, their time, and their freedom. It's an ideal world, but something nice to shoot for. ### Thanks and Future Work There is still much to do for TRITON and TriStation. There are many more sub-message types and nuances for parsing out the nitty gritty details, which is hard to do without a controller of our own. And although we’ve published much of what we learned about the TriStation here on the blog, our work will continue as we continue our study of the protocol. Thanks to everyone who did so much public research on TRITON and TriStation. We have cited a few individuals in this blog post, but there is a lot more community-sourced information that gave us clues and leads for our research and testing of the framework and protocol. We also have to acknowledge the research performed by the TRITON attackers. We borrowed a lot of your knowledge about TriStation from the TRITON framework itself. Finally, remember that we're here to collaborate. We think most of our research is right, but if you notice any errors or omissions, or have ideas for improvements, please contact: smiller@fireeye.com. ### Recommended Reading ----- Power Plant: Practical Experience Report Development of a tailored methodology and forensic toolkit for industrial control systems incident response [Analyzing the TRITON industrial malware](https://www.midnightbluelabs.com/blog/2018/1/16/analyzing-the-triton-industrial-malware) Repository containting original and decompiled files of TRISIS/TRITON/HATMAN malware [MAR-17-352-01 HatMan - Safety System Targeted Malware (Update A)](https://ics-cert.us-cert.gov/MAR-17-352-01-HatMan-Safety-System-Targeted-Malware-Update) [TRISIS Malware Analysis of Safety System Targeted Malware](https://dragos.com/blog/trisis/TRISIS-01.pdf) ### Appendix A: TriStation Message Type Codes The following table consists of hex values at offset 0 in the TriStation UDP packets and the associated dictionary definitions, extracted verbatim from the TRITON framework in library TS_cnames.pyc. Value at 0x0 Message Type 1 Connection Request 2 Connection Response 3 Disconnect Request 4 Disconnect Response 5 Execution Command 6 Ping Command |Value at 0x0|Message Type| |---|---| |1|Connection Request| |2|Connection Response| |3|Disconnect Request| |4|Disconnect Response| |5|Execution Command| ----- |9|MPS Are Dead| |---|---| |10|Access Denied| |11|Connection Failed| ### Appendix B: TriStation Execution Command Function Codes The following table consists of hex values at offset 6 in the TriStation UDP packets and the associated dictionary definitions, extracted verbatim from the TRITON framework in library TS_cnames.pyc. Value at 0x6 TS_cnames String 0 0: 'Start download all', 1 1: 'Start download change', 2 2: 'Update configuration', 3 3: 'Upload configuration', 4 4: 'Set I/O addresses', 5 5: 'Allocate network', |Value at 0x6|TS_cnames String| |---|---| |0|0: 'Start download all',| |1|1: 'Start download change',| |2|2: 'Update configuration',| |3|3: 'Upload configuration',| |4|4: 'Set I/O addresses',| ----- 7 7: Set calendar, |8|8: 'Get calendar',| |---|---| |9|9: 'Set scan time',| |A|10: 'End download all',| |B|11: 'End download change',| |C|12: 'Cancel download change',| |D|13: 'Attach TRICON',| |E|14: 'Set I/O address limits',| |F|15: 'Configure module',| |10|16: 'Set multiple point values',| |11|17: 'Enable all points',| |12|18: 'Upload vector table',| |13|19: 'Get CP status ',| ----- |16|22: 'Pause program',| |---|---| |17|23: 'Do single scan',| |18|24: 'Get chassis status',| |19|25: 'Get minimum scan time',| |1A|26: 'Set node number',| |1B|27: 'Set I/O point values',| |1C|28: 'Get I/O point values',| |1D|29: 'Get MP status',| |1E|30: 'Set retentive values',| |1F|31: 'Adjust clock calendar',| |20|32: 'Clear module alarms',| |21|33: 'Get event log',| 22 34: 'Set SOE block', ----- 24 36: 'Get SOE data', 25 37: 'Enable OVD', 26 38: 'Disable OVD', 27 39: 'Enable all OVDs', 28 40: 'Disable all OVDs', 29 41: 'Process MODBUS', 2A 42: 'Upload network', 2B 43: 'Set lable', 2C 44: 'Configure system variables', 2D 45: 'Deconfigure module', 2E 46: 'Get system variables', 2F 47: 'Get module types', 30 48: 'Begin conversion table download', |25|37: 'Enable OVD',| |---|---| |26|38: 'Disable OVD',| |27|39: 'Enable all OVDs',| |28|40: 'Disable all OVDs',| |29|41: 'Process MODBUS',| |2A|42: 'Upload network',| |2B|43: 'Set lable',| |2C|44: 'Configure system variables',| |2D|45: 'Deconfigure module',| |2E|46: 'Get system variables',| |2F|47: 'Get module types',| ----- |33|51: 'Get conversion table',| |---|---| |34|52: 'Set ICM status',| |35|53: 'Broadcast SOE data available',| |36|54: 'Get module versions',| |37|55: 'Allocate program',| |38|56: 'Allocate function',| |39|57: 'Clear retentives',| |3A|58: 'Set initial values',| |3B|59: 'Start TS2 program download',| |3C|60: 'Set TS2 data area',| |3D|61: 'Get TS2 data',| |3E|62: 'Set TS2 data',| 3F 63: 'Set program information' ----- 41 65: 'Upload program', 42 66: 'Upload function', 43 67: 'Get point groups', 44 68: 'Allocate symbol table', 45 69: 'Get I/O address', 46 70: 'Resend I/O address', 47 71: 'Get program timing', 48 72: 'Allocate multiple functions', 49 73: 'Get node number', 4A 74: 'Get symbol table', 4B 75: 'Unk75', 4C 76: 'Unk76', 4D 77: 'Unk77', |42|66: 'Upload function',| |---|---| |43|67: 'Get point groups',| |44|68: 'Allocate symbol table',| |45|69: 'Get I/O address',| |46|70: 'Resend I/O address',| |47|71: 'Get program timing',| |48|72: 'Allocate multiple functions',| |49|73: 'Get node number',| |4A|74: 'Get symbol table',| |4B|75: 'Unk75',| |4C|76: 'Unk76',| ----- |50|80: 'Go to DOWNLOAD mode',| |---|---| |51|81: 'Unk81',| |52|| |53|83: 'Unk83',| |54|| |55|| |56|| |57|| |58|| |59|| |5A|| |5B|| 5C ----- 5E 5F 60 61 62 63 64 100: 'Command rejected', 65 101: 'Download all permitted', 66 102: 'Download change permitted', 67 103: 'Modification accepted', 68 104: 'Download cancelled', 69 105: 'Program accepted', 6A 106: 'TRICON attached', |5F|Col2| |---|---| |60|| |61|| |62|| |63|| |64|100: 'Command rejected',| |65|101: 'Download all permitted',| |66|102: 'Download change permitted',| |67|103: 'Modification accepted',| |68|104: 'Download cancelled',| |69|105: 'Program accepted',| ----- |6D|109: 'Program is running',| |---|---| |6E|110: 'Program is halted',| |6F|111: 'Program is paused',| |70|112: 'End of single scan',| |71|113: 'Get chassis configuration response',| |72|114: 'Scan period modified',| |73|115: '<115>',| |74|116: '<116>',| |75|117: 'Module configured',| |76|118: '<118>',| |77|119: 'Get chassis status response',| |78|120: 'Vectors response',| ----- |7B|123: 'Configuration updated',| |---|---| |7C|124: 'Get minimum scan time response',| |7D|125: '<125>',| |7E|126: 'Node number set',| |7F|127: 'Get MP status response',| |80|128: 'Retentive values set',| |81|129: 'SOE block set',| |82|130: 'Module alarms cleared',| |83|131: 'Get event log response',| |84|132: 'Symbol table ccepted',| |85|133: 'OVD enable accepted',| |86|134: 'OVD disable accepted',| 87 135: 'Record event log response', ----- p, |8A|138: 'Alocate network accepted',| |---|---| |8B|139: 'Load vector table accepted',| |8C|140: 'Get calendar response',| |8D|141: 'Label set',| |8E|142: 'Get module types response',| |8F|143: 'System variables configured',| |90|144: 'Module deconfigured',| |91|145: '<145>',| |92|146: '<146>',| |93|147: 'Get conversion table response',| |94|148: 'ICM print data sent',| |95|149: 'Set ICM status response',| ----- |98|152: 'Process MODBUS response',| |---|---| |99|153: 'Allocate program response',| |9A|154: 'Allocate function response',| |9B|155: 'Clear retentives response',| |9C|156: 'Set initial values response',| |9D|157: 'Set TS2 data area response',| |9E|158: 'Get TS2 data response',| |9F|159: 'Set TS2 data response',| |A0|160: 'Set program information reponse',| |A1|161: 'Get program information response',| |A2|162: 'Upload program response',| |A3|163: 'Upload function response',| A4 164: 'Get point groups response', ----- A6 166: Program timing response, A7 167: 'Disable points full', A8 168: 'Allocate multiple functions response', A9 169: 'Get node number response', AA 170: 'Symbol table response', AB AC AD AE AF B0 B1 B2 |A7|167: 'Disable points full',| |---|---| |A8|168: 'Allocate multiple functions response',| |A9|169: 'Get node number response',| |AA|170: 'Symbol table response',| |AB|| |AC|| |AD|| |AE|| |AF|| |B0|| |B1|| ----- |B5|Col2| |---|---| |B6|| |B7|| |B8|| |B9|| |BA|| |BB|| |BC|| |BD|| |BE|| |BF|| |C0|| C1 ----- C3 C4 C5 C6 C7 C8 200: 'Wrong command', C9 201: 'Load is in progress', CA 202: 'Bad clock calendar data', CB 203: 'Control program not halted', CC 204: 'Control program checksum error', CD 205: 'No memory available', CE 206: 'Control program not valid', CF 207: 'Not loading a control program', |C4|Col2| |---|---| |C5|| |C6|| |C7|| |C8|200: 'Wrong command',| |C9|201: 'Load is in progress',| |CA|202: 'Bad clock calendar data',| |CB|203: 'Control program not halted',| |CC|204: 'Control program checksum error',| |CD|205: 'No memory available',| |CE|206: 'Control program not valid',| ----- |D2|210: 'A Network is missing',| |---|---| |D3|211: 'The download time mismatches',| |D4|212: 'Key setting prohibits this operation',| |D5|213: 'Bad control program version',| |D6|214: 'Command not in correct sequence',| |D7|215: '<215>',| |D8|216: 'Bad Index for a module',| |D9|217: 'Module address is invalid',| |DA|218: '<218>',| |DB|219: '<219>',| |DC|220: 'Bad offset for an I/O point',| |DD|221: 'Invalid point type',| DE 222: 'Invalid Point Location' ----- E0 224: '<224>', E1 225: '<225>', E2 226: '<226>', E3 227: 'Invalid module type', E4 228: '<228>', E5 229: 'Invalid table type', E6 230: '<230>', E7 231: 'Invalid network continuation', E8 232: 'Invalid scan time', E9 233: 'Load is busy', EA 234: 'An MP has re-educated', EB 235: 'Invalid chassis or slot', EC 236: 'Invalid SOE number', |E1|225: '<225>',| |---|---| |E2|226: '<226>',| |E3|227: 'Invalid module type',| |E4|228: '<228>',| |E5|229: 'Invalid table type',| |E6|230: '<230>',| |E7|231: 'Invalid network continuation',| |E8|232: 'Invalid scan time',| |E9|233: 'Load is busy',| |EA|234: 'An MP has re-educated',| |EB|235: 'Invalid chassis or slot',| ----- |EF|239: 'The variable is write protected',| |---|---| |F0|240: 'Node number mismatch',| |F1|241: 'Command not allowed',| |F2|242: 'Invalid sequence number',| |F3|243: 'Time change on non-master TRICON',| |F4|244: 'No free Tristation ports',| |F5|245: 'Invalid Tristation I command',| |F6|246: 'Invalid TriStation 1131 command',| |F7|247: 'Only one chassis allowed',| |F8|248: 'Bad variable address',| |F9|249: 'Response overflow',| |FA|250: 'Invalid bus',| bl ll d ----- FD 253: 'Point cannot be disabled', FE 254: 'Too many retentive variables', FF 255: 'LOADER_CONNECT', 256: 'Unknown reject code' [This entry was posted on Thu Jun 07 10:00 EDT 2018 and filed under Ics, python,](https://www.fireeye.com/blog/threat-research.html/category/etc/tags/fireeye-blog-tags/ics) [analysis, Evan Reese, Industrial Control Systems, and Steve Miller.](https://www.fireeye.com/blog/threat-research.html/category/etc/tags/fireeye-blog-tags/analysis) |FE|254: 'Too many retentive variables',| |---|---| |FF|255: 'LOADER_CONNECT',| ||256: 'Unknown reject code'| ----- ## Sign up for email updates Get information and insight on today's advanced threats from the leader in advanced threat prevention. First Name Last Name Email Address Company Name Threat Research Blog Products and Services Blog Executive Perspectives Blog SUBSCRIBE ----- [About FireEye](https://www.fireeye.com/company/why-fireeye.html) [Customer Stories](https://www.fireeye.com/customers.html) [Careers](https://www.fireeye.com/company/jobs.html) [Partners](https://www.fireeye.com/partners.html) [Investor Relations](http://investors.fireeye.com/) [Supplier Documents](https://www.fireeye.com/company/supplier.html) [Newsroom](https://www.fireeye.com/company/newsroom.html) [Press Releases](https://www.fireeye.com/company/press-releases.html) [Webinars](https://www.fireeye.com/company/webinars.html) [Events](https://www.fireeye.com/company/events.html) [Awards and Honors](https://www.fireeye.com/company/awards.html) [Email Preferences](https://www2.fireeye.com/manage-your-preferences.html) [Incident?](https://www.fireeye.com/company/incident-response.html) [Report Security Issue](https://www.fireeye.com/company/security.html) [Contact Support](https://www.fireeye.com/support/contacts.html) [Customer Portal](https://csportal.fireeye.com/secur/login_portal.jsp?orgId=00D3000000063LS&portalId=06030000000pSNE) [Communities](https://community.fireeye.com/welcome) [Documentation Portal](https://docs.fireeye.com) [Threat Research](https://www.fireeye.com/blog/threat-research.html) [Products and Services](https://www.fireeye.com/blog/products-and-services.html) [Executive Perspectives](https://www.fireeye.com/blog/executive-perspective.html) Threat Map [View the Latest Threats](https://www.fireeye.com/cyber-map/threat-map.html) [1 877 347 3393](tel:+1 877-347-3393) Stay Connected ##  Copyright © 2018 FireEye, Inc. All rights reserved. [Privacy & Cookies Policy | Privacy Shield | Legal Documentation](https://www.fireeye.com/company/privacy.html) Site Language English  -----