Fuzzing Windows message queues — WTF?
Yeap, you can’t earn a living by coding fuzzers, analysis-framewoks-to-be-in-100-years, weird YACC stuff etc. Since my monthly income is quite low, I decided to undertake a free lancing job for a Greek organization which I wouldn’t like to name. Among other things, the job involved reversing an application and creating a keygen as well as investigating the several points of I/O. Everything went pretty smoothly until I noticed that the application in question defined several WM_APP messages for internal use.
My first step was to launch Visual Studio’s Spy++ and start looking at the events exchanged by the application components. It turned out that most of the entries in Spy++’s list were not that interesting. Nevertheless, the following events cought my attention.
<00001> 000100D0 P message:0x8020 [User-defined:WM_APP+32] wParam:00000004 lParam:02574FD0 ... <00004> 000100D0 P message:0x8002 [User-defined:WM_APP+2] wParam:00000004 lParam:02574FD0 <00005> 000100D0 P message:0x8023 [User-defined:WM_APP+35] wParam:00000008 lParam:025C8C40 <00006> 000100D0 P message:0x8002 [User-defined:WM_APP+2] wParam:00000008 lParam:025C8C40 <00007> 000100D0 P message:0x8020 [User-defined:WM_APP+32] wParam:00000004 lParam:02539FD0 ... <00010> 000100D0 P message:0x8002 [User-defined:WM_APP+2] wParam:00000004 lParam:02539FD0
Due to the nature of the target application, detecting the handlers for those custom events was quite difficult, so, I decided to have some fun before firing up IDA pro. I devoted 10 minutes of my life to write a tiny C code that would send those custom events to all of the application’s threads. For wParam and lParam I used random values. It turned out that it wasn’t such a dumb idea after all. The target crashed, and then it crashed again, and again, and again…
The root cause of all those access violations was the fact the target application assumed that the wParam and lParam values were valid memory addresses! For example, a call to SendMessage() like the one below:
lResult = SendMessage(pProcessHwnd->hWnd, 0x8002, 0x00400000, 0x00400000); fprintf(stderr, "\tLRESULT = 0x%.8p\n", lResult);
Resulted in the following output in WinDBG’s command window.
00497744 8b7e28 mov edi,dword ptr [esi+28h] ds:0023:00400028=00000000 0:003> r? esi esi=00400000
Since the target application received network input my next step was to hook all the calls to recv() in order to find any static buffers for placing my data. For this purpose, I created the following one-liner socket sniffer for WinDBG :-P
bp WS2_32!recv "r $t1 = poi(@esp + 8); pt \"dd @$t1; g\""
I fired up the target, I monitored the network traffic and used netcat to send some alphas at one of the network ports the application was receiving data. This little test revealed 2-3 candidate buffers that were allocated at a fixed point. Notice that, so far, no reversing took place. All of our assumptions are based on pure observation (which is a bad thing if you’re trying to code a serious exploit).
Continueing with the vulnerable code, after a bunch of irrelevant stuff, I ended up in the following instruction where “eax” contains the return value of CreateWindow()!
mov [esi+4], eax
It turns out that we can write “eax” wherever we want! I haven’t figured out if it can be used to execute arbitrary code but I’m pretty sure the bytes pointed by the window handle will contain something useful ;-)
So that’s it for today. Before I end this post, I would like to share with you a few links that got my attention this month…
- windbg.info – A community for WinDBG users (check out the “WinDbg. From A to Z!” PDF, it rocks!).
- REcon 2010 is over. Waiting for the material to go public! Sean’s slides are already available at his blog.
- Everything you need to know about SSA.
- Indeed, it looks familiar.
Cya
– dp