CVE-2015-2869
Multiple exploitable denial of service vulnerabilities exist in the FileInfo Plugin for Total Commander. An attacker who can control the contents of a COFF Archive Library (.lib) file can can cause an out of bounds read by specifying overly large values for the 'Size' field of the Archive Member Header or the "Number Of Symbols" field in the 1st Linker Member. An attacker who can control the contents of a Linear Executable file can cause an out of bounds read by specifying overly large values for the "Resource Table Count" field of the LE Header or the "Object" field at offset 0x8 from a "Resource Table Entry". An attacker who successfully exploits this vulnerability can cause the Total Commander application to unexpectedly terminate.
FileInfo 2.21
FileInfo 2.22
http://www.totalcmd.net/plugring/fileinfo.html
The Microsoft documentation defines the COFF Archive File Structure as shown below:
Signature :”!<arch>\n”
Header 1st Linker Member
Header 2nd Linker Member
Header Longnames Member
Header Contents of OBJ File 1
(COFF format)
Header Contents of OBJ File 2
(COFF format)
Header Contents of OBJ File N
(COFF format)
Each Archive Member is preceeded by a Header structure which contains a 'Size' field as shown below:
Offset Size Field Description
48 10 Size ASCII decimalrepresentation of the total sizeof the
archivemember,not including the size of the header.
The 1st Linker Member contains a structure which includes a 'Number of Symbols' field as shown below:
Offset Size Field Description
0 4 Number of Symbols Unsigned long containing the number of symbols indexed.
The first vulnerability is due to lack of validation on the 'Size' field from the Member Header. The below pseudocode is decompiled from the function that reads the Archive Member Headers, we have included annotations inline:
int __cdecl sub_10016D53(int value, char *fileContent)
{
char *currentLocation; // esi@3
int v3; // eax@4
int v4; // eax@6
int v5; // eax@8
int v6; // eax@10
int v7; // eax@10
int v8; // eax@11
int v10; // [sp+0h] [bp-2Ch]@1
int v11; // [sp+4h] [bp-28h]@10
char v12; // [sp+8h] [bp-24h]@8
char v13; // [sp+Ch] [bp-20h]@6
int v14; // [sp+10h] [bp-1Ch]@4
int v15; // [sp+14h] [bp-18h]@7
int v16; // [sp+18h] [bp-14h]@5
char v17; // [sp+1Ch] [bp-10h]@1
int v18; // [sp+28h] [bp-4h]@1
v10 = 0;
((void (__thiscall *)(char *, _DWORD))mfc90_316)(&v17, *(_DWORD *)&v17);
v18 = 0;
if ( strncmp(fileContent, "!<arch>\n", 8u) )
{
mfc90_310(value, "Not a valid .LIB file - signature not found\n");
}
else
{
currentLocation = fileContent + 8;
if ( fileContent != (char *)-8 )
{
This is where we begin to read the series of Archive Members which are described by their headers:
do
{
v3 = readArchiveHeader(&v14, currentLocation, currentLocation - fileContent);
LOBYTE(v18) = 1;
mfc90_941(&v17, v3);
LOBYTE(v18) = 0;
mfc90_601(&v14);
mfc90_945(&v17, "\n");
if ( strncmp(currentLocation, "/ ", 0x10u) )
{
if ( strncmp(currentLocation, "// ", 0x10u) )
{
-- snip --
}
else
{
-- snip --
}
}
else
{
if ( v16 )
{
-- snip --
}
else
{
v4 = sub_10016BE0((int)&v13, currentLocation + 60);
LOBYTE(v18) = 2;
mfc90_941(&v17, v4);
LOBYTE(v18) = 0;
mfc90_601(&v13);
mfc90_945(&v17, "\n");
v16 = 1;
}
}
The next line reads the Size field offset (48) from the current Archive Header pointer which is held in the 'currentLocation' variable and then adds the Size value to the current Archive Header pointer:
currentLocation += (atoi(currentLocation + 48) + 61) & 0xFFFFFFFE; }
Finally, the attacker controlled pointer is passed to the strncmp function, resulting in an invalid memory access:
while ( !strncmp(currentLocation + 58, "`\n", 2u) && currentLocation );
}
mfc90_300(value, &v17);
}
mfc90_601(&v17);
return value;
}
The second vulnerability is due to lack of validation on the 'Number of Symbols' field from the 1st Linker Member. The below pseudocode is decompiled from the function that reads the 1st Linker Member, we have included annotations inline:
int __cdecl sub_10016BE0(int a1, char *pLinkerMember)
{
int numberOfSymbols; // edi@1
const CHAR *v3; // esi@1
int v4; // eax@3
int v5; // edi@3
LPSTR v6; // eax@5
char flagMoreSymbols; // zf@8
int counter; // [sp+4h] [bp-1Ch]@2
int v10; // [sp+8h] [bp-18h]@1
int v11; // [sp+8h] [bp-18h]@4
int v12; // [sp+Ch] [bp-14h]@1
const CHAR *v13; // [sp+10h] [bp-10h]@1
int v14; // [sp+1Ch] [bp-4h]@1
CHAR UnDecoratedName; // [sp+20h] [bp+0h]@1
unsigned int v16; // [sp+420h] [bp+400h]@1
v16 = (unsigned int)&UnDecoratedName ^ __security_cookie;
mfc90_316(a1);
mfc90_316(&v13);
v13 = *(const CHAR **)pLinkerMember;
v14 = 1;
The next two lines read the numberOfSymbols value from the file and set a pointer based upon that value:
numberOfSymbols = readBigEndinanDWORD(v13);
v3 = &pLinkerMember[4 * numberOfSymbols + 4];
mfc90_945(a1, "FIRST LINKER MEMBER\n");
v13 = (const CHAR *)numberOfSymbols;
mfc90_2539(&v13, "\tSymbols\t: %08X\n\n");
mfc90_941(v12, &v13);
mfc90_945(v12, "\tMbrOffs \tName\n\t-------------------- \t--------------------\n");
if ( numberOfSymbols )
{
counter = numberOfSymbols;
do
{
v13 = *(const CHAR **)v10;
v4 = readBigEndinanDWORD(v13);
v5 = v4;
if ( dword_100310A8 )
{
Finally, the attacker controlled value is passed to the strncmp function, resulting in an invalid memory access:
if ( strncmp("__imp_", v3, 6u) ) <- read out of bound
{
v6 = sub_10016F56(v3, &UnDecoratedName, 0x400u);
mfc90_2539(&v13, "\t%08X \t%s\r\n", v5, v6);
}
else
{
mfc90_820(&v13, &Default);
}
}
else
{
v13 = v3;
mfc90_2539(&v13, "\t%08X \t%s\r\n", v4, v3);
}
mfc90_941(v12, &v13);
v10 = v11 + 4;
flagMoreSymbols = counter-- == 1;
v3 += strlen(v3) + 1;
}
while ( !flagMoreSymbols );
}
mfc90_601(&v13);
return v12;
}
Linear Executable File Format: http://faydoc.tripod.com/formats/exe-LE.htm
The important structures for these vulnerabilities are:
---------------------------
| MS-DOS header |
---------------------------
| LE header |
---------------------------
| ... |
---------------------------
| Windows resource data |
---------------------------
| ... |
---------------------------
The below pseudocode is decompiled from the function that reads the Resource Table Entry, we have included annotations inline:
00000001 bool __thiscall sub_1000542B(void *this, struct_somestruct *somestruct)
00000002 {
00000003 void *v2; // edi@1
00000004 LE_header *le_header; // eax@1
00000005 size_t winResSize; // edx@1
00000006 bool result; // eax@2
00000007 WinResource *winResource; // esi@3
00000008 void *winResourceBuffer; // eax@7
00000009 size_t v8; // ST08_4@7
00000010 LPVOID lpBuffer; // [sp+4h] [bp-8h]@7
00000011 size_t puLen; // [sp+8h] [bp-4h]@1
00000012
00000013 v2 = this;
00000014 sub_10004903();
00000015 le_header = (LE_header *)&somestruct->fileContent[somestruct->LE_offset];
00000016 winResSize = le_header->winResSizeData;
00000017 puLen = winResSize;
00000018 *((_DWORD *)v2 + 18) = winResSize;
00000019 if ( winResSize )
00000020 {
00000021 winResource = (WinResource *)&somestruct->fileContent[le_header->winResOffset];
00000022 if ( winResource->byte3 > 0 )
00000023 {
00000024 strlen(&winResource->byte3);
00000025 winResSize = puLen;
00000026 ++winResource;
00000027 }
00000028 if ( winResource->dword8 <= winResSize ) <-- check to bypass
00000029 {
00000030 winResSize = winResource->dword8;
00000031 puLen = winResource->dword8;
00000032 }
00000033 winResourceBuffer = (void *)mfc90_265(winResSize);
00000034 v8 = puLen;
00000035 *((_DWORD *)v2 + 3) = winResourceBuffer;
00000036 memcpy(winResourceBuffer, &winResource->charC, v8); <--- read access violation
00000037 if ( VerQueryValueA(*((const LPVOID *)v2 + 3), "\\", &lpBuffer, &puLen) )
00000038 memcpy((char *)v2 + 20, lpBuffer, puLen);
00000039 result = sub_10004FD5(v2) != 0;
00000040 }
00000041 else
00000042 {
00000043 result = 0;
00000044 }
00000045 return result;
00000046 }
The vulnerability is due to lack of validation whether the size of the resource data is greater than entire file size before passing it as the size parameter for a memcpy operation.
Value of this fieLd is defined in LE Header at specified location:
+0xBC size of win-resource data DWORD
Setting this value greater than file size causes a read access violation at line:
00000036 memcpy(winResourceBuffer, &winResource->charC, v8);
WinDbg output when read access violation occurs:
(d1c.82c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=10020ac0 ecx=03b37ffd edx=00000000 esi=1004fffc edi=1ed30010
eip=7855b05b esp=000a5f34 ebp=000a5f3c iopl=0 nv dn ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010612
MSVCR90!memcpy+0x23b:
7855b05b f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> kb
ChildEBP RetAddr Args to Child
000a5f3c 10005496 10050020 0137000c 44332211 MSVCR90!memcpy+0x23b
WARNING: Stack unwind information not available. Following frames may be wrong.
000a5f64 10005644 000a5f80 4bd180e7 000a6020 fileinfo+0x5496
000a5fdc 100074f9 0123b250 4bd1bf97 00000000 fileinfo+0x5644
000a60ac 7e42f40b 0095be08 009574c0 00000000 fileinfo!ListLoad+0x17b1
000a6190 00000000 00000000 00000000 00000000 user32!SendMessageA+0x7f
*Note - This report replaces TALOS-2015-24 and TALOS-2015-25.
Marcin 'Icewall' Noga of Cisco Talos