The Little Bitmap That Could(n’t)

October 31, 2019 | Mat Powell

It’s probably safe to say that I am always fuzzing something or talking about things I have fuzzed in the past. There really isn’t anything like logging in and seeing a pile of newly discovered crashes just waiting to be triaged. In my particular case it’s not just job security, but I actually enjoy triaging the crashes and going through the whole disclosure process.

Every now and then someone will come up to me and ask how they can get started with fuzzing. These conversations almost always pivot one or two ways into “How do I get started?” and “How can I get paid?” 

The latter is pretty straightforward. Find bugs. Get paid. Simple. As for the former, you can Google around and find plenty of blogs & success stories on how product X was fuzzed and how so-and-so made Y amount of dollars off of it. Getting another researcher’s perspective and approach to a target is a great way to augment your own knowledge, but there is a problem with this — you’re only getting one part of the story:  the end of the story where the hero gets the girl (and a large paycheck).

Today’s story is going to end quite differently. I’m going to tell you how I went head-to-head with oleaut32.dll and lost.

But before that, let’s take a look at what started me down this road. It started with ZDI-CAN-8807, discovered by Trend Micro’s own KP Choubey. KP discovered an out-of-bounds read vulnerability when processing bitmap images and leveraged an Excel macro as an attack surface. Let’s take a look at the macro.

Figure 1: The XLSX macro

It’s pretty straightforward. The macro uses OleLoadPicturePath to load a corrupted bitmap image that results in an access violation.

Figure 2: The Crash

Now the details of this bug are a story for another day, but it does serve as the spark of my inspiration. Not to mention Microsoft came back to us on this one and said it didn’t meet the bar for servicing, so this case ultimately got closed. ☹

But before Microsoft crushed my hopes and dreams, I wanted to look into this further. The first thing was to get some more information on this API off of MSDN.

Figure 3: MSDN specification on OleLoadPicturePath

And it got me thinking. Why not just fuzz this API? And that’s what we’re going to do.

Creating the Harness

This seemed like a prime opportunity to get my hands dirty working with WinAFL. But before we can get started, we need to create a harness for the OleLoadPicturePath API. Essentially, we’re taking the information from the MSDN documentation, then adding a couple of exports: one for the OleLoadPicturePath API, and the other for our main method.

Figure 4: Harness for OleLoadPicturePath in C++

Next, we’ll create a batch file that will execute afl-fuzz for us. The parameters that we’ll be using for afl-fuzz.exe are:

 Parameter Description
-i Location of seed files
-o Location of output files
-D Path to DynamoRio
-t Time in milliseconds
-coverage_module Module to record coverage for
-target_module The harness we’ve created
-target_method Method inside target module we’re targeting
-nargs Number of arguments
-- Executable with @@ to represent params

Figure 5: Arguments to afl-fuzz.exe within a batch file

The last thing we need to do before kicking this off is provide some seed files for WinAFL to mutate that will go in our input directory. I have found that using seed files less than 10k in size helps keep things going smoothly.

Finally, we launch the batch file and WinAFL starts up.

Figure 6: WinAFL up and going

The next part is the hardest part: patience. However, it was nice to see that we have a crash within 5 minutes of starting the fuzzer. Looks promising, right?

Figure 7: First result in under 5 minutes

Once everything was up and running, the next step was to deploy a handful of these to the server farm and just let them run for a few weeks. Once I got back around to them, it was like Christmas morning all over again. I had hundreds of unique crashes. I was going to make that MSRC Researcher Leaderboard after all!

So, what did I find? A whole bunch of nothing. An epic pile of null-pointer exceptions.

Figure 8: I am a complete and utter failure

However, not all was lost. Somewhere beneath all of these null-pointer exceptions were two bugs — both of which might be good for an information disclosure. The first one was with the harness we created above in oleaut32.dll.

Figure 9: Out-Of-Bounds Read in oleaut32

The second was in a slight modification of this harness to target a particular API within gdi32.

Figure 10: Vindicated!

Alright, so I’m going to walk away with my first two Microsoft CVEs, right? Say hello to ZDI-CAN-8923 and ZDI-CAN-9183!

Oh, you forgot something, didn’t you? In the beginning, I told you there wasn’t a light at the end of this tunnel. After submitting the bugs to Microsoft, they came back and said that both bugs didn’t meet the bar for servicing and ultimately both cases were closed. ☹

Conclusion

Henry Ford once said, “Failure is simply the opportunity to begin again, this time more intelligently.” Even though this entire effort ended up not yielding any fruit, at the end of the day, I learned quite a bit on creating wrappers for WinAFL, got some hands-on experience with DynamoRio, and even got to take some time reversing some commonly used Windows APIs. So, if you’re just getting started and not getting anywhere — don’t get frustrated, it happens to all of us. Just keep trying and learning and you’ll get there.

Now if you’ll excuse me, I’ve got to get back to work. My name isn’t going to wind up on that MSRC list on its own ☺. Until then, you can find me on Twitter @mrpowell, and follow the team for the latest in exploit techniques and security patches