Windows.Detection.CryptnetUrlCache

This artifact will hunt for evidence of Certutil use as a download cradle.

The CryptnetUrlCache contains both content and metadata of files downloaded by CertUtil and other Windows Crypto components. The artifact will first look for content larger than a specified size, then check headers against a whitelist of common content types. Additional options include a UrlWhitelist and search of VSS.

NOTE: Expect some false positives and build a whitelist of Urls to add for regular hunts. Alternatively target specific headers such as PE files by adding ‘^MZ’ to the HeaderRegex field.

name: Windows.Detection.CryptnetUrlCache
description: |
   This artifact will hunt for evidence of Certutil use as a download cradle.

   The CryptnetUrlCache contains both content and metadata of files downloaded by
   CertUtil and other Windows Crypto components. The artifact will first look for
   content larger than a specified size, then check headers against a whitelist
   of common content types. Additional options include a UrlWhitelist and search
   of VSS.

   NOTE: Expect some false positives and build a whitelist of Urls to add for
   regular hunts. Alternatively target specific headers such as PE files by
   adding '^MZ' to the HeaderRegex field.

author: "@mgreen27 - Matt Green"

reference:
  - https://thinkdfir.com/2020/07/30/certutil-download-artefacts/
  - https://lolbas-project.github.io/lolbas/Binaries/Certutil/


parameters:
  - name: GlobLookup
    default: |
      FileGlob
      C:\Windows\*\config\systemprofile\AppData\LocalLow\Microsoft\CryptnetUrlCache\**
      C:\Users\*\AppData\LocalLow\Microsoft\CryptnetUrlCache\**
  - name: SusSize
    description: "Size in bytes for CryptnetUrlCache content to be suspicious"
    default: '10000'
    type: int
  - name: HeaderRegex
    description: 'Regex of content headers.'
    default: '.'
    type: regex
  - name: HeaderWhitelist
    description: 'Whitelist regex of content headers.'
    default: '^(MSCF|0|<|.<|----)'
    type: regex
  - name: UrlWhitelist
    description: 'Regex to whitelist Url field'
    type: regex
  - name: SearchVSS
    description: "Add VSS into query."
    type: bool


sources:
  - precondition:
      SELECT OS From info() where OS = 'windows'

    query: |
      -- file target globs
      LET CryptnetUrlCache <= SELECT FileGlob
            FROM parse_csv(filename=GlobLookup, accessor='data')


      -- expand provided glob into a list of paths on the file system (fs)
      LET fspaths(path) = SELECT * FROM glob(globs=path) WHERE NOT IsDir


      -- function returning list of VSS paths corresponding to path
      LET vsspaths(path) = SELECT *
        FROM Artifact.Windows.Search.VSS(SearchFilesGlob=path)
        WHERE NOT IsDir


      -- determine files in scope from globs
      LET files <= SELECT * FROM foreach(row=CryptnetUrlCache,
            query={
                SELECT * FROM if(condition=SearchVSS,
                    then= {
                       SELECT * FROM vsspaths(path=FileGlob)
                    },
                    else= {
                       SELECT * FROM fspaths(path=FileGlob)
                    })
            })


      -- extract metadata lines
      LET metadata = SELECT * FROM foreach(row=files,
        query={
            SELECT
                FullPath as MetaPath,
                Mtime as MetaMtime,
                Ctime as MetaCtime,
                Atime as MetaAtime,
                parse_string_with_regex(
                    string=utf16(string=Line),
                    regex=['[\\s\\S]*(?P<Url>(http[s]?:|\\\\\\\\|ftp:)[\\s\\S]+)']
                        ).Url as Url
            FROM parse_lines(filename=FullPath)
            WHERE MetaPath =~ '\\\\Microsoft\\\\CryptnetUrlCache\\\\metadata\\\\'
            GROUP BY MetaPath
        })


      -- find suspicious content files and extract headers
      LET hits = SELECT
            FullPath,Name,Size,
            Mtime, Atime, Ctime,
            hash(path=FullPath) as Hash,
            read_file(length=4,filename=FullPath) as Header
        FROM files
        WHERE
            FullPath =~ '\\\\Microsoft\\\\CryptnetUrlCache\\\\Content\\\\'
            AND Size > int(int=SusSize)


      -- output rows
      SELECT * FROM foreach(row=hits,
        query={
            SELECT
                FullPath,Name,Size,Header,
                Mtime, Atime, Ctime,
                Url, Hash,
                if(condition= Header=~ 'MZ',
                    then= parse_pe(file=FullPath).VersionInformation,
                    else= 'N/A' ) as VersionInformation,
                if(condition= Header=~ 'MZ',
                    then= authenticode(filename=FullPath),
                    else= 'N/A' ) as Authenticode,
                MetaPath,
                MetaMtime,MetaAtime,MetaCtime
            FROM metadata
            WHERE
                MetaPath =~ Name
                AND Header =~ HeaderRegex
                AND NOT if(condition= HeaderWhitelist,
                    then= Header =~ HeaderWhitelist,
                    else= FALSE)
                AND NOT if(condition=UrlWhitelist,
                    then= Url =~ UrlWhitelist,
                    else= FALSE)
                AND split(
                    string=FullPath,
                        sep='CryptnetUrlCache')[0] = split(string=MetaPath,
                            sep='CryptnetUrlCache')[0]
        })