The Centrifuge platform is capable of analyzing binary firmware for previously unknown vulnerabilities and providing detailed reports of great use to developers and vulnerability researchers alike. This document details the interpretation of these code analysis reports by analyzing a simple binary that contains both safe and unsafe usage of common standard library functions.
Example Source Code
The following code represents a simple application, written in C, that passes a user supplied string from the command line to two functions:
As their names imply,
safe_functions handles the user supplied data in a safe manner, while
unsafe_functions handles user supplied data in an unsafe manner.
safe_functions copies the user supplied data to a local buffer (
stack_buffer in the source code), limiting the number of bytes copied to the defined size of the local buffer through the proper use of
strncpy. In contrast,
unsafe_functions uses an unbounded
strcpy as well as an improperly implemented use of
strncpy to copy the user-supplied data, resulting in potential stack based buffer overflows which can easily lead to exploitation. Although simplistic, these potential vulnerabilities are indicative of the bugs found in the vast majority of embedded devices.
Inside Centrifuge Static Analysis
The source code was compiled as a dynamically linked MIPS binary and uploaded to Centrifuge for analysis:
Centrifuge immediately identifies this binary as a high-risk executable, and viewing the details we see that the code analysis has identified both the
strcpy and the
strncpy function calls inside of
unsafe_functions as potential vulnerabilities. Note that the proper use of
strncpy inside of
safe_functions is not reported:
Centrifuge provides detailed analysis about where these problematic function calls originate from inside of
unsafe_functions, and details about the arguments passed to each problematic function call.
The destination argument for each of these function calls is
buf[ ] indicating that the destination argument for both function calls is a local stack buffer. As the source data is determined at run-time, this static analysis simply provides the register names from which the source data is derived at run time (in MIPS,
$a0 contains the first argument to a function, and
$v0 is the return value from a function).
Inside Centrifuge Dynamic Analysis
To provide further insight into these potential bugs, Centrifuge also performs a proprietary dynamic analysis on the binary. Potential vulnerabilities that are revealed through use of this dynamic analysis are listed as Critical Flaws in the code analysis page:
The dynamic analysis provides much more insight into these potential vulnerabilities. First, we see that the destination buffers for
strncpy are both local character buffers inside of
unsafe_functions. and we can see the total stack size allocated by
0x820 bytes), as well as the relative location of each character buffer on the stack (
Second, we see that in both instances
‘AAAA’ is the source data being copied to these local buffers. During dynamic analysis, Centrifuge provides a number of different inputs; for simple string inputs,
‘AAAA’ is the first argument,
‘BBBB’ is the second,
‘CCCC’ is the third, etc. This indicates that the first argument passed to
unsafe_functions is copied into these local buffers.
Third, we can see that the size argument passed to
4, which is exactly the size of the source data (
‘AAAA’); this is a strong indication that the number of bytes to copy is calculated based on the size of the source data and not the size of the destination buffer (indeed, as is clear from the source code, the number of bytes to copy is based on the string length of the source data).
It is also worth noting that this dynamic analysis was performed without any supporting file system, directory structure, or even external libraries. Although providing a full firmware image containing these resources can aid in dynamic analysis, they are not necessary.
Centrifuge is instrumental in identifying critical flaws and vulnerabilities in firmware binaries. Given a real-world firmware image containing hundreds or thousands of files, the ability to hone in on a handful of high risk binaries and to quickly identify flaws inside specific functions contained in those binaries is essential for those looking to secure their own products, as well as those attempting to exploit embedded devices.