Skip to content

Commit

Permalink
Add multithreading to COM class image file permission check
Browse files Browse the repository at this point in the history
  • Loading branch information
itm4n committed Dec 27, 2024
1 parent e96a52f commit 43cb6eb
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 56 deletions.
5 changes: 5 additions & 0 deletions info/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

- Support for multithreading.

### Changed

- Add multithreading to COM class registry path permission check.
- Add multithreading to COM class image file permission check.

## 2024-12-25

### Added
Expand Down
64 changes: 9 additions & 55 deletions src/check/Configuration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ function Invoke-ComServerRegistryPermissionCheck {
process {
Get-ComClassFromRegistry |
Where-Object { ($_.Value -like "*server*") -and ($null -ne $_.Path) } |
Invoke-CommandMultithread -InitialSessionState $(Get-InitialSessionState) -Command "Get-ModifiableComClassEntry" -InputParameter "ComClassEntry" |
Invoke-CommandMultithread -InitialSessionState $(Get-InitialSessionState) -Command "Get-ModifiableComClassEntryRegistryPath" -InputParameter "ComClassEntry" |
ForEach-Object { $AllResults += $_ }

$CheckResult = New-Object -TypeName PSObject
Expand Down Expand Up @@ -951,64 +951,18 @@ function Invoke-ComServerImagePermissionCheck {

begin {
$AllResults = @()
$AlreadyCheckedPaths = @()
$FsRedirectionValue = Disable-Wow64FileSystemRedirection
# Create a synchronized list that we will use to store file paths which were
# tested and are not vulnerable. This list will be populated by the threads,
# hence why we need to use thread-safe collection object.
$AlreadyCheckedPaths = [System.Collections.ArrayList]::Synchronized((New-Object System.Collections.ArrayList))
}

process {
$RegisteredClasses = Get-ComClassFromRegistry | Where-Object { ($_.Value -like "*server*") -and ($null -ne $_.Path) -and ($null -ne $_.Data) }

foreach ($RegisteredClass in $RegisteredClasses) {

$CandidatePaths = @()

switch ($RegisteredClass.DataType) {
"FileName" {
Resolve-ModulePath -Name $RegisteredClass.Data | ForEach-Object { $CandidatePaths += $_ }
}
"FilePath" {
$CandidatePaths += [System.Environment]::ExpandEnvironmentVariables($RegisteredClass.Data).Trim('"')
}
"CommandLine" {
$CommandLineResolved = [string[]] (Resolve-CommandLine -CommandLine $RegisteredClass.Data)
if ($null -eq $CommandLineResolved) { continue }

$CandidatePaths += $CommandLineResolved[0]

if (($CommandLineResolved[0] -match ".*rundll32(\.exe)?`$") -and ($CommandLineResolved.Count -gt 1) -and ($CommandLineResolved[1] -like "*.dll,*")) {
$PathToAnalyze = $CommandLineResolved[1].Split(',')[0]
if ([System.IO.Path]::IsPathRooted($PathToAnalyze)) {
$CandidatePaths += $PathToAnalyze
}
else {
Resolve-ModulePath -Name $PathToAnalyze | ForEach-Object { $CandidatePaths += $_ }
}
}
}
default {
Write-Warning "Unknown server data type: $($RegisteredClass.DataType)"
continue
}
}

foreach ($CandidatePath in $CandidatePaths) {

if ([String]::IsNullOrEmpty($CandidatePath)) { continue }
if ($AlreadyCheckedPaths -contains $CandidatePath) { continue }

$ModifiablePaths = Get-ModifiablePath -Path $CandidatePath | Where-Object { $_ -and (-not [String]::IsNullOrEmpty($_.ModifiablePath)) }
if ($null -eq $ModifiablePaths) { $AlreadyCheckedPaths += $CandidatePath; continue }

foreach ($ModifiablePath in $ModifiablePaths) {

$Result = $RegisteredClass.PSObject.Copy()
$Result | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $ModifiablePath.ModifiablePath
$Result | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $ModifiablePath.IdentityReference
$Result | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value ($ModifiablePath.Permissions -join ", ")
$AllResults += $Result
}
}
}
Get-ComClassFromRegistry |
Where-Object { ($_.Value -like "*server*") -and ($null -ne $_.Path) -and ($null -ne $_.Data) } |
Invoke-CommandMultithread -InitialSessionState $(Get-InitialSessionState) -Command "Get-ModifiableComClassEntryImagePath" -InputParameter "ComClassEntry" -OptionalParameter @{ "CheckedPaths" = $AlreadyCheckedPaths } |
ForEach-Object { $AllResults += $_ }

$CheckResult = New-Object -TypeName PSObject
$CheckResult | Add-Member -MemberType "NoteProperty" -Name "Result" -Value $AllResults
Expand Down
70 changes: 69 additions & 1 deletion src/helper/AccessControl.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ function Get-ModifiableRegistryPath {
}
}

function Get-ModifiableComClassEntry {
# Used by 'Invoke-ComServerRegistryPermissionCheck'
function Get-ModifiableComClassEntryRegistryPath {

[CmdletBinding()]
param (
Expand All @@ -385,6 +386,73 @@ function Get-ModifiableComClassEntry {
}
}

# Used by 'Invoke-ComServerImagePermissionCheck'
function Get-ModifiableComClassEntryImagePath {

[CmdletBinding()]
param (
[Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Object] $ComClassEntry,

[System.Collections.ArrayList] $CheckedPaths = $null
)

begin {
if ($null -eq $CheckedPaths) { $CheckedPaths = New-Object System.Collections.ArrayList }
}

process {
$CandidatePaths = @()

switch ($ComClassEntry.DataType) {
"FileName" {
Resolve-ModulePath -Name $ComClassEntry.Data | ForEach-Object { $CandidatePaths += $_ }
}
"FilePath" {
$CandidatePaths += [System.Environment]::ExpandEnvironmentVariables($ComClassEntry.Data).Trim('"')
}
"CommandLine" {
$CommandLineResolved = [string[]] (Resolve-CommandLine -CommandLine $ComClassEntry.Data)
if ($null -eq $CommandLineResolved) { continue }

$CandidatePaths += $CommandLineResolved[0]

if (($CommandLineResolved[0] -match ".*rundll32(\.exe)?`$") -and ($CommandLineResolved.Count -gt 1) -and ($CommandLineResolved[1] -like "*.dll,*")) {
$PathToAnalyze = $CommandLineResolved[1].Split(',')[0]
if ([System.IO.Path]::IsPathRooted($PathToAnalyze)) {
$CandidatePaths += $PathToAnalyze
}
else {
Resolve-ModulePath -Name $PathToAnalyze | ForEach-Object { $CandidatePaths += $_ }
}
}
}
default {
Write-Warning "Unknown server data type: $($ComClassEntry.DataType)"
continue
}
}

foreach ($CandidatePath in $CandidatePaths) {

if ([String]::IsNullOrEmpty($CandidatePath)) { continue }
if ($CheckedPaths -contains $CandidatePath) { continue }

$ModifiablePaths = Get-ModifiablePath -Path $CandidatePath | Where-Object { $_ -and (-not [String]::IsNullOrEmpty($_.ModifiablePath)) }
if ($null -eq $ModifiablePaths) { $null = $CheckedPaths.Add($CandidatePath); continue }

foreach ($ModifiablePath in $ModifiablePaths) {

$Result = $ComClassEntry.PSObject.Copy()
$Result | Add-Member -MemberType "NoteProperty" -Name "ModifiablePath" -Value $ModifiablePath.ModifiablePath
$Result | Add-Member -MemberType "NoteProperty" -Name "IdentityReference" -Value $ModifiablePath.IdentityReference
$Result | Add-Member -MemberType "NoteProperty" -Name "Permissions" -Value ($ModifiablePath.Permissions -join ", ")
$Result
}
}
}
}

function Get-ExploitableUnquotedPath {
<#
.SYNOPSIS
Expand Down

0 comments on commit 43cb6eb

Please sign in to comment.