SetRunspacePrompt() injects VM/container name into PowerShell script without escaping — arbitrary code executes on every prompt render · Issue #28255 · PowerShell/PowerShell · GitHub
PushRunspaceCommand.SetRunspacePrompt() (line 1138, src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs, current HEAD bf456e85) builds a function global:prompt { … } body by string-interpolating the VM/container name directly into a double-quoted PowerShell string, with no escaping.
Because the result is executed with ps.AddScript(promptFn).Invoke(), any $(…) subexpression embedded in the name is evaluated — once when the function is defined, and again on every subsequent prompt draw, because that is how the host prompt works.
targetName is set from this.VMName, this.ContainerId, this.Session.ComputerName, or remoteRunspace.ConnectionInfo.ComputerName depending on the parameter set. None of these values are validated or escaped before use. StringUtil.Format is a plain string.Format(CultureInfo.CurrentCulture, …) wrapper with no sanitization (confirmed in StringUtil.cs).
The template in RemotingErrorIdStrings.resx is {0}[{1}]: {2} — slot {1} receives targetName unmodified.
Reproduction (no Hyper-V required)
The following reproduces the exact string-building and execution logic used by the real code path:
Output shows two different timestamps — confirming re-execution on every prompt call, not just at definition time.
Real-world trigger
Any user who can name or rename a Hyper-V VM or Windows Container to a string containing $(…) causes injected code to execute inside the session of any user who runs Enter-PSSession -VMName (or -VMId, -ContainerId) against it. The injected code re-runs after every command typed in that session.
Suggested fix
Option A — escape targetName before interpolation using the API already used correctly elsewhere in the codebase (CodeGeneration.EscapeSingleQuotedStringContent):
Option B — stop building the prompt as script text. Pass targetName into the runspace as a variable and reference it from a script block, so it is never evaluated as PowerShell syntax.
Affected files
src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs (SetRunspacePrompt() line 1138; VMName property line 158)
I'm trying to work out if there is a use case where this actually might be the wanted behaviour at all, though I somehow doubt it.
The only possible use case I could think of maybe actually returning the in VM ComputerName which can be different to the referenced Hyper-V VM name in the prompt.
Any user who can name or rename a Hyper-V VM or Windows Container to a string containing $(…) causes injected code to execute inside the session of any user who runs Enter-PSSession -VMName (or -VMId, -ContainerId) against it. The injected code re-runs after every command typed in that session.
To confirm this I created a Server 2019 VM called svr19
Using 7.6.9
I entered a PSSession to confirm the prompt shows svr19 as expected
I set the VM Name in Hyper-V Manager to svr19-$($env:computername)
I entered a new PSSession to this VM by $vm = (get-vm | where Name -Match computername).name[0] followed by Enter-PSSession -VMName $vm -Credential $vmCreds - which as expected returned [svr19-WIN-G78O17CQJPE]: PS C:\Users\Administrator\Documents>
I updated the VMName to '$(Write-Output "INJECTED @ $(Get-Date -Format o)")'
I entered a new PSSession to this VM by $vm = (get-vm | where Name -Match Output).name[0] followed by Enter-PSSession -VMName $vm -Credential $vmCreds - which returned [''INJECTED @ 2026-11-08T08:18:43.3939799+00:00'']: PS C:\Users\Administrator\Documents>
I then built a local build of PowerShell that implemented your suggested Option A & tried the steps above again. This suggestion however didn't seem to mitigate this.
Your Option B lead me to building out this branch & then raising this PR to mitigate this.
I went down a bit of a rabbit hole by trying to make use of the AST to guard around this as can be seen in commit f229dc0 & decided to instead simplify this in commit afa9957
Activity
lupin-sudo commented on Oct 24, 2026
Reported by Nader Tahan (lupin-sudo)
kilasuit commented on Nov 8, 2026
I'm trying to work out if there is a use case where this actually might be the wanted behaviour at all, though I somehow doubt it.
To confirm this I created a Server 2019 VM called svr19
Using 7.6.9
I then built a local build of PowerShell that implemented your suggested Option A & tried the steps above again. This suggestion however didn't seem to mitigate this.
Your Option B lead me to building out this branch & then raising this PR to mitigate this.
I went down a bit of a rabbit hole by trying to make use of the AST to guard around this as can be seen in commit f229dc0 & decided to instead simplify this in commit afa9957