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