Windows.Timeline.Registry.RunMRU

Output all available RunMRU registry keys in timeline format.

RunMRU is 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. A tickbox enables Grouped results with order in a single line.

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


name: Windows.Timeline.Registry.RunMRU
description: |
    # Output all available RunMRU registry keys in timeline format.

    RunMRU is 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.
    A tickbox enables Grouped results with order in a single line.

    Note: This artifact will collect RunMRU from ntuser.dat files and
    may exclude very recent entries in transaction (HKCU).  Future
    versions of this content will 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: |
        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)