Windows.Forensics.ThumbCache

ThumbCache_xx.db parser.

Windows 8 and above is supported. This does NOT parse Win7/Vista caches.

By default, for resident files, the NAME field in a cache entry contains the ASCII equivalent of the cache id, a 64 bit number. However for some deleted files and files residing on external hosts or external storage, there is either a file name, full UNC path or an alternate representation such as: <VOLUME NAME>?<VOLUME NUMBER>?<FILENAME>
or
<VOLUME NAME>?<VOLUME NUMBER>?<MFT REFERENCE NUMBER>
or
\\<hostname>\c$\<file path>

This artifact omits the default resident files by removing any entries that look like the cache ids leaving behind the interesting files, usually references to external disks or deleted files. At times there are references to external files that may be useful to an investigation, when other artifacts have been removed.


name: Windows.Forensics.ThumbCache
author: "Yogesh Khatri - @SwiftForensics / CyberCX"
description: |
    ThumbCache_xx.db parser. 
    
    Windows 8 and above is supported. This does NOT parse Win7/Vista caches.
    
    By default, for resident files, the NAME field in a cache entry contains the
    ASCII equivalent of the cache id, a 64 bit number. However for some deleted 
    files and files residing on external hosts or external storage, there is 
    either a file name, full UNC path or an alternate representation such as:
        ```<VOLUME NAME>?<VOLUME NUMBER>?<FILENAME> ```  
        or  
        ```<VOLUME NAME>?<VOLUME NUMBER>?<MFT REFERENCE NUMBER>```  
        or  
        ```\\<hostname>\c$\<file path>```
        
    This artifact <b>omits</b> the default resident files by removing any entries that 
    look like the cache ids leaving behind the <b>interesting files</b>, usually 
    references to external disks or deleted files. At times there are references
    to external files that may be useful to an investigation, when other 
    artifacts have been removed.

reference:
    - https://github.com/jas502n/010-Editor-Template/blob/main/ThumbCache.bt
    - https://www.hackerfactor.com/blog/index.php?/archives/360-Thumbs-Up.html

type: CLIENT

parameters:
   - name: GlobPath
     default: "C:/Users/**/AppData/Local/Microsoft/Windows/Explorer/thumbcache_*.db"
     description: Change this to scan custom folders
     
   - name: MaxCountPerFile
     default: 10000
     type: int64
     description: Don't change unless you have a good reason to. By default, the max count is far less than this.

   - name: NameRegex
     default: .
     type: regex
     description: Regex to filter on Name field. E.g Add ^\\\\ to hunt for UNC path.
     
sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'

    query: |
      
      LET ProfileX = '''
        [
           ["Header", 0, [
              ["Signature", 0, "String", { "length": 4 }],
              ["Version", 4, "Enumeration", {
                  "type": "uint32",
                  "map": {
                            "WINDOWS_VISTA" : 0x14,
                            "WINDOWS_7"     : 0x15,
                            "WINDOWS_8"     : 0x1A,
                            "WINDOWS_8v2"   : 0x1C,
                            "WINDOWS_8v3"   : 0x1E,
                            "WINDOWS_8_1"   : 0x1F,
                            "WINDOWS_10"    : 0x20,
                  }
              }
              ],
              ["HeaderSize", 16, "uint32"],
              ["records", "x=>x.HeaderSize", "Array", {
                  "type": "Entry",
                  "max_count": "x=>MaxCountPerFile",
                  "count": "x=>MaxCountPerFile",
              }]
            ]],
           ["Entry", "x=>x.Size", [
              ["Signature", 0, "String", { "length": 4 }],
              ["Size", 4, "uint32"],
              ["NameSize", 16, "uint32"],
              ["Name", 56, "String", {"encoding": "utf16", "length": "x=>x.NameSize"}]
             ],
            ]
        ]
        '''
   
        LET targets <= SELECT OSPath, 
            read_file(filename=OSPath,offset=0,length=4) as _Header
            FROM glob(globs=GlobPath)
            WHERE NOT IsDir 
              AND OSPath =~ "thumbcache_[0-9]+\.db"
              AND _Header =~ '^CMMM$'

        LET thumbcache_data <= SELECT OSPath,
            parse_binary(filename=OSPath,
                profile=ProfileX, struct="Header") AS Parsed 
        FROM targets
                WHERE NOT Parsed.Version IN ("WINDOWS_VISTA", "WINDOWS_7")
                
        SELECT OSPath, Name, Version FROM 
              foreach(row=thumbcache_data, query={
                  SELECT Name, OSPath, Parsed.Version as Version
                    FROM foreach(row=Parsed.records) 
              })
              WHERE Name
                  AND Name =~ NameRegex
                  AND NOT Name =~ "^[0-9a-fA-F]{14,16}$" 
                  AND NOT Name =~ "^::{"