Skip to content

Fix CWE-295: Remove WinRM certificate/hostname validation bypass in PSCloudShellUtility related to ICM 842194797 - #638

Merged
Richard Hoover (rhkodiak) merged 1 commit into
Azure:masterfrom
rhkodiak:rhoover/fix-winrm-bug
Aug 12, 2026
Merged

Fix CWE-295: Remove WinRM certificate/hostname validation bypass in PSCloudShellUtility related to ICM 842194797#638
Richard Hoover (rhkodiak) merged 1 commit into
Azure:masterfrom
rhkodiak:rhoover/fix-winrm-bug

Conversation

@rhkodiak

@rhkodiak Richard Hoover (rhkodiak) commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves ICM 847527874 (CWE-295, Improper Certificate Validation).  PSCloudShellUtility.psm1  set a module-scoped  PSSessionOption  with  SkipCACheck = $true  /  SkipCNCheck = $true , used by the exported  Invoke-AzVMCommand  to connect to Windows VMs over WinRM-HTTPS with self-signed certs. This disabled all server-identity validation, letting a network attacker who could intercept/redirect traffic to the VM's public endpoint spoof the server and steal the Basic-auth credential.

Fix

Replaced the WinRM/self-signed-cert connection path (Windows only) with Azure  Invoke-AzVMRunCommand , which executes via the ARM control plane instead of a direct network connection to the VM — eliminating the need for any client-side certificate trust decision entirely.

  • Invoke-AzVMCommand : Windows branch now validates the VM exists ( Get-AzVM ) and calls  Invoke-AzVMRunCommand  ( CommandId = RunPowerShellScript ) instead of  Invoke-Command  over WinRM. Result parsing splits the returned status into StdOut/StdErr; StdOut is written as output, and a non-empty StdErr message is thrown as a terminating  InvalidOperationException  (Run Command's status object never signals failure via Level/Code — only a non-empty StdErr message indicates the script errored).

  • Credential  remains a mandatory parameter (validated non-null) for backward compatibility but is no longer forwarded anywhere — Run Command always executes as  SYSTEM , and  Invoke-AzVMRunCommand  has no  -RunAsUser / -RunAsPassword  parameters. Authorization is now enforced via Azure RBAC on the VM resource rather than a guest-OS credential. Documented in the  .PARAMETER Credential  help.

  • Enable-AzVMPSRemoting  /  Disable-AzVMPSRemoting : Windows branches converted to no-ops (kept only so existing  -OsType windows  callers don't break) — there's no WinRM listener, self-signed cert, or NSG port to provision/tear down anymore.

  • Test-AzVM : simplified remoting-status check to Linux-only (SSH), since Windows no longer routes through this check.

  • The Linux/SSH code path is entirely untouched.

  • Removed the flagged module-scope  $script:sessionOption  block and all references to it.

Validation (live Cloud Shell session)

Check Result
Invoke-AzVMCommand against a real Windows VM (success path) ✅ Returned real hostname/whoami (nt authority\system)/Get-Date output via Run Command
Failure path (throw inside -ScriptBlock) ✅ Correctly caught by caller as InvalidOperationException with full remote error text
Enable-/Disable-AzVMPSRemoting ✅ No-op, no errors
NSG rules for ports 5985/5986 ✅ Confirmed none created post-fix
Linux/SSH path ✅ Confirmed unaffected by diff (code review); live SSH blocked only by unrelated pre-existing issues (tenant NRMS NSG policy; Ubuntu 18.04-hardcoded install script — not introduced by this change)
Module import (Import-Module -Force) ✅ Imports cleanly, no errors, correct version/commands resolved

Follow-ups (not in scope for this PR)

  • Tenant NRMS baseline NSG policy blocks direct internet SSH — pre-existing, unrelated to this fix.

  • Enable-AzVMPSRemoting  Linux install script is hardcoded to Ubuntu 18.04 and fails on newer Ubuntu releases — pre-existing bug, separate from this fix.

Fixes ICM 847527874.

Test Results

  • Upload modified PSCloudShellUtility file to your CloudShell session
image Good, module is loaded (PSCloudShellUtility 0.9.3). Now re-run the VM tests:
  1. Success path = Invoke-AzVMCommand -Name winvm-test -ResourceGroupName cs-validation-rg -Credential $cred -ScriptBlock { hostname; whoami; Get-Date }
  2. image
  3. Failure path = try { Invoke-AzVMCommand -Name windows-vm -ResourceGroupName cs-validation-rg -Credential $cred -ScriptBlock { throw "forced test failure" }} catch { "Caught expected error: $($_.Exception.Message)"}
image
Test Result
Success path ✅ windows-vm / nt authority\system / live VM timestamp — genuine remote execution via Run Command
Failure path throw correctly caught as an exception with full remote error text

The fix continues to work as expected on a fresh Cloud Shell session.

@rhkodiak
Richard Hoover (rhkodiak) merged commit d02d592 into Azure:master Aug 12, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants