diff --git a/test.ps1 b/test.ps1 index 971ef06..f047672 100644 --- a/test.ps1 +++ b/test.ps1 @@ -158,79 +158,73 @@ function Invoke-ServiceImagePathAudit { param($Obj) $outObj = $Obj | Select-Object * - $badpath = $false - $examine = $outObj.ImagePath + $img = [string]$outObj.ImagePath if ($ShowProgress) { Write-Progress -Activity "Analyzing ImagePath" -Status "Checking $($outObj.ComputerName)\$($outObj.Key)" } - if ($outObj.Key -eq "Unavailable" -or $examine -eq "Unavailable" -or [string]::IsNullOrWhiteSpace($examine)) { - $outObj | Add-Member NoteProperty BadKey "Unknown" -Force + # Default outputs + $badKey = "No" + $fixed = "N/A" + + # Can't analyze + if ($outObj.Key -eq "Unavailable" -or $img -eq "Unavailable" -or [string]::IsNullOrWhiteSpace($img)) { + $outObj | Add-Member NoteProperty BadKey "Unknown" -Force $outObj | Add-Member NoteProperty FixedKey "Can't Fix" -Force return $outObj } - # Ignore already-quoted or special \?? prefixes - if (-not $examine.StartsWith('"') -and -not $examine.StartsWith("\??")) { + $trim = $img.Trim() - if ($examine.Contains(" ")) { - - # If we see flagged args, try to isolate a path portion - if ($examine.Contains("-") -or $examine.Contains("/")) { - - $split = $examine -split " -", 0, "simplematch" - $split = $split[0] -split " /", 0, "simplematch" - $newpath = $split[0].Trim() - - if ($newpath.Contains(" ")) { - $eval = $newpath -Replace '".*"', '' - $detunflagged = $eval -split "\\", 0, "simplematch" - - if ($detunflagged[-1].Contains(" ")) { - $fixarg = $detunflagged[-1] -split " ", 0, "simplematch" - $quoteexe = $fixarg[0] + '"' - $examine = $examine.Replace($fixarg[0], $quoteexe) - $examine = '"' + $examine.Trim('"') + '"' - $badpath = $true - } - - $examine = $examine.Replace($newpath, '"' + $newpath + '"') - $badpath = $true - } - - } else { - # No flagged args, either just a bad path or an unflagged argument scenario - $eval = $examine -Replace '".*"', '' - $detunflagged = $eval -split "\\", 0, "simplematch" - - if ($detunflagged[-1].Contains(" ")) { - $fixarg = $detunflagged[-1] -split " ", 0, "simplematch" - $quoteexe = $fixarg[0] + '"' - $examine = $examine.Replace($fixarg[0], $quoteexe) - $examine = '"' + $examine.Trim('"') + '"' - $badpath = $true - } else { - $examine = '"' + $examine.Trim('"') + '"' - $badpath = $true - } - } - } - } - - if (-not $badpath) { - $outObj | Add-Member NoteProperty BadKey "No" -Force - $outObj | Add-Member NoteProperty FixedKey "N/A" -Force + # Already quoted or special prefix we don't touch + if ($trim.StartsWith('"') -or $trim.StartsWith('\??')) { + $outObj | Add-Member NoteProperty BadKey $badKey -Force + $outObj | Add-Member NoteProperty FixedKey $fixed -Force return $outObj } - while ($examine.EndsWith('""')) { $examine = $examine.Substring(0, $examine.Length - 1) } + # If no spaces, not vulnerable in the classic sense + if ($trim -notmatch '\s') { + $outObj | Add-Member NoteProperty BadKey $badKey -Force + $outObj | Add-Member NoteProperty FixedKey $fixed -Force + return $outObj + } - $outObj | Add-Member NoteProperty BadKey "Yes" -Force - $outObj | Add-Member NoteProperty FixedKey $examine -Force + # Quote only the executable portion (best practice for service ImagePath) + # Matches: + # C:\Path With Spaces\app.exe + # \\server\share\Path With Spaces\app.exe + # Also tolerates env-var rooted paths like: + # %ProgramFiles%\App\app.exe + $exeRegex = '^(?(?:(?:[A-Za-z]:\\)|(?:\\\\[^\\]+\\[^\\]+\\)|(?:%[^%]+%\\))[^"]*?\.(?:exe|com|bat|cmd))(?\s+.*)?$' + + if ($trim -match $exeRegex) { + $exe = $Matches['exe'] + $args = $Matches['args'] + + # Only "bad" if the exe path contains whitespace and is unquoted (it is) + if ($exe -match '\s') { + $badKey = "Yes" + $fixed = '"' + $exe + '"' + ($args ?? '') + } else { + $badKey = "No" + $fixed = "N/A" + } + } + else { + # Fallback: if we can't confidently isolate an exe, quote the whole string + # (better than producing broken quotes) + $badKey = "Yes" + $fixed = '"' + $trim.Trim('"') + '"' + } + + $outObj | Add-Member NoteProperty BadKey $badKey -Force + $outObj | Add-Member NoteProperty FixedKey $fixed -Force return $outObj } + function _RepairOne { param($Obj)