Six-Library Vulnerability in NGA
Imagine if all of the sudden satellites across the world stopped working. Services that we take for granted such as navigation, satellite imagery, weather, and even time-keeping would become unavailable seemingly without explanation. Although we may not think about it all the time, satellites are a part of our daily lives.
This scenario may not be so far-fetched. The US government has published a software library called six-library designed to parse and manipulate satellite imagery and data for both internal and public use. This software contained a code execution bug discovered by ForAllSecure Mayhem, an advanced fuzz testing solution.
Now, it’s unclear where and how exactly this library is being used so it’s impossible to say the full reach that this vulnerability could have had if it was discovered by the wrong parties. One thing is for sure, this code is likely used in both government and private code bases and this particular bug has been lying unknown (hopefully!) for over 10 years on git.
In this blog post we will detail:
- How to find fuzzing targets (for Mayhem!)
- Finding the bug
- Fixing and verifying with Mayhem
Additionally, you can find a fully reproducible POC in ForAllSecure Vulnerabilities Lab on Github.
How to find fuzzing targets (for Mayhem!)
When examining six-library for potential entry points for fuzzing I kept the following in mind:
- Look for functions amenable to consuming raw binary data
- Look for example or test binaries that are fuzzable
- Mayhem unlocks more options here (file, network, standard in, uninstrumented)
Luckily after a little digging in the repository, I found a collection of test binaries that took input from file and exercised interesting parts of the library such as parsing and transforming data. Since these binaries already took input from a file specified on the command line, they were the perfect target for fuzzing.
Finding the bug
Not long after throwing some of these test binaries into Mayhem crashes start pouring out of one of them: six-extract-xml, a binary that (as the name implies) extracts xml data from the satellite data files. One interesting thing to note here is that the bugs appear to dry up after the first couple of hours, but around the day mark more crashes are found. Don’t stop fuzzing just because you’ve found a bug!
Let’s look more in detail at one of these crashes.
Here we can see that for one crash Mayhem found several bugs that eventually led up to the crash. Additionally, the final crash is caused by an invalid jump, meaning that the bug results in total control of where the program executes.
Let’s take a look at the source code where the crash occurred and see if we can’t find and fix the bug.
Just like Mayhem suggested, it looks like we have an uninitialized value error. In readTRE this can occur when it fails the call to readField and executes the error handling code before tre is initialized. If readField succeeds, tre is initialized and this bug is avoided entirely. But when a user controls the data file and can send malformed field data, the error handling code will be executed with tre uninitialized where it is then passed to nitf_TRE_destruct.
Want to Learn More About Zero-Days?
Catch the FASTR series to see a technical proof of concept on our latest zero-day findings.
Watch EP 03 See TV Guide
In C, uninitialized value errors can be tricky to track down due to their non-deterministic nature. This is due to uninitialized values in C taking on the value of whatever was on the stack prior to the function being called. Since different functions can be called before the buggy one the uninitialized variable can take on different values.
In this case the function called before readTre has the user input stream data structure on the stack. This data structure happens to have the same memory layout as the tre struct used by nitf_TRE_destruct to call the specialized destruct function for a tre. Because the location of user input and the virtual function table for tre line up, we can control the function pointers in the vtable using our user input – resulting in arbitrary control of the program execution! Because Mayhem has diagnosed the type and location of the bugs, fixing them is easy too. Since tre was uninitialized, let's initialize it to NULL so that when readField fails, the destructor is not called.
Putting this back into Mayhem, we can see that the vast majority of bugs we found earlier are gone.
Conclusion
At ForAllSecure, we take responsible disclosure seriously, allowing us to do our part to keep the community safe and secure. Listed below is our the responsible disclosure process we followed for this bug:
- June 24 2020, Bug is discovered by fuzzing in Mayhem
- June 26 2020, Root cause of bug diagnosed
- June 29 2020, First reach out attempt made to contact developers
- July 9 2020, Contact made with relevant developers and bug/fix disclosed
- July 22 2020, Developers commit fix for the bug to repository
Not sure if you’re affected by this bug? Reach out to us here, and one of our security experts would be happy to help you navigate the issue.
While this post demonstrated how to work with six-library, the same ideas can be applied to any other targets that take file inputs in order to find deep bugs that others might miss.
Interested in Mayhem’s other findings? You can find the full list here.
Add Mayhem to Your DevSecOps for Free.
Get a full-featured 30 day free trial.