Intro
The Startup folder in Windows is a location that plays an important role in the functioning of a system. Essentially, it contains shortcuts to applications that are automatically launched when a user logs in to the system.
This can be a convenient way for IT teams to automate various tasks and ensure necessary software is ran. However, it is also a potential venue that can be used by adversaries to persist on a compromised system and execute malicious code.
What are Windows Startup Folders?
The Startup folder in Microsoft Windows is a location that contains shortcuts to applications that are automatically launched upon user login. There are two Startup folders in Windows:
One for the current user located at:
C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
One for all users located at:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
IT teams often leverage the Startup folder to automate various tasks and ensures that necessary software is started.
Adversaries can also use the Startup folder as a means of persistence on a compromised system as well. For example, an attacker may gain access to a system and place a malicious shortcut in the all-users Startup folder that references malware designed to steal sensitive information. Upon user login, the malware is automatically executed and begins to extract sensitive information, which the attacker can then use for malicious purposes.
Adversaries often choose to use the Startup folder as a method of persistence because it very easy to do and enables them to maintain their presence on a compromised system even if the user logs out or the system is restarted. If the adversary managed to escalate their privileges to an administrator on the system, placing the malicious shortcut in the all-users Startup folder would grant the attacker a guarantee that their malware is executed for all users on the system, not just the current user.
Script
This script will be valuable for incident response teams as it will gather all relevant information contained within the Startup folders, which is crucial for investigations. If the file is a shortcut, the script will map to the target file and extract additional file information if it is still accessible.
Output can be found at:
C:\temp\TA0003_Persistence\T1547.001_Startup_Folders.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.
$outputFolder = "C:\temp\TA0003_Persistence\"
if (!(Test-Path $outputFolder)) {
New-Item -ItemType Directory -Path $outputFolder | Out-Null
}
# Delete the existing output file if it exists
$outputFile = Join-Path $outputFolder "T1547.001_Startup_Folders.csv"
if (Test-Path $outputFile) {
Remove-Item $outputFile | Out-Null
}
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]
}
}
$results = @()
$lnkFile = New-Object -ComObject WScript.Shell
$users = Get-ChildItem "C:\Users"
foreach ($user in $users) {
if ($user.PSIsContainer) {
$startupFolder = Join-Path $user.FullName "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
if (Test-Path $startupFolder) {
Get-ChildItem $startupFolder | ForEach-Object {
$file = $_
$fileProperties = [ordered]@{
"FileName" = $file.FullName
"FileSize" = (Get-FormattedByteSize $file.Length)
"SHA256" = (Get-FileHash $file.FullName -Algorithm SHA256).Hash
"CreationTime" = $file.CreationTime
"LastModifiedTime" = $file.LastWriteTime
"LastAccessTime" = $file.LastAccessTime
}
if ($file.Extension -eq ".lnk") {
$shortcut = $lnkFile.CreateShortcut($file.FullName)
$target = $shortcut.TargetPath
if (Test-Path $target) {
$targetFile = Get-Item $target
$fileProperties["Target"] = $target
$fileProperties["TargetSize"] = (Get-FormattedByteSize $targetFile.Length)
$fileProperties["TargetHash"] = (Get-FileHash $target -Algorithm SHA256).Hash
$fileProperties["TargetCreationTime"] = $targetFile.CreationTime
$fileProperties["TargetLastModifiedTime"] = $targetFile.LastWriteTime
$fileProperties["TargetLastAccessTime"] = $targetFile.LastAccessTime
}
else {
$fileProperties["Target"] = "No longer exists"
$fileProperties["TargetSize"] = "N/A"
$fileProperties["TargetHash"] = "N/A"
$fileProperties["TargetCreationTime"] = "N/A"
$fileProperties["TargetLastModifiedTime"] = "N/A"
$fileProperties["TargetLastAccessTime"] = "N/A"
}
}
$results += New-Object PSObject -Property $fileProperties
}
}
}
}
# Gather information from the second file path
Get-ChildItem "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp" | ForEach-Object {
$file = $_
$fileProperties = [ordered]@{
"FileName" = $file.FullName
"FileSize" = (Get-FormattedByteSize $file.Length)
"SHA256" = (Get-FileHash $file.FullName -Algorithm SHA256).Hash
"CreationTime" = $file.CreationTime
"LastModifiedTime" = $file.LastWriteTime
"LastAccessTime" = $file.LastAccessTime
}
# If the file is an .lnk file, retrieve target information
if ($file.Extension -eq ".lnk") {
$shortcut = $lnkFile.CreateShortcut($file.FullName)
$target = $shortcut.TargetPath
if (Test-Path $target) {
$targetFile = Get-Item $target
$fileProperties["Target"] = $target
$fileProperties["TargetSize"] = (Get-FormattedByteSize $targetFile.Length)
$fileProperties["TargetHash"] = (Get-FileHash $target -Algorithm SHA256).Hash
$fileProperties["TargetCreationTime"] = $targetFile.CreationTime
$fileProperties["TargetLastModifiedTime"] = $targetFile.LastWriteTime
$fileProperties["TargetLastAccessTime"] = $targetFile.LastAccessTime
}
else {
$fileProperties["Target"] = "No longer exists"
$fileProperties["TargetSize"] = "N/A"
$fileProperties["TargetHash"] = "N/A"
$fileProperties["TargetCreationTime"] = "N/A"
$fileProperties["TargetLastModifiedTime"] = "N/A"
$fileProperties["TargetLastAccessTime"] = "N/A"
}
}
$results += New-Object PSObject -Property $fileProperties
$results = $results | Sort-Object -Property TargetCreationTime -Descending
}
# Output results to a CSV file
$results | Export-Csv -Path $outputFile -NoTypeInformation
Conclusion
The Startup folders serve as a great tool for both IT teams and adversaries. IT teams use it to automate tasks and ensure necessary software or scripts are ran, while adversaries use it as a means of persistence and to execute malicious code.
It is critical for blue teams to be aware of the potential risks associated with the Startup folder and to implement measures to secure these folders and prevent adversaries from using them as a means of persistence through monitoring the contents of the Startup folders.