Intro
During an incident, it is imperative to gather as much information as possible to establish a comprehensive timeline of events. One crucial aspect of information collection is identifying the most recent files and directories on the impacted host found in %AppData%\Roaming\Microsoft\Windows\Recent
. This information plays a crucial role in helping to understand the sequence of events that took place and to support the overall timeline.
The %AppData%\Roaming\Microsoft\Windows\Recent
directory holds information about recently used files and directories as shortcut files.
Explained
The shortcut files in the %AppData%\Roaming\Microsoft\Windows\Recent directory are jump list shortcuts. Jump lists are a feature in Microsoft Windows that provide quick access to frequently used items. You might be familiar with how jump lists work if you open the Start menu and see a recent application you used sitting there at the top of the list or going into Quick Access in File Explorer to find your recent items.
Jump list shortcuts are essentially links to items that are stored in the jump list. When a user opens an item (meaning a program, directory, or file), a shortcut is created. The next time the user wants to access that item, they can simply click on the shortcut in the jump list. The shell:recent
command in the Run dialog box can also be used to quickly access the Recent items used on the system.

Blue Team Perspective
Incident response teams want to know the recent files and directories on a system as part of their efforts to piece together a complete timeline of events during an incident. With this information, security teams can determine what files and directories were used or modified recently, which can help them understand the scope of an incident and identify relevant information. This data will help blue teams determine the extent of any damage or assess the impact on the system and its users.
Red Team Perspective
Adversaries may seek to obtain information about recent files and directories on a target system as part of their efforts to access sensitive information. By analyzing this data, they may be able to identify files and directories that have been recently used or modified, which can indicate areas of the system that may contain valuable or vulnerable information.
In cases where initial access was not detected by the security team, adversaries may use this information to create a more convincing presence on the system, such as mimicking the names of files and directories used by the compromised system. This can help them to evade detection and remain active on the system for an extended period of time.
Script
This script will collect information on each shortcut file in %AppData%\Roaming\Microsoft\Windows\Recent
and extract as much useful information about it as possible for incident response teams. The output file, if ran as is, can be found at C:\temp\recent_files_and_directories.csv
.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Function to get formatted byte size
function Get-FormattedByteSize {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true)]
[double]$ByteSize
)
$SizeUnits = @("bytes", "KB", "MB", "GB", "TB", "PB")
$ByteSize | ForEach-Object {
$UnitIndex = 0
$Size = [math]::Round($_, 2)
while ($Size -ge 1KB) {
$Size = $Size / 1KB
$UnitIndex++
}
"{0:N2} {1}" -f $Size, $SizeUnits[$UnitIndex]
}
}
# Output file
$output_files_for_recents = 'C:\temp\recent_files_and_directories.csv'
# Get users on the system
$users = Get-ChildItem C:\Users -ErrorAction SilentlyContinue
# Array to store the results
$results = @()
# Loop through each user
foreach ($user in $users) {
# Get the list of items in the Recent folder for the current user
$items = Get-ChildItem "$($user.FullName)\AppData\Roaming\Microsoft\Windows\Recent" -ErrorAction SilentlyContinue
# Loop through Recent folder
foreach ($item in $items) {
# Skip
if ($item.Name -eq "AutomaticDestinations" -or $item.Name -eq "CustomDestinations") {
continue
}
# Store the item's properties
$obj = New-Object -TypeName PSObject
# Add the username, Mode, LastWriteTime, Length, and Name to the object
$obj | Add-Member -MemberType NoteProperty -Name "Username" -Value $user.Name
$obj | Add-Member -MemberType NoteProperty -Name "Mode" -Value $item.Attributes
$obj | Add-Member -MemberType NoteProperty -Name "LastWriteTime" -Value $item.LastWriteTime
# Get size
$size = Get-FormattedByteSize -ByteSize $item.Length -ErrorAction SilentlyContinue
$obj | Add-Member -MemberType NoteProperty -Name "Length" -Value $size
# find the target file
if ($item.Extension -eq ".lnk") {
$shortcut = (New-Object -ComObject WScript.Shell).CreateShortcut($item.FullName)
$targetFile = $shortcut.TargetPath
if (![string]::IsNullOrEmpty($targetFile)) {
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $item.Name
$obj | Add-Member -MemberType NoteProperty -Name "Target" -Value $targetFile
# Column for target size
if ((Get-Item $targetFile -ErrorAction SilentlyContinue).PSIsContainer) {
$targetSize = ""
}
else {
$targetSize = Get-FormattedByteSize -ByteSize (Get-Item $targetFile -ErrorAction SilentlyContinue).Length -ErrorAction SilentlyContinue
}
$obj | Add-Member -MemberType NoteProperty -Name "TargetSize" -Value $targetSize
# Add column for target type (file or directory)
if ((Get-Item $targetFile -ErrorAction SilentlyContinue).PSIsContainer) {
$targetType = "Directory"
}
else {
$targetType = "File"
}
$obj | Add-Member -MemberType NoteProperty -Name "TargetType" -Value $targetType
# If target still exists
if (Test-Path $targetFile -ErrorAction SilentlyContinue) {
$exists = "TRUE"
# Get target timestamps
$lastAccessTime = (Get-Item $targetFile -ErrorAction SilentlyContinue).LastAccessTime
$lastWriteTime = (Get-Item $targetFile -ErrorAction SilentlyContinue).LastWriteTime
$creationTime = (Get-Item $targetFile -ErrorAction SilentlyContinue).CreationTime
# Add target timestamp columns
$obj | Add-Member -MemberType NoteProperty -Name "TargetLastAccessTime" -Value $lastAccessTime
$obj | Add-Member -MemberType NoteProperty -Name "TargetLastWriteTime" -Value $lastWriteTime
$obj | Add-Member -MemberType NoteProperty -Name "TargetCreationTime" -Value $creationTime
}
else {
$exists = "FALSE"
}
$obj | Add-Member -MemberType NoteProperty -Name "Exists" -Value $exists
# Get hash
$hash = (Get-FileHash -Path $targetFile -Algorithm SHA256 -ErrorAction SilentlyContinue).Hash
$obj | Add-Member -MemberType NoteProperty -Name "Hash" -Value $hash
}
else {
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $item.Name
$obj | Add-Member -MemberType NoteProperty -Name "Target" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "TargetSize" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "TargetType" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "Exists" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "TargetLastAccessTime" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "TargetLastWriteTime" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "TargetCreationTime" -Value ""
$obj | Add-Member -MemberType NoteProperty -Name "Hash" -Value ""
}
# Add results
$results += $obj
}
}
}
# Write results
$results | Select-Object Username, Mode, Name, LastWriteTime, Length, Target, TargetSize, TargetType, Exists, TargetLastAccessTime, TargetLastWriteTime, TargetCreationTime, Hash | Export-Csv -Path $output_files_for_recents -NoTypeInformation
Conclusion
%AppData%\Roaming\Microsoft\Windows\Recent
can be used by incident response teams to collect useful information about activity that was accessed on a system.
By identifying when particular files or directories were accessed, incident responders can construct a more precise timeline of events, which enables them to obtain a better understanding of the attack’s progression, the sequence of actions performed by the attacker, and potentially identifying the attacker’s goals and what crown jewels may be targeted.