This artifact extracts RecentDocs MRU from the target.
By default the artifact will target all users on the machine when run in live mode but can be targeted directly using the HiveGlob parameter.
Output includes LastWriteTime of key and a list of MRU items in the order specified in the MRUListEx key value. MruEntries has the format: [KeyName] := [Parsed Key value]
Available filters include: - Time bounds to select LastWrite timestamp within time ranges. - EntryRegex to target specific entry values - UserRegex to target specific users. Note: this filter does not work when using HiveGlob. - SidRegex to target a specific SID.
Note: both UserRegex and SidRegex does not work when using HiveGlob and all MRU will be returned.
name: Windows.Registry.RecentDocs
author: Matt Green - @mgreen27
description: |
This artifact extracts RecentDocs MRU from the target.
By default the artifact will target all users on the machine when run in
live mode but can be targeted directly using the HiveGlob parameter.
Output includes LastWriteTime of key and a list of MRU items in the
order specified in the MRUListEx key value.
MruEntries has the format: [KeyName] := [Parsed Key value]
Available filters include:
- Time bounds to select LastWrite timestamp within time ranges.
- EntryRegex to target specific entry values
- UserRegex to target specific users. Note: this filter does not work
when using HiveGlob.
- SidRegex to target a specific SID.
Note: both UserRegex and SidRegex does not work when using HiveGlob
and all MRU will be returned.
parameters:
- name: KeyGlob
type: hidden
default: Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\**
- name: HiveGlob
description: "optional hive glob to target for offline processing."
- name: DateAfter
description: "search for events after this date. YYYY-MM-DDTmm:hh:ssZ"
type: timestamp
- name: DateBefore
description: "search for events before this date. YYYY-MM-DDTmm:hh:ssZ"
type: timestamp
- name: EntryRegex
default: .
description: "regex filter for document/entry name."
- name: UserRegex
default: .
description: "regex filter for username over standard query."
- name: SidRegex
default: .
description: "regex filter for user SID over standard query."
- name: Profile
type: hidden
default: |
[
["Target", 0, [
["Filename", 0, "String", {
encoding: "utf16",
}],
]]
]
sources:
- query: |
-- time testing
LET time_test(stamp) =
if(condition= DateBefore AND DateAfter,
then= stamp < DateBefore AND stamp > DateAfter,
else=
if(condition=DateBefore,
then= stamp < DateBefore,
else=
if(condition= DateAfter,
then= stamp > DateAfter,
else= True
)))
-- dynamic function to extract RecentDocs order from MRUListEx data value
LET find_order(value) = SELECT
parse_binary(accessor='data',
filename=substr(str=value,start=_value,end=_value + 4),
struct='uint32') as Int
FROM range(end=len(list=value),start=0,step=4)
WHERE NOT Int = 4294967295
-- NTUser method is most accurate
LET NTUserValues = SELECT
Mtime,
OSPath.Components[-2] AS Type,
OSPath.Components[-1] AS Name,
if(condition= OSPath.Basename = 'MRUListEx',
then= find_order(value=Data.value).Int,
else= parse_binary(
accessor="data",
filename=Data.value,
profile=Profile, struct="Target").Filename ) as Value,
Data,
OSPath.DelegatePath as HiveName,
OSPath,
Username,
UUID
FROM Artifact.Windows.Registry.NTUser(KeyGlob=KeyGlob)
WHERE Username =~ UserRegex
AND UUID =~ SidRegex
AND Data.type =~ 'BINARY'
-- Glob method allows offline processing but can not filter by user
LET GlobValues = SELECT
Mtime,
OSPath.Components[-2] AS Type,
OSPath.Components[-1] AS Name,
if(condition= OSPath.Basename = 'MRUListEx',
then= find_order(value=Data.value).Int,
else= parse_binary(
accessor="data",
filename=Data.value,
profile=Profile,
struct="Target").Filename ) as Value,
Data,
OSPath.DelegatePath as HiveName,
OSPath
FROM glob(
globs=KeyGlob,
root=pathspec(DelegatePath=HiveGlob),
accessor="raw_reg")
WHERE Data.type =~ 'BINARY'
-- precalculate all hive values for performance
LET AllValues <= SELECT * FROM if(condition= HiveGlob,
then={ SELECT * FROM GlobValues},
else={ SELECT * FROM NTUserValues} )
WHERE time_test(stamp=Mtime)
-- memorise for lookup / performance
LET Items <= memoize(query={
SELECT Type, Name, Value,
Type + ':' + Name + ':' + HiveName AS Key
FROM AllValues
}, key="Key")
-- flattern output then add lookup of processed data
LET flat_data(type,hivename) = SELECT *,
str(str=Value) + ' := ' +
get(item=Items, field=str(str=Type) + ':' +
str(str=Value) + ':' + str(str=hivename) ).Value AS Value
FROM flatten(query={
SELECT Mtime, Type, Name, Value,HiveName
FROM AllValues
WHERE Name = 'MRUListEx'
AND Type = type AND HiveName = hivename
})
GROUP BY Value
-- prep results
LET results = SELECT Mtime as LastWriteTime, Type,
flat_data(type=Type, hivename=HiveName).Value as MruEntries,
OSPath.Path as Key,
HiveName,
if(condition=HiveGlob,
then='', else=Username) as Username,
if(condition=HiveGlob,
then='', else=UUID) as UUID
FROM AllValues
WHERE Name = 'MRUListEx'
-- print rows, remove Username/SID from offline
SELECT * FROM if(condition=HiveGlob,
then = {
SELECT LastWriteTime, Type,
if(condition= NOT MruEntries[0],
then= Null,
else= MruEntries) as MruEntries,
Key, HiveName
FROM results
},
else={
SELECT LastWriteTime, Type,
if(condition= NOT MruEntries[0],
then= Null,
else= MruEntries) as MruEntries,
Key, HiveName, Username, UUID
FROM results
})
WHERE format(format='%v', args=MruEntries) =~ EntryRegex