The concept of Attack Surface is a formal way for quantifying the exposure of a connected system. It’s a measure of exposure and not that of vulnerabilties. However, two aspects of Attack Surface, channels and protocols, are key in figuring out how to attack a system and where the failure points are. In the last blog about mutations, I wrote about how we can create reusable mutation objects that can be plugged into arbitrary protocols and how we can use code coverage as one metric for measuring the effectiveness of it. From a system level perspective, I want to introduce this new notion of Attack Surface Coverage.
First of all, we consider protocols in the more generic sense; structured exchange of messages. This includes file formats (video, images, executables, codecs, documents etc), API’s and really any other structured message that can be delivered to an application. With the recent PDF, QuickTime and Powerpoint vulnerabilities, this should be pretty obvious.
With the recent attention to fuzzers (though they’ve been around a long while), there’s the obvious question of how effective a fuzzer is. Is there a way to quantify this somehow or at least talk about what goes on behind a fuzzer that conveys the effectiveness of it?
In my mind there are three parts to measuring the effectiveness that together form a single attack surface coverage metric:
- Specification coverage
- Code coverage
- Configuration coverage
Broadly, this maps to three stages of any product’s life cycle – design, development and deployment. Interestingly enough, in most cases, each one in the given order is a superset of the next. In other words, specification/design covers the gamut of things that can happen in the protocol space. During development, typically not all parts of the specification are implemented. Finally, the way the product is configured and deployed further reduces the scope (think kill-bit for Active-X controls, mod_ssl for Apache, SASL authentication for an FTP server) of what’s actually available for attack. Let’s go through them and see why all three are important.
Specification coverage really translates to expressing the interdepencies of protocols in a precise and thorough way such that the russian doll effect can be handled to arbitrary nesting levels. It tells us how deep the attacks are applied relative to the protocol components defined in the specification. For example, LDAP DN AttributeValue can be the BER encoding of a string (as opposed to a simple ASCII string). This, when used in an already ASN.1 encoded X.509 certificate and transmitted as part of client authenticated SSL over TCP over IPv6 really shows how deep the dolls can be stacked. Nested levels and strange loops. Reminds me of GÃ¶del, Escher, Bach.
Now the downside of just using specification coverage is that, most specifications allow for structured extensions to the core protocol. HTTP, for example allows one to have arbitrary headers as long as they adhere to the overall ‘name: value\r\n‘ syntax. The McAfee ePolicy server vulnerability was due to two custom HTTP headers – AgentGUID and Source. Being an RFC-bigot won’t get us far since we might miss out attacking code that has these extensions. Which leads us to…
This is a fairly obvious one, so I won’t bother elaborating much. The gotcha, as I mentioned in the OOMPH blog, is that 100% code coverage is really a starting point for figuring out additional attacks. The example I stated was the strcpy function. Any non-empty string will result in 100% code coverage, but you could still have buffer overflows lurking in the application.
100% code coverage != zero vulnerabilities
The context of how the coverage data was generated is as important as the coverage metric.
Finally we come to the reality of the networked world. You can implement attacks on a protocol till the cows go home, but if someone turns on SSL on the TCP port you are attacking or runs the service such that it listens on IPv6 and the attacks can’t be delivered over SSL or IPv6, you have zero configuration coverage. The same holds true for the different authentication mechanisms a target system is configured to support. If the attack is post-auth and the configured authentication mechanism is not supported during attack delivery, zero configuration coverage again.
Configuration coverage (and flexibility of attack composition) is as important as the above two because it allows us to dynamically adapt to how a target system is configured/deployed such that we can deliver the attacks in all sorts of ways. Notice that it doesn’t change the state (or the layer) at which the attacks are present, just how we reach the system we are attacking.