Results 1 to 4 of 4

Thread: outfox.c - a tool for dumping firefox cache data

Hybrid View

  1. #1
    Just burned his ISO
    Join Date
    Dec 2010
    Posts
    3

    Default outfox.c - a tool for dumping firefox cache data

    A while back, the HR department at my work asked me to investigate a company laptop that they suspected had been being used inappropriately. Part of what I needed to do was dig into a particular user's surfing history and I was surprised to find that there was next to nothing in the way of free tools for extracting data from Firefox's cache.

    In the end, I was forced to use some VB app that comes with the CAINE live distro and is run under Wine (yuck!).

    After doing a bit of research to understand how FF's cache format works, I wrote this little program. It's my own code, and still pretty much a beta, but I've lost interest in doing much more with it, so I'm donating it to the BT team for their consideration.


    Code:
    /* outfox.c
     * Version:     0.1b
     * Date:        20101215
     * Author:      Written for BackTrack Linux by c0rruption
     *
     * References:  http://www.securityfocus.com/infocus/1832 (factually inaccurate
     *                                here and there, but hugely helpful anyway)
     *
     *              mozilla/netwerk/cache/src/ from the mozilla source tree :)
     *
     * Usage:       outfox <input dir> <output dir>
     *
     * Extracts a mozilla cache to the given output directory.  
     */
     
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    #include <string.h>
    
    #include <limits.h>
    #include <endian.h>
    
    #include <errno.h>
    
    #include <time.h>
    #include <sys/stat.h>
    
    #include <arpa/inet.h> // ntohl()
    #include <libgen.h>    // basename()
    
    #define min(a,b)                 (((a) < (b))? (a) : (b))
    
    #define NBUCKETS                 (1 << 5)
    
    #define loc_initialized(n)       ((n) & 0x80000000)
    #define loc_generation(n)        ((n) & 0x000000ff)
    #define loc_fileSelector(n)     (((n) & 0x30000000) >> 28)
    
    #define loc_blockNumber(n)       ((n) & 0x00ffffff)
    #define loc_blockSize(n)         (256 << (2 * (loc_fileSelector(n) - 1)))
    #define loc_recordOffset(n)      (loc_blockNumber(n) * loc_blockSize(n) + 4096)
    
    
    const int V_MAJOR = 0;
    const int V_MINOR = 1;
    
    
    typedef struct {
        uint32_t version;
        uint32_t dataSize;
        uint32_t entryCount;
        uint32_t isDirty;
        uint32_t recordCount;
    
        uint32_t evictionRank[NBUCKETS];
        uint32_t bucketUsage[NBUCKETS];
    } mapHeader;
    
    typedef struct {
        uint32_t hashNumber;
        uint32_t evictionRank;
        uint32_t dataLocation;
        uint32_t metaLocation;
    } mapRecord;
    
    typedef struct {
        uint32_t headerVersion;
        uint32_t metaLocation;
        int32_t  fetchCount;
        uint32_t lastFetched;
        uint32_t lastModified;
        uint32_t expirationTime;
        uint32_t dataSize;
        uint32_t keySize;       // includes terminating null byte
        uint32_t metaSize;      // includes terminating null byte
    } metaDataRecordHeader;
    
    
    
    size_t copyFile(FILE *infile, const char *ofname, size_t size) {
        FILE *outfile;
        char buf[1024];
        size_t n, sz = size;
    
        if ((outfile = fopen(ofname, "wb")) == NULL) {
            return 0;
        }
    
        while (sz && (n = fread(buf, 1, min(sizeof(buf), sz), infile)) != 0) {
            if (fwrite(buf, 1, n, outfile) != n) {
                fprintf(stderr, "%s: write error\n", ofname);
                return -1;
            }
            sz -= n;
        }
    
        fclose(outfile);
        return size;
    }
    
    
    
    FILE *openRecord(uint32_t hashNumber, uint32_t loc, const char *inputdir) {
        FILE *ret;
        char fname[PATH_MAX];
    
        int cachefile = loc_fileSelector(loc);
    
        if (cachefile == 0) {
            /* TODO: should I use "%08Xm%02d" for on-disk meta records? */
            snprintf(fname, sizeof(fname), "%s/%08Xd%02d", inputdir,
                                                           hashNumber,
                                                           loc_generation(loc));
        } else {
            snprintf(fname, sizeof(fname), "%s/_CACHE_%03d_", inputdir, 
                                                              cachefile);
        }
    
        if ((ret = fopen(fname, "rb")) == NULL) {
            perror(fname);
            return NULL;
        }
    
        if (cachefile && fseek(ret, loc_recordOffset(loc), SEEK_SET) != 0) {
            fprintf(stderr, "%s: couldn't seek to offset %lu: %s\n",
                            fname,
                            (unsigned long) loc_recordOffset(loc),
                            strerror(errno));
    
            fclose(ret);
            return NULL;
        }
    
        return ret;
    }
    
    
    char *getExtension(const char *url) {
        // TODO: fix this cheesy hack.
        // TODO: wide characters in URLs?
        char *ret = NULL;
    #ifndef NO_EXTENSIONS
        char *ptr, *buf;
    
        if ((buf = strdup(url)) == NULL) {
            return NULL;
        }
    
        if ((ptr = strchr(buf, '?')) != NULL) {
            *ptr = 0;
        }
    
        if ((ptr = strchr(buf, '#')) != NULL) {
            *ptr = 0;
        }
    
        //  TODO: This is fine for http://foo.com/abd/def.ext or 
        //  even for http://foo.com/ but what about http://foo.com
        ptr = ((ptr = strrchr(buf, '/'))? ptr+1 : buf);
    
        // .torrent is the longest extension I can think of.
        // 12 chars seems a good max length.
        if ((ptr = strrchr(ptr, '.')) != NULL && strlen(ptr) < 12) {
            ret = strdup(ptr);
        }
    
        free(buf);
    #endif
        return ret;
    }
    
    int readRecord(mapRecord *maprec, 
                   FILE *indexFile, 
                   const char *inputdir,
                   const char *outputdir) {
    
        FILE *infile;
        char ofname[PATH_MAX];
        metaDataRecordHeader metarec;
    
        int retstatus = 1;
    
        char *ext;  // file extension
        static char *ext_default = "";
    
        infile = openRecord(maprec->hashNumber, maprec->metaLocation, inputdir);
    
        if (infile == NULL) {
            fprintf(stderr, "couldn't open metarecord: %08x\n", maprec->hashNumber);
            return 0;
        }
    
        fread(&metarec, 1, sizeof(metaDataRecordHeader), infile);
    
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        {
            int ei;
            uint32_t *endian_ptr = (uint32_t *) &metarec;
            for (ei = 0; ei < sizeof(metaDataRecordHeader) / 4; ++ei) {
                endian_ptr[ei] = ntohl(endian_ptr[ei]);
            }
        }
    #endif
    
        /* read key data (metarec.keySize includes null terminator) */
        char *key = malloc(metarec.keySize);
    
        if (key == NULL) {
            fclose(infile);
            perror("malloc");
            return 0;
        }
    
        fread(key, metarec.keySize, 1, infile);
        if ((ext = getExtension(key)) == NULL) {
            ext = ext_default;
        }
    
        if (indexFile) {
            struct tm *tmptr;
            char dateFetched[16];  // ccyymmddhhmmss
            char dateModified[16]; // ccyymmddhhmmss
    
            if ((tmptr = localtime((time_t *) &metarec.lastFetched)) == NULL) {
                strcpy(dateFetched, "(unknown)");
            } else {
                strftime(dateFetched, sizeof(dateFetched), "%Y%m%d%H%M%S", tmptr);
            }
    
            if ((tmptr = localtime((time_t *) &metarec.lastModified)) == NULL) {
                strcpy(dateModified, "(unknown)");
            } else {
                strftime(dateModified, sizeof(dateModified), "%Y%m%d%H%M%S", tmptr);
            }
    
            fprintf(indexFile, "%s, %u, %s, %08X%s, %s\n", dateFetched,
                                                           metarec.fetchCount,
                                                           dateModified,
                                                           maprec->hashNumber,
                                                           ext,
                                                           key);
        }
    
        free(key);
    
        snprintf(ofname, sizeof(ofname), "%s/%08X%s.meta", outputdir,
                                                           maprec->hashNumber,
                                                           ext);
    
        size_t copied = copyFile(infile, ofname, metarec.metaSize - 1);
        fclose(infile);
    
        if (copied != metarec.metaSize - 1) {
            fprintf(stderr,
                    "%08X: tried to copy %d bytes metadata, copied %d instead\n",
                    maprec->hashNumber,
                    metarec.metaSize - 1,
                    copied);
    
            retstatus = 0;  // nonfatal. return an error, but proceed for now
        }
    
        /* OK, that's the metadata, now on to the datadata :) */
    
        snprintf(ofname, sizeof(ofname), "%s/%08X%s", outputdir,
                                                      maprec->hashNumber,
                                                      ext);
    
        if (ext && ext != ext_default) {
            free(ext);
        }
    
        infile = openRecord(maprec->hashNumber, maprec->dataLocation, inputdir);
    
        if (infile == NULL) {
            fprintf(stderr, "couldn't open datarecord: %08x\n", maprec->hashNumber);
            return 0;
        }
    
        copied = copyFile(infile, ofname, metarec.dataSize);
        fclose(infile);
    
        if (copied != metarec.dataSize) {
            fprintf(stderr,
                    "%08X: tried to copy %d bytes data, copied %d instead\n",
                    maprec->hashNumber,
                    metarec.dataSize,
                    copied);
            return 0;
        }
    
        return retstatus;
    }

  2. #2
    Just burned his ISO
    Join Date
    Dec 2010
    Posts
    3

    Default Re: outfox.c - a tool for dumping firefox cache data

    Code:
    int main(int argc, char **argv) {
        mapRecord rec;
        mapHeader header;
    
        FILE *infile, *indexfile;
    
        char *dirs[2];
        char fname[PATH_MAX];
    
        int i, nrecords = 0;
    
        if (argc != 3) {
            fprintf(stderr, "usage:\n  outfox <input dir> <output dir>\n");
    
            return 1;
        }
    
        for (i = 1; i < 3; ++i) {
            struct stat st;
    
            if ((dirs[i-1] = realpath(argv[i], NULL)) == NULL) {
                perror(argv[i]);
                return 1;
            }
    
            if (stat(argv[i], &st) != 0) {
                perror(argv[i]);
                return 1;
            } else if (! S_ISDIR(st.st_mode)) {
                fprintf(stderr, "%s: not a directory\n", argv[i]);
                return 1;
            }
        }
    
        /* PATH_MAX: bit of a hack since this isn't necessarily constant */
        if (strncmp(dirs[0], dirs[1], PATH_MAX) == 0) {
            fprintf(stderr, "input and output directories are the same\n");
            return 1;
        }
    
        free(dirs[0]);
        free(dirs[1]);
    
        /* Sanity checking over, down to business */
    
        printf("Outfox v%d.%d (written for Backtrack Linux)\n", V_MAJOR, V_MINOR);
    
        snprintf(fname, sizeof(fname), "%s/%s", argv[2], "_index_");
        if ((indexfile = fopen(fname, "w")) == NULL) {
            perror(fname);
            return 1;
        }
    
        snprintf(fname, sizeof(fname), "%s/%s", argv[1], "_CACHE_MAP_");
        if ((infile = fopen(fname, "rb")) == NULL) {
            perror(fname);
            return 1;
        }
    
        fprintf(indexfile, "# dateFetched, fetchCount, dateModified, hashNumber, key\n");
    
        fread(&header, 1, sizeof(header), infile);
    
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        header.version = ntohl(header.version);
        header.dataSize = ntohl(header.dataSize);
        header.entryCount = ntohl(header.entryCount);
        header.isDirty = ntohl(header.isDirty);
        header.recordCount = ntohl(header.recordCount);
    #endif
    
        while (fread(&rec, 1, sizeof(mapRecord), infile) == sizeof(mapRecord)) {
    #if __BYTE_ORDER == __LITTLE_ENDIAN
            {
                int ei;
                uint32_t *endian_ptr = (uint32_t *) &rec;
                for (ei = 0; ei < sizeof(mapRecord) / 4; ++ei) {
                    endian_ptr[ei] = ntohl(endian_ptr[ei]);
                }
            }
    #endif
            if (rec.hashNumber == 0 || !loc_initialized(rec.metaLocation)) {
                continue;
            }
    
            ++nrecords;
            readRecord(&rec, indexfile, argv[1], argv[2]);
        }
    
        fclose(infile);
        fclose(indexfile);
    
        printf ("%d/%d records processed\n", nrecords, header.entryCount);
        return 0;
    }
    Last edited by sickness; 12-19-2010 at 01:40 PM.

  3. #3
    Just burned his ISO
    Join Date
    Jan 2011
    Posts
    11

    Default Re: outfox.c - a tool for dumping firefox cache data

    How can I run such files (.c extensions files) in BT4 console?

  4. #4
    Just burned his ISO
    Join Date
    May 2008
    Posts
    14

    Default Re: outfox.c - a tool for dumping firefox cache data

    Not to sound like a jerk, but why would you be using backtrack if you dont even know how to compile a c file?

    Use gcc to compile the c file and then you'll have an executable(elf binary). Good luck

Similar Threads

  1. Replies: 0
    Last Post: 12-07-2010, 07:29 PM
  2. Command Line Fun: Rip Flash video from Firefox cache
    By Virchanza in forum OLD BT4beta HowTo's
    Replies: 6
    Last Post: 12-12-2009, 09:00 PM
  3. Dumping xp passwords - bt3 video
    By purehate in forum OLD Tutorials and Guides
    Replies: 44
    Last Post: 09-10-2009, 08:15 AM
  4. new tool - dumping cached credentials from linux
    By bofh28 in forum OLD Programming
    Replies: 0
    Last Post: 11-01-2008, 03:22 PM
  5. Dumping Ram to a Physical Device?
    By Friendless in forum OLD Newbie Area
    Replies: 1
    Last Post: 02-22-2008, 04:54 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •