All applications and devices should log security events to help detect attacks and provide an audit trail for after incident investigations. Unfortunately most PLC’s and other field devices provide very limited, if any, security logging. And Rockwell Automation’s ControlLogix is typical in its limited security logging. One approach to address this deficiency is to create ladder logic in ControlLogix to record security events as points similar to any other monitoring points.
ControlLogix Security Events
There is a large number of potential PLC security events. Some simple examples include failed authentication and system status messages that might reflect security issues (e.g. are the controllers ‘locked’ so that they are not susceptible to remote code changes by non-authorized parties). Specifically, a controller mode key error, might indicate that someone is attempting an unauthorized code or data manipulation. Any event that may have a security repercussion is a candidate for a security event.
Recording Security Events in ControlLogix
One method to record security events is to use RSLogix 5000 ladder logic to move FaultLog data from the ControlLogix controller to a data historian or other control system application.
Out of the box, the ControlLogix PAC is not maintaining a log per se. There is something called a FaultLog object that is capable of monitoring systems events. But it only has 4 variables and they are being updated continuously. The four system variables are the MajorEvents, MajorFaultBits, MinorEvents, & MinorFaultBits data structures. The MajorEvents and MinorEvents are decimal integers that simply track the number of respective events. The MajorFaultBits and MinorFaultBits contain encoded information for the current respective event. Since these values are often rewritten on the very next pass of the controller logic, it is critical to capture their values in a produced tag so they can be sent to and stored by the data logger. Any system event information that can be captured can be sent as a ‘MESSAGE’ to a peer device or as a ‘produced’ tag to one or more consumers. Using messages allows direct Common Industrial Protocol (CIP) writes and reads between specific destinations but in the example that follows the simpler, generic PRODUCER/CONSUMER mechanism is used. The following example shows how to:
- Produce a simple system event (in this first simple case, a simple programming fault).
- Capture system event information from the FaultLog.
- Place a message about the system event in the produced tag so it can be read by a data historian.
The consumers in this examples might have been other devices including the HMI, logging tool, or a historian.
This simple example manipulates a SimpleString. To show that things are working an ‘A’ is added to the SimpleString and the first character of the SimpleString is deleted. So after a few ‘A’s have been added and deleted the example continues to delete characters until the string is empty. At that point the next delete will result in a programming fault.
This example manipulates the system by down-loading the ladder logic from the RSLogix5000 to the controller, placing the RSLogix5000 user interface in run mode, and then manipulating data by bit toggling. In run mode, the bit values in the RSLogix5000 are highlighted on the ladder logic diagram with unmistakable green bars. By selecting one of the bit flags and either selecting Toggle bit from the right-click menu or simply using the Control-T hot key the bit can be toggled. The DoIt_Flag is used to add an ‘A’ to our SimpleString. The Del_Flag will be used to delete the first char of the SimpleString. Toggling the Del_Flag when the SimpleString is empty will cause the program fault.
Here’s the ladder logic…
And the descriptions of the tags used…
Below is the explanation of what each rung does.
The ONS instruction used in the rung 0 (and rung 1) is a ‘one shot instruction’. A one shot instruction is needed so it is only executed one time after the value is toggled. If an EQU instruction (an equals test) had been used, the rung would be true every pass the controller makes and the string would quickly concatenate away. Note this string is the default length of 82 chars event though it starts out with no values assigned to the 82 positions.
So when the DoIt_Flag is toggled the CONCAT instruction is executed which initializes the SimpleString to ” if it has not already been initialized and then concatenates the value of CharAString which is a constant ‘A’ onto the SimpleString.
Rung 1 works similar to Rung 0 except that when the rung is true the DELETE instruction is used to delete the first character (1 character starting at the first position) from the SimpleString and place the remaining value back in the Simple String.
The GSV instruction of Rung two executes every pass because there is no stated input condition criteria. It grabs the MinorEvent value from the FaultLog.MinorEvents field. The MinorEvents was chosen because this was known to produce a ‘minor’ programming fault. A real event handler would need to handle both major & minor events, and it would need to grab more details such as the event general ‘type’, it’s ‘event code’, the time stamp of the event, and possibly other information, from the appropriate FaultBits data structure.
To flag that the MinorEvents has changed an OldMinorEvents counter is kept. The NEQ instruction is the ‘not equal’ instruction so basically what happens is that if the current count is not equal to our previous count then:
1. use the MOV instruction to update the local OldMinorEvents variable.
2. use the DTOS instruction to copy a string representation of the LocalMinorEvents decimal integer into the EventsAsString variable.
3. finally concatenate the ‘Messages so far: ‘ label and out EventsAsString strings into the SimpleProducedTag. Since the system checked ‘Send Data State Change Event to Consumers’ in the connections properties box when it declared the SimpleProducedTag, this tag will be automatically broadcast to consumers.
So the basic process is: use the GSV instruction to inspect the FaultLog object and then based on interesting changes, update a ‘produced’ tag for the data historian to record.