At S4x16 and later at ACSAC ICSS'16, we presented a case study of applying the LangSec approach to the popular ICS protocol, DNP3, and of using the Hammer toolkit to building a validating parser/proxy for DNP3. It can be thought of as an exhaustive inspection protocol-specific firewall, configurable to pass through only a desired, validated subset of the protocol. In the process we learned a lot both about the protocol and how it could be specified to avoid ambiguity in the standard, reduce potential for vulnerabilities, and improve parser performance—at the same time.
Checking the incoming payloads of a complex protocol is known to be a hard, error-prone task for the programmer. However, despite all the widely shared advice that input-validation errors are a leading cause of vulnerabilities and must be avoided, exactly what the ICS/SCADA programmer should do to avoid these errors for certain is still unclear. There are many conditions to be checked: is the incoming data encoded, delimited, and encapsulated correctly? Do its values fit into correct ranges? Do they relate correctly to other incoming values? All of these must be checked at some point before the incoming data can be safely used, but in what order? How can you make sure no check has been missed? Is there a general method to write production input-checking ICS code with less effort and fewer errors?
We attempted to answer these questions with our design and development method for writing a safe(r) protocol parser. We started with distilling the grammar that expressed the DNP3 message validity requirements from the protocol specification&emdash;including those not phrased so in the spec. We then implemented this grammar using the Hammer parser construction kit, getting the parser code that very closely resembled the grammar. In doing so, we found a number of potential parsing pitfalls that the Project Robus found two years ago—and a few new ones that surprised us.
Paper: [ICSS'16]
Videos: [youtube]
Code: (open source, BSD license)
The Hammer parser construction kit:
ELFbac intra-process memory protection system (isolating the parser & and raw input buffers):