Skip to content
Please update to the latest release 0.77.2 to address Multiple CVEs.
Windows.Forensics.CertUtil

Windows.Forensics.CertUtil

Extracts download metadata from the Windows Certutil CryptnetUrlCache to reveal LOLBin downloads.

The Windows Certutil binary is capable of downloading arbitrary files. Attackers typically use it to fetch tools undetected when using “Living off the Land” (LOL) techniques.

Certutil maintains a cache of the downloaded files and this contains valuable metadata. The artifact parses this metadata to establish what was downloaded and when.


name: Windows.Forensics.CertUtil
description: |
  Extracts download metadata from the Windows Certutil
  CryptnetUrlCache to reveal LOLBin downloads.

  The Windows Certutil binary is capable of downloading arbitrary
  files. Attackers typically use it to fetch tools undetected when
  using "Living off the Land" (LOL) techniques.

  Certutil maintains a cache of the downloaded files and this contains
  valuable metadata. The artifact parses this metadata to establish
  what was downloaded and when.

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

parameters:
  - name: MinSize
    type: int
    description: Only show contents larger than this size.
  - name: URLWhitelist
    type: csv
    default: |
      URL
      http://sf.symcd.com
      http://oneocsp.microsoft.com
      http://certificates.godaddy.com
      http://ocsp.pki.goog
      http://repository.certum.pl
      http://www.microsoft.com
      http://ocsp.verisign.com
      http://ctldl.windowsupdate.com
      http://ocsp.sectigo.com
      http://ocsp.usertrust.com
      http://ocsp.comodoca.com
      http://cacerts.digicert.com
      http://ocsp.digicert.com
  - name: MetadataGlobUser
    default: C:/Users/*/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/*
  - name: MetadataGlobSystem
    default: C:/Windows/*/config/systemprofile/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/*
  - name: AlsoUpload
    type: bool

  - name: VSSAnalysisAge
    type: int
    default: 0
    description: |
      If larger than zero we analyze VSS within this many days
      ago. (e.g 7 will analyze all VSS within the last week).  Note
      that when using VSS analysis we have to use the ntfs accessor
      for everything which will be much slower.

  - name: DISABLE_DANGEROUS_API_CALLS
    type: bool
    description: |
      Enable this to disable potentially flakey APIs which may cause
      crashes.


sources:
  - query: |
      LET VSS_MAX_AGE_DAYS <= VSSAnalysisAge
      LET Accessor = if(condition=VSSAnalysisAge > 0, then="ntfs_vss", else="auto")

      LET Profile = '[
        ["Header", 0, [
          ["UrlSize", 12, "uint32"],
          ["HashSize", 100, "uint32"],
          ["DownloadTime", 16, "uint64"],
          ["FileSize", 112, "uint32"],
          ["URL", 116, "String", {
              "encoding": "utf16",
              "length": "x=>x.UrlSize"
          }],
          ["Hash", "x=>x.UrlSize + 116", "String", {
              "encoding": "utf16",
              "length": "x=>x.HashSize"
          }]
        ]]
      ]'

      -- Build a whitelist regex
      LET URLRegex <= "^" + join(array=URLWhitelist.URL, sep="|")
      LET Files = SELECT OSPath,

          -- Parse each metadata file.
          parse_binary(filename=OSPath, accessor=Accessor,
                       profile=Profile,
                       struct="Header") AS Header,

          -- The content is kept in the Content directory.
          OSPath.Dirname.Dirname + "Content" + OSPath.Basename AS _ContentPath,
          read_file(length=4, accessor=Accessor,
                filename=OSPath.Dirname.Dirname + "Content" + OSPath.Basename) AS ContentHeader
      FROM glob(globs=[MetadataGlobUser, MetadataGlobSystem], accessor=Accessor)
      WHERE Header.FileSize > MinSize

      SELECT OSPath AS _MetadataFile, _ContentPath,
               if(condition=AlsoUpload, then=upload(file=OSPath, accessor=Accessor)) AS _MetdataUpload,
               if(condition=AlsoUpload, then=upload(file=_ContentPath, accessor=Accessor)) AS _Upload,
               Header.URL AS URL,
               url(parse=Header.URL).Host AS UrlTLD,
               Header.FileSize AS FileSize,
               regex_replace(re='"', replace="", source=Header.Hash) AS Hash,
               timestamp(winfiletime=Header.DownloadTime) AS DownloadTime,
               if(condition= ContentHeader=~ 'MZ',
                    then= parse_pe(file= _ContentPath, accessor=Accessor).VersionInformation,
                    else= 'N/A' ) as VersionInformation,
               if(condition= ContentHeader=~ 'MZ',
                    then= authenticode(filename= _ContentPath, accessor=Accessor),
                    else= 'N/A' ) as Authenticode

      FROM Files
      WHERE NOT URL =~ URLRegex