Fuzzing Embedded Systems
Learn how to protect your hardware-dependent APIs against unexpected threats
and increase your code coverage.
The Challenge: Security Testing in an Embedded Architecture
Embedded software usually communicates with a whole range of hardware-dependent systems. The surge of this interconnectivity led to the rapid adoption of dynamic testing approaches, such as fuzz testing. Fuzzing is a highly effective way to find security issues early in the development process. With the upcoming ISO 21434, this is now gaining even more importance, particularly within the automotive industry.
In this excerpt from a recorded live coding session, Daniel explains how you can fuzz embedded systems with dependencies in three steps.
Why Is it So Hard to Secure Embedded Applications with Dependencies?
With all the connectivity, coding can often get messy and complicated pretty quickly. These dependencies add an additional layer of complexity for security testing, as they usually require plenty of manual effort. Since C and C++ code is dependent on the hardware and the operating system, it cannot be cross-compiled. This leads to two significant problems:
Problem 1: Although the Public API documentation is available, developers need to write plenty of test harnesses, which is incredibly time-consuming.
The Obvious Solution
A solution for these two problems is a testing procedure that involves providing invalid, unexpected, or random data as input to your embedded application. Generally speaking, your hardware dependencies require fuzz testing and mocking with fuzz data. By applying fuzz testing to your software, you can protect yourself against edge cases and unexpected behaviors. It will save you valuable time and allow you to find vulnerabilities much earlier in the development process.
Use the API Documentation to Generate Fuzz Tests
You will most likely need some form of structured input to automatically generate your test harnesses, for example, documentation that defines the Public APIs. But in Automotive and other industries that work on embedded hardware, some form of API documentation often exists already.
In many cases, the information is stored in a simple CSV or Excel table. This documentation can be used to create sophisticated fuzz tests without any further adaptations automatically.
When running the automatically generated fuzz test, the fuzzer will randomly select functions from your Public API and call them in random order with random parameters. Using feedback about the covered code, the fuzzer can change the called functions and the parameters to achieve a high code coverage. This will allow you to generate test cases you might not have thought of and detect errors early onset
Static Mocking vs. Mocking with Fuzz Data
To test the application with dynamic inputs, you would usually have to compile and run it first. But with embedded software, you often have the problem that the code only runs on certain hardware or that the application requires input from external sources. For this reason, mocking/simulating these dependencies is needed for DAST, IAST or fuzzing.
Of course, there are different ways to mock your dependencies. For example, you could use simple mocking for the hardware-dependent functions that return static values, but this way your fuzzing runs will most likely not reach a high code coverage because not all possible behavior is taken into account.
On the other hand, you can use your fuzzing engine to dynamically generate return values for your mocked functions. This way, you can make use of the magic of feedback-based fuzzing to simulate the behavior of the external sources under realistic conditions and cover unexpected and unlikely edge cases. However, if you have unlimited resources, I would always recommend combining different DAST, IAST, and fuzzing approaches. Because, this way, your software would become even more secure.
Integrate Fuzz Testing In Your Development
Implementing fuzz testing into your continuous integration will allow you to find vulnerabilities and bugs at an earlier stage of your development process. The earlier you find a bug in an embedded application, the easier it is to fix it.
For example, if you find a bug in a JSON parser during unit testing, you can most likely fix it in a couple of minutes. This is relatively easy compared to fixing a crash that was possibly caused by a specific user input that only affects one certain component. So do yourself a favor and fix the bugs before they pop up in production.
See the Process in Action!
I hope this coding session gives you an idea of how fuzzing can help you to secure embedded software projects with hardware dependencies. However, the best thing is to see the process in action. So I provided you a recording of my coding session. Feel free to reach out, and to leave comments or feedback!
In modern vehicles, the security of human beings is a top priority. Learn more about how it can be ensured through advanced software testing.