microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
163.74k stars 29.1k forks source link

shellIntegration.ps1 is not digitally signed #174021

Open deejaybeam opened 1 year ago

deejaybeam commented 1 year ago

Does this issue occur when all extensions are disabled?: Yes

Steps to Reproduce:

  1. Set Execution Policy via GPO to "AllSigned"
  2. Start VSCode

This appears since 1.74. I hoped it was just forgotten to sign shellIntegration.ps1, but with current release the issue continues.

We have a company policy to set execution of powershell-scripts via GPO to "AllSigned" so it is not possible to bypass or change the execution-policy on the computer.

We are also monitor this event on our systems which triggers an alert everytime VSCode is started, because of the execution of an unsigned powershell script.

Relevant EventLogs:

Error Message = File C:\Program Files\Microsoft VS Code\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1 cannot be loaded. The file C:\Program Files\Microsoft VS Code\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
Fully Qualified Error ID = UnauthorizedAccess
Recommended Action = 

Context:
        Severity = Warning
        Host Name = ConsoleHost
        Host Version = 5.1.20348.643
        Host ID = 50954c61-f870-4707-885a-02d2e38b7fd4
        Host Application = C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -noexit -command try { . "c:\Program Files\Microsoft VS Code\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1" } catch {}
        Engine Version = 5.1.20348.643
        Runspace ID = 2bb84ced-b167-4b5e-8c57-ccb090c1fd31
        Pipeline ID = 2
        Command Name = 
        Command Type = 
        Script Name = 
        Command Path = 
        Sequence Number = 15
        User = DOMAIN\USER
        Connected User = 
        Shell ID = Microsoft.PowerShell

User Data:
Error Message = Windows PowerShell updated your execution policy successfully, but the setting is overridden by a policy defined at a more specific scope.  Due to the override, your shell will retain its current effective execution policy of AllSigned. Type "Get-ExecutionPolicy -List" to view your execution policy settings. For more information please see "Get-Help Set-ExecutionPolicy".
Fully Qualified Error ID = ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
Recommended Action = Contact your system administrator.

Context:
        Severity = Warning
        Host Name = Visual Studio Code Host
        Host Version = 2023.1.0
        Host ID = c5a89ab3-2377-4a74-9319-250218a2387e
        Host Application = C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -Command Import-Module 'c:\Users\USER\.vscode\extensions\ms-vscode.powershell-2023.1.0\modules\PowerShellEditorServices\PowerShellEditorServices.psd1'; Start-EditorServices -HostName 'Visual Studio Code Host' -HostProfileId 'Microsoft.VSCode' -HostVersion '2023.1.0' -AdditionalModules @('PowerShellEditorServices.VSCode') -BundledModulesPath 'c:\Users\USER\.vscode\extensions\ms-vscode.powershell-2023.1.0\modules' -EnableConsoleRepl -StartupBanner "PowerShell Extension v2023.1.0
Copyright (c) Microsoft Corporation.

https://aka.ms/vscode-powershell
Type 'help' to get help.
" -LogLevel 'Normal' -LogPath 'c:\Users\USER\AppData\Roaming\Code\User\globalStorage\ms-vscode.powershell\logs\1676010691-aed66317-eb79-4c4f-bc78-3b4cd369c81c1676010688614\EditorServices.log' -SessionDetailsPath 'c:\Users\USER\AppData\Roaming\Code\User\globalStorage\ms-vscode.powershell\sessions\PSES-VSCode-4876-818833.json' -FeatureFlags @() 
        Engine Version = 5.1.20348.643
        Runspace ID = b2b4db5d-31d5-49bb-b635-c26b9be5952d
        Pipeline ID = 6
        Command Name = Set-ExecutionPolicy
        Command Type = Cmdlet
        Script Name = 
        Command Path = 
        Sequence Number = 71
        User = DOMAIN\USER
        Connected User = 
        Shell ID = Microsoft.PowerShell

User Data:
Tyriar commented 1 year ago

We noticed this in https://github.com/microsoft/vscode/issues/157083 originally and just wrapped the script in a try. @joaomoreno any idea how to sign a ps1 file? If I recall right I tried adding it to our sign step and it didn't work.

mkoohafkan commented 11 months ago

Alternatively, could you use the Unblock-File cmdlet to allow shellIntegration.ps1? See example 7 in https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy

deejaybeam commented 11 months ago

Alternatively, could you use the Unblock-File cmdlet to allow shellIntegration.ps1? See example 7 in https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy

Please read the example 7 again: The Unblock-File cmdlet unblocks scripts so they can run, but doesn't change the execution policy.

Unblock-File has nothing to do with execution-policy.

The problem is that the execution-policy is set to "AllSigned" via GPO - this way is is NOT possible to change that.

mkoohafkan commented 11 months ago

Ah I see, Unblock-File would work for RemoteSigned but not AllSigned or Restricted.

My understanding is that when "terminal.integrated.shellIntegration.enabled": true the terminal launch command (without the try() wrap) is e.g.

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '-noexit' '-command' ' . "c:\LocalPrograms\g2encmck\VSCode\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1"'

My computer's ExecutionPolicy is Restricted, so this does not work. However, I can add the -ExecutionPolicy ByPass argument and it works:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '-noexit' '-ExecutionPolicy' 'ByPass' '-command' ' . "c:\LocalPrograms\g2encmck\VSCode\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1"'

I think this will also work for AllSigned, and it would be great to add this for Restricted users in any case.

no-identd commented 10 months ago

Ah I see, Unblock-File would work for RemoteSigned but not AllSigned or Restricted.

My understanding is that when "terminal.integrated.shellIntegration.enabled": true the terminal launch command (without the try() wrap) is e.g.

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '-noexit' '-command' ' . "c:\LocalPrograms\g2encmck\VSCode\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1"'

My computer's ExecutionPolicy is Restricted, so this does not work. However, I can add the -ExecutionPolicy ByPass argument and it works:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe '-noexit' '-ExecutionPolicy' 'ByPass' '-command' ' . "c:\LocalPrograms\g2encmck\VSCode\resources\app\out\vs\workbench\contrib\terminal\browser\media\shellIntegration.ps1"'

I think this will also work for AllSigned, and it would be great to add this for Restricted users in any case.

This seems like a bad idea. The correct solution still consists of having the file become signed properly again. Admittedly however this would still leave a requirement to help with execution policy being set to Restricted, but if anything, such an override should set the policy to AllSigned or RemoteSigned, there's no reason to use Bypass at all in this context. AND such an override is already possible to configure:

https://code.visualstudio.com/docs/terminal/profiles

So I guess your ask is to expose UI toggles for this? If so, that should go into a separate feature request issue, right?

@Tyriar :

When you asked @TylerLeonhardt the same question you asked @joaomoreno about here, in this issue over in the following issue: https://github.com/microsoft/vscode/issues/157083#issuecomment-1206495419

you mentioned that "It's included in our signing job but [doesn't work]". Assuming it contains no secrets, can you please share the step? Anyway, since there's some confusion, I'll try to help:

The example script from https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing?view=powershell-7.4 is a bit misleading. Instead you'll probably need something like:

## Signs a file
[cmdletbinding()]
param( #the comments in this block exist to showcase potential but fictional default values one would want to set here instead of initializing them as empty. In that case one might want to comment out the respective mandatory=$true lines
    [Parameter(Mandatory=$true)]
    [string] $FilePath = '', #= 'c:\scripts\Remodel.ps1',
    [Parameter(Mandatory=$true)]
    [string] $CertificatePath = '', # = 'Cert:\CurrentUser\My', # note that "Cert:\" is NOT a stand-in for a file system drive letter but instead a reference to the certificate provider drive, see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/about/about_certificate_provider?view=powershell-7.4
    [Parameter(Mandatory=$true)]
    [string] $TimestampServer = '' #'https://timestamp.fabrikam.com/scripts/timstamper.dll'
)

$Certificate = Get-ChildItem -Path $CertificatePath -CodeSigningCert
Set-AuthenticodeSignature -FilePath $FilePath -Certificate $Certificate -IncludeChain All -TimestampServer $TimestampServer

which is essentially a refined hybrid of Examples 1 and 3 from https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-authenticodesignature?view=powershell-7.4

Of course, you'll have to manually sign the above script by manually executing the steps from it before you can use it to automatically sign scripts with it as part of CI.

However, this might not be enough to accomplish what you need, not sure how this works internally at MSFT, so, at the risk of overkill, here's a full-blown article series on this:

https://devblogs.microsoft.com/scripting/hey-scripting-guy-how-can-i-sign-windows-powershell-scripts-with-an-enterprise-windows-pki-part-1-of-2/

https://devblogs.microsoft.com/scripting/hey-scripting-guy-how-can-i-sign-windows-powershell-scripts-with-an-enterprise-windows-pki-part-2-of-2/

But it's written from an 'omnipotent'/lab perspective AND from the perspective of publishing for Enterprise-internal users (which will have the Root CA cert of the Internal PKI in their trusted Root CAs store) instead of for Enterprise-external users (which won't, but which WILL have other ones, such as the one you'll indirectly use to sign this, since presumably you won't use a self-signed cert, since that'd make the whole exercise pointless), so, you almost certainly can't 1:1 adapt it, but it should provide some important context one would miss when just reading the docs 'naively'.

mkoohafkan commented 10 months ago

This seems like a bad idea. The correct solution still consists of having the file become signed properly again. Admittedly however this would still leave a requirement to help with execution policy being set to Restricted, but if anything, such an override should set the policy to AllSigned or RemoteSigned, there's no reason to use Bypass at all in this context. AND such an override is already possible to configure:

https://code.visualstudio.com/docs/terminal/profiles

So I guess your ask is to expose UI toggles for this? If so, that should go into a separate feature request issue, right?

@no-identd I agree the script should be signed in any case but there should be a solution for restricted users as well. I think it's about as bad an idea as it is for Anaconda to use it for their prompt and I've never heard any security concerns regarding that. If the signing works then setting it to AllSigned or RemoteSigned would be fine too but I don't see a big difference since VSCode is bypassing the ExecutionPolicy for this specific command in any case. I agree this is a separate ask so we can continue the discussion in https://github.com/microsoft/vscode/issues/199122

Tyriar commented 10 months ago

@no-identd I think this is the call:

https://github.com/microsoft/vscode/blob/d70a46b6bd65aade0248f900a6e67a85f1c2cde2/build/azure-pipelines/cli/cli-win32-sign.yml#L44

Things have changed since I looked at this but I remember adding .ps1 to the list of files to be signed and it didn't seem to work. Unfortunately I don't think this can be fixed/validated externally so it needs someone who knows more about this than me to help out.

@joaomoreno @rzhao271 any help here would be appreciated

rdebath commented 4 months ago

Just decided to see what this was about in my AppLocker logs. What's it actually breaking if I don't give it a bypass?


Signing PS1 scripts is fully supported by Microsoft tools, so if it doesn't work for you I'd be raising a bug with your CI support.

For example ...

$cert= Get-PfxCertificate -FilePath $certfile
Set-AuthenticodeSignature -Certificate $cert -HashAlgorithm SHA256 -FilePath $exename

Sticks a huge comment on the end of the script ...

./Get-AppLockerEvents.ps1 -ForwardedEvents > c:\temp\applocker2.csv

# SIG # Begin signature block
# MIIGLgYJKoZIhvcNAQcCoIIGHzCCBhsCAQExDzANBglghkgBZQMEAgEFADB5Bgor
...
# M14pXYkIxEqYOiVDnizAE00+DubW5yEa19ig5n/8cJvVlgYt2mDmWrCoMKQoPD8i
# Cko=
# SIG # End signature block
Tyriar commented 4 weeks ago

Future reference: this is where the executables are being signed: https://github.com/microsoft/vscode/blob/74b0694b19996bc247d08855f7222271a51c7456/build/azure-pipelines/cli/cli-win32-sign.yml#L45

Tyriar commented 4 weeks ago

DM'd @joaomoreno for some help