Fuzzing, as a practice, has been around for a while. Throw garbage at an input to a program and see what falls apart. Analyze the crashes and dumps, and see if any involve commonly exploitable issues, such as buffer overflows, off by one errors, etc.
I’ve seen SCADA protocols brought low by a simple repetition of ‘AAA…'; and I’ve seen much more complex fuzzing efforts (such as those brought up by Terry McCorkle and Billy Rios in some of their advanced training). In normal IT, fuzzing often focuses effort on the ‘server’ or ‘input’ part of the communication, where the user can influence input sent to the server. This is because the user is determined to be the larger threat, and the server is considered the system to defend. There are exceptions to this, one of the notable ones are browser based bugs designed to infect users based on input received from a web server.
But most ICS deployments have the exact opposite use case. Yes, we use client-server type architectures, but the systems that require the most protection are often not the server ones (the PLCs), they are the client systems (the controlling HMIs, etc). For example, take a Modbus communication: The client requests point data directly from a PLC, which then responds back to the client with the point data requested. Simple right?
However, when the client is requesting data it is also vulnerable to a malicious response. In most research I’ve read, this malicious response has usually been ‘bad data’, data designed to provide upstream operators with false readouts and influence decisions. Easy to do when your protocols lack integrity. But, what if there were buffer overflow conditions in code that processes a response? An attacker might be able to use these conditions to crash the upstream process, and potentially insert their own code.
This request-response type architecture is present everywhere in automation, and runs over both TCP/IP and bare serial protocols. The more interesting condition is that a control center (the client) communicates to potentially hundreds of end devices (the servers), and only a small portion of those end devices may have physical and cyber protections. I use this example because I see it in NERC CIP implementations everywhere, where you have a Control Center with a dozen critical substations and 4 dozen that are non-critical.
I had an opportunity to do some very basic fuzzing for clients in the past, and recently looked at this condition. While I lacked time (this was limited to about 4 hours of hacking, including recording the requests and responses) to put together something truly magnificent, I did write-up some basic Python code to send back malformed data when a SCADA system requested it. I didn’t find anything with this, maybe you will. Use your best judgement in implementing this code, as it is intentionally designed to provide data intended to crash systems and process.
This code was written to pretend to be a Modbus device in a few very simple cases, allowing the Control Center to request data from it. The responses from the program were intended to be scripted, but you could build more intelligence in if necessary with the PyModbus project. Specifically, this code pretended to be a serial Modbus device, accessible through a Ethernet to serial converter. This made interception and monitoring of the communication much simpler than going through a bare serial interface.
The intent of the code was simple:
- Listen for a specific requests from the control center
- Queue up a valid response to that request
- Randomly alter some of the parameters from a valid response
- Respond to the control center
- Log all information regarding the communications to flat file for analysis
The major limitation in this code is lack of CRC generation, which would ensure that the malicious data is accepted by the subsystem as valid, and then processed. I’ve also removed the payloads I worked with, you’ll need to generate your own valid requests and responses.
title image by oskay