To IEX or not to IEX

I’ve recently started some challenges on Root-Me, a non-profit organization which goal is to promote the spread of knowledge related to hacking and information security.

I’ve recently solved the following challenge where you need to find the password from an obfuscated PowerShell script.

It was clear that I’d use a dynamic approach versus a static code analysis approach. I already knew that obfuscation can have many layers.

I started a Windows Sandbox with the obfuscated script.

I had 3 ideas: 1. use the ScriptBlockLogging and read the log. 2. Install and use the Revoke-Obfuscation module 3. use another less known approach.

I followed and tested successfully all these 3 ideas.

I noticed that the script uses and abuses all forms of IEX (the alias of Invoke-Expression).

So, my 3rd approach was to replace the IEX alias by something else that would deobfuscate the code into layers. Unfortunately there’s no built-in Remove-Alias cmdlet in Windows PowerShell 5.1 to remove a read-only alias like there is in PS7.

Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
# 1. Remove the built-in read-only alias
dir alias:iex | del -Force
# Initialize a counter
$global:i = 0
# Replace iex by this function
Function iex {
Param(
[Parameter(Mandatory,ValueFromPipeline)]
$inputObject
)
$global:i++
echo "About to excute layer $global:i `n$('*'*20)`n$($inputObject)`n$('*'*20)`n"
$inputObject | Invoke-Expression
}
# then I ran the code from the challenge

I cannot give more explanation or show the solution. The only clue I can give is to start by the end (at layer 8) and read the deobfuscated code 😎

DCOM Hardening

KB5004442 Manage changes for Windows DCOM Server Security Feature Bypass (CVE-2021-26414) entered recently its last phase. As of March 14, 2023,

Phase 3 Release – Hardening changes enabled by default with no ability to disable them. By this point, you must resolve any compatibility issues with the hardening changes and applications in your environment.

There’s a tool available on GitHub spotted, but its logic is broken, it doesn’t correspond to the new behavior after March 14, 2023. The PowerShell code quality is quite low.

Well, telling you how to fix this code would be a very long story. Here’s my take on making it simple, using best practices PowerShell and what it should do in phase 3.

#Requires -RunAsAdmin
<#
.SYNOPSIS
Help evaluation the DCOM hardening status introduced by KB5004442
.DESCRIPTION
Help evaluation the DCOM hardening status introduced by KB5004442
.PARAMETER Enable
Switch to modify the registry and enable explictly the RequireIntegrityActivationAuthenticationLevel value
.PARAMETER OnlyShowLowAuthDcomApp
Switch to only get the list of low Authentication DCOM Applications from the WMI repository
.EXAMPLE
.\DCOM-Hardening.ps1 -Verbose
VERBOSE: Evaluating DCOM Hardening status
VERBOSE: RequireIntegrityActivationAuthenticationLevel value is: 1
VERBOSE: Hardening is enabled explicitly
VERBOSE: Value is already enabled for RequireIntegrityActivationAuthenticationLevel – no change is required
VERBOSE: Hardening raise value not present
VERBOSE: Hardening is enabled and raise value not present
.EXAMPLE
.\DCOM-Hardening.ps1 -Verbose -Enable
VERBOSE: Require value changed to 1 successfully
VERBOSE: Raise value RaiseActivationAuthenticationLevel deleted successfully
.EXAMPLE
.\DCOM-Hardening.ps1 -OnlyShowLowAuthDcomApp
Caption AppID AuthenticationLevel
——- —– ——————-
{42CBFAA7-A4A7-47BB-B422-BD10E9D02700} 2
UPnPContainer {6d8ff8e0-730d-11d4-bf42-00b0d0118b56} 0
UPnPContainer64 {6d8ff8e8-730d-11d4-bf42-00b0d0118b56} 0
AccStore Class {DE5DBCDC-104A-4cbc-A4D5-0C2104A142C5} 1
.NOTES
https://support.microsoft.com/en-us/topic/kb5004442-manage-changes-for-windows-dcom-server-security-feature-bypass-cve-2021-26414-f1400b52-c141-43d2-941e-37ed901c769c&quot;
https://techcommunity.microsoft.com/t5/windows-it-pro-blog/dcom-authentication-hardening-what-you-need-to-know/ba-p/3657154
DCOM client-side patch on November 8, 2022
This update will automatically raise authentication level for all non-anonymous activation requests from DCOM clients to RPC_C_AUTHN_LEVEL_PKT_INTEGRITY at a minimum.
With this change, most Windows DCOM clients will automatically work with DCOM hardening changes on the server side without any further modification to the DCOM client.
This update will be activated by default but can be deactivated by setting its registry key to 1.
This patch is disabled by default for Windows 10, versions 1809 and 1607 and Windows Server 2016.
To enable it, set the registry key value for RaiseActivationAuthenticationLevel to 2
Inspired from https://github.com/otoriocyber/DCOM-HardeningTool/blob/main/DisableDcomHardening.ps1
#>
[CmdletBinding(DefaultParameterSetName='__AllParameterSets')]
Param (
[Parameter(ParameterSetName='Set')]
[Switch]$Enable,
[Parameter(ParameterSetName='WMI')]
[Switch]$OnlyShowLowAuthDcomApp
)
Begin {
$RequireValueName ='RequireIntegrityActivationAuthenticationLevel'
$RaiseValueName = 'RaiseActivationAuthenticationLevel'
$HT = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Ole\AppCompat'
ErrorAction = 'Stop'
}
}
Process {}
End {
Switch ($PSCmdlet.ParameterSetName) {
WMI {
# Getting all low Authentication DCOM applications from WMI
try {
Get-CimInstance -Query 'SELECT * FROM Win32_DCOMApplicationSetting where AuthenticationLevel<5' -ErrorAction Stop -Verbose:$false|
Select-Object -Property Caption, AppID, AuthenticationLevel
} catch {
Write-Warning -Message "Failed to read WMI because $($_.Exception.Message)"
}
break
}
Set {
#region Enable
try {
# 1. Explicitly set Enable value
$null = New-ItemProperty -Name $RequireValueName -Value 1 -Type DWord -Force @HT
Write-Verbose -Message "Require value changed to 1 successfully"
# 2. Remove Raise value
$null = Remove-ItemProperty -Name $RaiseValueName -Force -ErrorAction SilentlyContinue -Path 'HKLM:\SOFTWARE\Microsoft\Ole\AppCompat'
Write-Verbose -Message "Raise value $($RaiseValueName) deleted successfully"
} catch {
Write-Warning -Message "Failed to set value because $($_.Exception.Message)"
}
#endregion
break
}
default {
Write-Verbose -Message 'Evaluating DCOM Hardening status'
#region RequireIntegrityActivationAuthenticationLevel
try{
$result = (Get-ItemProperty @HT -Name $RequireValueName).($RequireValueName)
} catch {
Write-Warning -Message "Failed to read value $($RequireValueName) because $($_.Exception.Message)"
}
if ($result) {
Write-Verbose -Message "$($RequireValueName) value is: $($result)"
Switch ($result) {
0 {
Write-Verbose -Message 'Hardening is disabled explicitly, value is ignored'
break
}
1 {
Write-Verbose -Message 'Hardening is enabled explicitly'
Write-Verbose -Message "Value is already enabled for $($RequireValueName) – no change is required"
break
}
default {
Write-Warning -Message "Unexpected result for $($RequireValueName) found $($result)"
}
}
} else {
Write-Verbose -Message 'Hardening value not present'
if ((Get-Date) -gt (Get-Date -Year 2023 -Month 3 -Day 14)) {
# Absent value means, hardening enabled by default
Write-Verbose -Message 'Hardening is enabled with value not present'
}
}
#endregion
#region Raise
$result = $null
$result = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Ole\AppCompat' -Name $RaiseValueName -ErrorAction SilentlyContinue).($RaiseValueName)
if ($result) {
Write-Verbose -Message "$($RaiseValueName) value is: $($result)"
Switch ($result) {
2 {
Write-Verbose -Message 'Activation Authentication Level is raised'
break
}
1 {
Write-Verbose -Message 'Activation Authentication Level is Default'
break
}
default {
Write-Warning -Message "Unexpected raise value found $($result)"
}
}
} else {
Write-Verbose -Message 'Hardening raise value not present'
if ((Get-Date) -gt (Get-Date -Year 2023 -Month 3 -Day 14)) {
# Absent value means, hardening enabled by default
Write-Verbose -Message 'Hardening is enabled and raise value not present'
}
}
#endregion
}
}
}

Let’s see how to use it.

There are 3 modes:

Just using the Verbose switch parameter will tell you how your computer is set for DCOM Hardening.

Using the OnlyShowLowAuthDcomApp will query the WMI repository looking for low authentication DCOM applications.

Using the Enable switch will set the RequireIntegrityActivationAuthenticationLevel value to 1 (enabled) and delete the RaiseActivationAuthenticationLevel value if found.

Patching CVE-2022-41099

Microsoft released a fix for CVE-2022-41099 (a BitLocker Security Feature Bypass Vulnerability) back in November 2022 and published on March 16, 2023 a sample script to help automate updating WinRE.

It helped understand how to find out if the WinRE is patched. It mounts the Winre.wim image and checks the version of bootmenuux.dll file under the $mountDir + “\Windows\System32\bootmenuux.dll”

Do you know that you can check the version of the file without mounting the WinRE.wim image ?

The first step is to get the WinRE.wim location from the running Windows OS using these 2 lines:

$null,$RELoc = ((& (Get-Command "$($env:systemroot)\system32\ReAgentC.exe") @('/info')) | 
Where-Object { $_ -match 'Windows\sRE\slocation:' } ) -split ':'
$RELoc = $RELoc.Trim()

The 2nd step consists in listing the content of the image using the built-in Get-WindowsImageContent cmdlet

The basic idea is the following:

Get-WindowsImageContent -ImagePath "$($RELoc)\winre.wim" -Index 1 | Where { $_ -match 'BootMenuUX' } |
Out-GridView -PassThru

Now, how do I extract the correct latest version from this list of file names.

Well, with the following oneliner:

$null,$RELoc = ((& (Get-Command "$($env:systemroot)\system32\ReAgentC.exe") @('/info')) | Where-Object { $_ -match 'Windows\sRE\slocation:' } ) -split ':'
$RELoc = $RELoc.Trim()
Get-WindowsImageContent -ImagePath "$($RELoc)\winre.wim" -Index 1 |
Where-Object { $_ -match 'Windows\\WinSxS\\amd64_microsoft-windows-bootmenuux_.+_(?<Version>(\d{1,5}\.)+\d{1,5})_.+\\BootMenuUX\.dll'} |
ForEach-Object { ([regex]'Windows\\WinSxS\\amd64_microsoft-windows-bootmenuux_.+_(?<Version>(\d{1,5}\.)+\d{1,5})_.+\\BootMenuUX\.dll').Matches($_) |
Select-Object -Expand Groups | Where-Object Name -eq 'Version' | Select-Object -ExpandProperty Value}| ForEach-Object { try {[version]$_}catch{ 'Failed'}} |
Sort-Object -Descending | Select-Object -First 1 | ForEach-Object ToString

I’ve got 10.0.19041.2247 version installed and it means my WinRE.wim is patched 😎