Skip to content
Please update to the latest release 0.77.3 to address Multiple CVEs.
Windows.Timeline.Registry.RunMRU

Windows.Timeline.Registry.RunMRU

Extracts RunMRU registry entries from user hives and outputs them in timeline format.

RunMRU is updated when a user enters a command into the START > Run prompt. Entries will be logged in the user hive under: Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU

The artifact numbers all entries with the most recent at reg_mtime starting at 0. Second recent 1, Third recent 2 etc.

Default output enables a line per MRU entry. The boolean parameter groupResults enables Grouped results with ordering.

Note: This artifact will collect RunMRU from ntuser.dat files and may exclude very recent entries in transaction (HKCU). Future versions of this content might address this gap.


name: Windows.Timeline.Registry.RunMRU
description: |
  Extracts RunMRU registry entries from user hives and outputs them
  in timeline format.

  RunMRU is updated when a user enters a command into the START > Run
  prompt. Entries will be logged in the user hive under:
  Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU

  The artifact numbers all entries with the most recent at reg_mtime
  starting at 0. Second recent 1, Third recent 2 etc.

  Default output enables a line per MRU entry. The boolean parameter
  `groupResults` enables Grouped results with ordering.

  Note: This artifact will collect RunMRU from `ntuser.dat` files and
  may exclude very recent entries in transaction (HKCU).  Future
  versions of this content might address this gap.

author: Matt Green - @mgreen27

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

parameters:
  - name: KeyGlob
    type: hidden
    default: Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU\MRUList
  - name: dateAfter
    description: "search for events after this date. YYYY-MM-DDTmm:hh:ss Z"
    type: timestamp
  - name: dateBefore
    description: "search for events before this date. YYYY-MM-DDTmm:hh:ss Z"
    type: timestamp
  - name: targetUser
    description: "target user regex"
    type: regex
  - name: regexValue
    description: "regex search over RunMRU values."
    type: regex
  - name: groupResults
    description: "groups MRU entries to one message line"
    type: bool

sources:
 - query: |
        // linter: symbol_mask_warn:source|user

        LET hostname_lu <= SELECT Fqdn FROM info()
        LET HKEY_USERS <= pathspec(parse="HKEY_USERS", path_type="registry")

        // First we need to extract populated RunMRU
        LET MRUList <= SELECT OSPath,
           Data.value as RunMruOrder,
           len(list=Data.value) as RunMruLength,
           Username,
           UUID
        FROM Artifact.Windows.Registry.NTUser(KeyGlob=KeyGlob)

        // Now extract RunMRU entries and order
        LET results <= SELECT * FROM foreach(
           row=MRUList,
           query={
             SELECT
                OSPath.DelegatePath as source,
                Username,
                Mtime as reg_mtime,
                OSPath.Basename as reg_name,
                HKEY_USERS + UUID + OSPath.Dirname.Path as reg_key,

                -- Value data is similar to 'cmd.exe\1' so we just need the bit before the \
                regex_replace(source=Data.value, re="\\\\1$", replace="") as reg_value,
                Data.type as reg_type,
                RunMruLength - 1 - len(list=regex_replace(
                   source=RunMruOrder,
                   re="^.*" + OSPath.Basename,
                   replace="")) as mru_order,
                RunMruOrder
             FROM glob(globs='*', root=OSPath.Dirname, accessor="raw_reg")
             WHERE not reg_name = "MRUList" AND
                    if(condition=targetUser, then=Username =~ targetUser,
                        else=TRUE) AND
                    if(condition=dateAfter, then=reg_mtime > timestamp(string=dateAfter),
                        else=TRUE) AND
                    if(condition=dateBefore, then=reg_mtime < timestamp(string=dateBefore),
                        else=TRUE)
                    AND log(message=UUID)
             ORDER BY mru_order
          })

        // join mru values and order for presentation
        LET usercommands <= SELECT Username as user, mru_order,
                format(format="MRU%v: %v", args=[mru_order,reg_value]) as mru_grouped
        FROM results

        // Prepare join use case
        LET joinOut = SELECT
                reg_mtime as event_time,
                hostname_lu[0].Fqdn as hostname,
                "RunMRU" as parser,
                "RunMRU evidence user: " + Username + ", " +
                  join(array=mru_grouped, sep=" | ")  + "'" as message,
                source,
                Username as user
        FROM foreach(row=usercommands,
            query={
                SELECT *, Username,
                    {
                        SELECT mru_grouped
                        FROM usercommands
                        WHERE user = Username
                        ORDER BY mru_grouped
                    } as mru_grouped
                FROM results
                ORDER BY mru_grouped
            })
        GROUP BY source

        // Prepare split use case
        LET splitOut = SELECT
                    reg_mtime as event_time,
                    hostname_lu.Fqdn[0] as hostname,
                    "RunMRU" as parser,
                    "RunMRU evidence user: " + Username +
                        format(format=", order: %v, command: %v", args=[mru_order,reg_value])
                            + "'" as message,
                    source,
                    Username as user,
                    reg_key,
                    reg_mtime,
                    reg_name,
                    reg_value,
                    reg_type
            FROM results

        // Print out chosen usecase
        SELECT *
        FROM if(condition=groupResults,
            then={ SELECT * FROM joinOut},
            else={ SELECT * FROM splitOut})
        WHERE if(condition=regexValue, then=message =~ regexValue, else=TRUE)