Calling Functions From Shared Object File for Fuzzing and Debugging
Its been a while since I last wrote a post here, the majority on this site were migrated from an older hugo generated template, which will explain the timestamps on the posts.
I have been busy in my career pivoting away from what we typically think of pentesting (testing broad external/internal networks for known vulnerabilities, running nessus and validating, etc) and towards where my interest currently lies: vulnerability research and binary exploitation.
Since writing my posts about the Phoenix ARM challenges, I have had hands-on experience fuzzing real world targets and disclosing vulnerabilities to the developers. This has been mostly between pentest clients, and was on an internal project at my employer. However, I have recently discovered the world of desktop application testing vectors in FedRamp and commercial penetration testing. I managed to get myself into two of these testing vectors recently and I found them to be a good way to begin my professional vulnerability research journey.
During my most recent of these engagements, I was tasked with seeing what I could find in a Linux AV product that I am not at liberty to expose the name of. At the end of my time (I had about a week and a few days, going from zero knowledge of where to start), I had zero vulnerabilities to disclose. While I later found an interesting way to bypass registering the product on Windows, I focused almost exclusively on the Linux client.
This Linux AV included four seperate installations: the open driver, the proprietary driver, the AV service, and the UI. I know, this should have all been in one package. As I stated before, I had zero knowledge of where to start as the lead on the project who has been doing this for years was sick the majority of the time I had time on this project. After figuring out the installation I was left with a bit of a mystery to solve. There were .dll files included in the install directory. I wasted quite some time looking at these wondering how a Linux client can call exported functions from a Windows DLL file. As it turns out these files did not export any functions. I wish I knew why they were included, but maybe I will learn that another day.
Eventually I turned my attention to the Shared Object (.so) files. Using IDA Free (our IDA Pro license was registered elsewhere), I took a peek into the .so file and started to piece together what I was really looking at. For the sake of this example, I will use something I had noticed before. While running strace and restarting the AV service, I saw an attempt to run execve on /usr/bin/sestatus which is a binary to tell the AV whether SE Linux was installed or not. Using IDA Free I found the function within one of the .so files which executed this command:
Since this is my first time reversing a shared object file, I wanted to figure out how you could call one of these functions in a test file as this seemed a way one could create a test harness to fuzz these functions. Who knows, if the parsing functions can be called this way, you could find a bug in the function! I did this in two ways, both of which work well. The first way I accomplished this was via the dlopen
function in C. This allowed me to dynamically load the library function, cast it as a void pointer, and execute this function.
One advantage this solution has is the ability to specify the shared object file and not worry about dynamic linking via the LD_LIBRARY_PATH environment variable. The downside is, well honestly I am unsure of any real downsides besides performance. In a fuzzing environment this may be the real killer to use the second method which is, as you could guess, dynamically linking at compile time.
Only my future endeavers will tell which is the best use case. I’l hedge my bets on the second. During my time looking through these .so files, I noticed many functions sent error messages to syslog. An easy way to constantly monitor the syslog messages for any process you are playing with (in my case I did not realize I needed to be root until after I checked out /dev/log/syslog):
tail -f /var/log/syslog | grep binaryname
Thats all for now.