Site icon Infused Innovations

ASRmageddon: What Happened and How We Responded

Image of sitting at the computer dealing with ASRmageddon

On January 13th, Microsoft deployed a new definitions update for Microsoft Defender. This caused an issue for some users, as Defender began to flag and delete shortcuts in the “C:\ProgramData\Microsoft\Windows\Start Menu\Programs” folder on devices with the Attack Surface Reduction (ASR) rule “Block Win32 API calls from Office macro” set to Block. The widespread nature of this problem prompted the nickname “ASRmageddon” among technology providers and those affected.

Upon discovering the issue, Infused Innovations quickly took action to mitigate the effects of the problem. We first changed the problematic ASR rule to Audit only for our clients and reached out to make them aware of the situation. This helped to prevent further damage and gave us the opportunity to assess the impact of the issue on our clients’ systems. 

Once the rule was updated, we created and pushed out a script to restore the most common applications used by our clients, focusing on trying to get them back up and running quickly. Our goal was to minimize disruption to our clients’ operations and help them to return to normal as soon as possible. 

Through our efforts, we were able to restore the majority of our clients’ shortcuts before Microsoft released their first remediation script. At the time, Microsoft’s script fixed fewer shortcuts than ours, and we provided our clients with a more comprehensive solution. 

We continued to iterate on our script to restore as much as possible for our clients, while keeping an eye on any new developments from Microsoft and the IT community at large. Our team worked continuously to ensure that our clients were not negatively impacted by this issue for long. 

Overall, the incident serves as a reminder of the importance of staying up to date on the latest updates and developments in the IT industry. It also highlights the importance of having a well-trained and responsive IT team that can quickly respond to and resolve issues as they arise

At Infused, we pride ourselves on providing comprehensive IT solutions and exceptional customer service. We will continue to monitor the situation and work closely with our clients to ensure that their systems are fully restored and that any future issues are avoided. 

We would like to thank our clients for their patience and understanding during this incident and assure them that we are always available to answer any questions or concerns they may have. 

For those who are interested or still need to fix this bug, see our script below.

Script to Restore Shortcuts Deleted by Defender ASR Bug MO497128 #ASRmageddon

<#
.SYNOPSIS
    Script to restore shortcuts deleted by Defender ASR Bug MO497128 #ASRmageddon

.DESCRIPTION
    This script will check to see if a Volume Shadow Service restore point is available from before the deletions happened. 
    If a restore point is available, it will try to restore the shortcuts to the start menu and users' desktop folders.
    If there's no available restore points, it will check to see if there's a payload deployed alongside the script with shortcuts included, 
    if there is, it will restore the missing shortcuts included in the payload for programs that are installed.

    To create the payload, include any shortcuts you want to deploy in a archive named "Programs.zip"

.NOTES
    Written by Alex Clayton - 1/2023

#>

$StartMenuFolder = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs"

$ShadowCopyMountPath = "C:\VSSShortcutRestore"

$DateTime = [datetime]'01/13/2023 09:00:00'

$ToTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById((Get-Timezone).Id)

$ConvertedTime = ([System.TimeZoneInfo]::ConvertTimeFromUtc($DateTime ,$ToTimeZone))

$ShadowCopies = Get-CIMInstance -Class Win32_ShadowCopy | Where-Object {$_.InstallDate -lt $ConvertedTime} | Sort-Object -Property InstallDate -Descending

Function Mount-VolumeShadowCopy {

<#

.SYNOPSIS

Mount a volume shadow copy.

.DESCRIPTION

Mount a volume shadow copy.

.PARAMETER ShadowPath

Path of volume shadow copies submitted as an array of strings

.PARAMETER Destination

Target folder that will contain mounted volume shadow copies

.EXAMPLE

Get-CimInstance -ClassName Win32_ShadowCopy |

Mount-VolumeShadowCopy -Destination C:\VSS -Verbose

#>

[CmdletBinding()]

Param(

[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]

[ValidatePattern('\\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy\d{1,}')]

[Alias("DeviceObject")]

[String[]]$ShadowPath,

[Parameter(Mandatory)]

[ValidateScript({

Test-Path -Path $_ -PathType Container

}

)]

[String]$Destination

)

Begin {

Try {

$null = [mklink.symlink]

}

Catch {

Add-Type @"

using System;

using System.Runtime.InteropServices;

namespace mklink

{

public class symlink

{

[DllImport("kernel32.dll")]

public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);

}

}

"@

}

}

Process {

$ShadowPath | ForEach-Object -Process {

if ($($_).EndsWith("\")) {

$sPath = $_

}

else {

$sPath = "$($_)\"

}

$tPath = Join-Path -Path $Destination -ChildPath (

'{0}-{1}' -f (Split-Path -Path $sPath -Leaf), [GUID]::NewGuid().Guid

)

try {

if (

[mklink.symlink]::CreateSymbolicLink($tPath, $sPath, 1)

) {

Write-Verbose "`tSuccessfully mounted $sPath to $tPath"

return $tPath

}

else {

Write-Warning "[!] Failed to mount $sPath"

}

}

catch {

Write-Warning "[!] Failed to mount $sPath because $($_.Exception.Message)"

}

}

}

End {}

}

Function Dismount-VolumeShadowCopy {

<#

.SYNOPSIS

Dismount a volume shadow copy.

.DESCRIPTION

Dismount a volume shadow copy.

.PARAMETER Path

Path of volume shadow copies mount points submitted as an array of strings

.EXAMPLE

Get-ChildItem -Path C:\VSS | Dismount-VolumeShadowCopy -Verbose

#>

[CmdletBinding()]

Param(

[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]

[Alias("FullName")]

[string[]]$Path

)

Begin {

}

Process {

$Path | ForEach-Object -Process {

$sPath = $_

if (Test-Path -Path $sPath -PathType Container) {

if ((Get-Item -Path $sPath).Attributes -band [System.IO.FileAttributes]::ReparsePoint) {

try {

[System.IO.Directory]::Delete($sPath, $false) | Out-Null

Write-Verbose "`tSuccessfully dismounted $sPath"

}

catch {

Write-Warning "[!] Failed to dismount $sPath because $($_.Exception.Message)"

}

}

else {

Write-Warning "[!] The path $sPath isn't a reparsepoint"

}

}

else {

Write-Warning "[!] The path $sPath isn't a directory"

}

}

}

End {}

}

function Restore-StartMenuShortcuts {

[CmdletBinding()]

param (

[Parameter(ValueFromPipeline)]

$InputObject

)

process {

$InputObject | ForEach-Object {

$relativePath = ($_.fullname -split('Programs'))[1]

If(test-path ($StartMenuFolder + $relativepath)) {

"$($_.name) already exist in start menu"

}

else {

"$($_.name) not found in start menu - checking if program pointed to by shortcut exist"

$sh = New-Object -ComObject WScript.Shell

if(Test-Path($sh.CreateShortcut($_.FullName).TargetPath)){

"Program exists - copying $($_.Name) into start menu folder"

Copy-Item -Path $_.FullName -Destination "$StartMenuFolder$relativepath" -Force

}

else {

"Did not find $($sh.CreateShortcut($_.FullName).TargetPath) - will not copy $($_.name)"

}

}

}

}

}

function Restore-DesktopShortcuts {

[CmdletBinding()]

param (

[Parameter(ValueFromPipeline)]

$InputObject

)

process {

$InputObject | ForEach-Object {

$relativePath = ($_.FullName -split('Users'))[1]

If(test-path ("C:\users$relativepath")) {

"$RelativePath already exists"

}

else {

"$RelativePath not found checking if program pointed to by shortcut exist"

$sh = New-Object -ComObject WScript.Shell

if(Test-Path($sh.CreateShortcut($_.FullName).TargetPath)){

"Program exists - copying $RelativePath to users' desktop"

Copy-Item -Path $_.FullName -Destination "C:\users$relativepath" -ErrorAction:Continue

}

else {

"Did not find $($sh.CreateShortcut($_.FullName).TargetPath) - will not copy $($_.name)"

}

}

}

}

}

If ($null -ne $ShadowCopies) {

New-Item -Path $ShadowCopyMountPath -ItemType Directory -Force | Out-Null

If ($ShadowCopies -is [array]) {

$ShadowCopy = $ShadowCopies[0] | Mount-VolumeShadowCopy -Destination $ShadowCopyMountPath

} elseif ($ShadowCopies -is [object]) {

$ShadowCopy = $ShadowCopies | Mount-VolumeShadowCopy -Destination $ShadowCopyMountPath

} else {

Remove-Item -Path $ShadowCopyMountPath -Recurse -Force | Out-Null

Write-Warning -Message "Error with parsing the available shadow copies. Exiting."

Exit;

}

$ShortCuts = Get-ChildItem -Path "$ShadowCopy\ProgramData\Microsoft\Windows\Start Menu\Programs" -Recurse -File

$ShortCuts | Restore-StartMenuShortcuts

$UserProfiles = Get-ChildItem -Path "$ShadowCopy\Users"

$UserProfiles | ForEach-Object {

Get-ChildItem $_.FullName -Filter "*Desktop" -directory -Depth 1 | ForEach-Object {

$DesktopShortcutsLNK = Get-ChildItem -Path $_.FullName -File -Filter "*.lnk"

$DesktopShortcutsURL = Get-ChildItem -Path $_.FullName -File -Filter "*.url"

$DesktopShortcutsLNK | Restore-DesktopShortcuts

$DesktopShortcutsURL | Restore-DesktopShortcuts

}

}

Get-ChildItem -Path $ShadowCopyMountPath | Dismount-VolumeShadowCopy -Verbose

Remove-Item -Path $ShadowCopyMountPath -Recurse -Force | Out-Null

} ElseIf (Test-Path -Path "$PSScriptRoot\Programs.zip") {

Expand-Archive -Path "$PSScriptRoot\Programs.zip" -DestinationPath "$PSScriptRoot" -Force

$ShortCuts = Get-ChildItem "$PSScriptRoot\Programs" -Recurse -File

$ShortCuts | Restore-StartMenuShortcuts

} Else {

Write-Warning -Message "No restore points found. Exiting."

Exit;

}

Credits:

Exit mobile version