Windows.Forensics.Shellbags

Windows uses the Shellbag keys to store user preferences for GUI folder display within Windows Explorer.

This artifact uses the raw registry parser to inspect various user registry hives around the filesystem for BagMRU keys. Different OS versions may have slightly different locations for the MRU keys.


name: Windows.Forensics.Shellbags
description: |
  Windows uses the Shellbag keys to store user preferences for GUI
  folder display within Windows Explorer.

  This artifact uses the raw registry parser to inspect various user
  registry hives around the filesystem for BagMRU keys. Different OS
  versions may have slightly different locations for the MRU keys.

reference:
  - https://www.sans.org/blog/computer-forensic-artifacts-windows-7-shellbags/

parameters:
  - name: SearchSpecs
    type: csv
    description: Define locations of MRU bags in various registries.
    default: |
      HiveGlob,KeyGlob
      C:/Users/*/NTUSER.dat,\Software\Microsoft\Windows\Shell\BagMRU\**
      C:/Users/*/AppData/Local/Microsoft/Windows/UsrClass.dat,\Local Settings\Software\Microsoft\Windows\Shell\BagMRU\**


imports:
  # Link files use the same internal format as shellbags so we import
  # the profile here.
  - Windows.Forensics.Lnk

sources:
  - query: |
       LET AllHives = SELECT *
         FROM foreach(row=SearchSpecs,
                      query={
           SELECT OSPath AS HivePath,
                  KeyGlob
           FROM glob(globs=HiveGlob)
           WHERE log(message="Inspecting hive %v", args=HivePath)
         })

       -- Form a lookup key based on the hive and the components.
       LET MakeKey(Hive, Components) = regex_replace(
           re="\\\\", replace="/", source=Hive) + join(array=Components, sep="/")

       LET ShellValues <= SELECT
           *, MakeKey(Hive=Hive, Components=Components) AS LookupKey
         FROM foreach(row=AllHives,
                      query={
           SELECT OSPath.DelegatePath AS Hive,
                  OSPath.Path AS RegValue,
                  OSPath.Components AS Components,
                  Data.value AS RawData,
                  parse_binary(profile=Profile,
                               filename=Data.value,
                               accessor="data",
                               struct="ItemIDList") AS _Parsed,
                  ModTime
           FROM glob(root=pathspec(DelegatePath=HivePath),
                     globs=KeyGlob,
                     accessor="raw_reg")
           WHERE Data.type =~ "BINARY"
            AND OSPath.Basename =~ "^[0-9]+$"
         })

       LET Lookup <= memoize(key="LookupKey", period=10000,
                             query={
           SELECT LookupKey,
                  _Parsed
           FROM ShellValues
         })

       LET GetRecord(Hive, Components) = get(
           item=Lookup, field=MakeKey(Hive=Hive, Components=Components))

       -- Recursive function to get the parents from the lookup cache.
       LET GetParents(Hive, Components) = SELECT
           LookupKey,
            _Parsed,
            _Parsed.ShellBag.Description.LongName ||
            _Parsed.ShellBag.Description.ShortName || "?" AS Name
         FROM chain(a={
             SELECT *
             FROM foreach(row=GetRecord(Hive=Hive, Components=Components))
         }, b={
             SELECT * FROM if(condition=Components,
             then={
                SELECT * FROM foreach(row=GetParents(Hive=Hive, Components=Components[:-1]))
             })
         })

         -- Shorter keys are higher in the directory heirarchy
         ORDER BY LookupKey

       // Compute the full path to the item by traversing the parents.
       LET GetFullPath(Hive, Components) = join(
           array=GetParents(Hive=Hive, Components=Components).Name, sep=" -> ")

       LET X = SELECT Hive,
                      dirname(path=RegValue, path_type="registry") AS KeyPath,
                      basename(path=RegValue) AS Slot,
                      GetFullPath(Hive=Hive, Components=Components) AS FullPath,
                      RawData AS _RawData,
                      ModTime,
                      get(item=Lookup, field=LookupKey)._Parsed AS _Parsed
         FROM ShellValues

       SELECT *, _Parsed.ShellBag.Description AS Description
       FROM X

column_types:
  - name: _RawData
    type: base64