|
| 1 | + |
| 2 | +<# |
| 3 | +.SYNOPSIS |
| 4 | + test if current session/identity is elevated |
| 5 | + (a.k.a. check if we've got admin privileges) |
| 6 | +#> |
| 7 | +function Test-Admin { |
| 8 | + $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() |
| 9 | + $principal = New-Object System.Security.Principal.WindowsPrincipal( $identity ) |
| 10 | + return $principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Administrator ) |
| 11 | +} |
| 12 | + |
| 13 | +<# |
| 14 | +.SYNOPSIS |
| 15 | + get name of 'WellKnownSidType' in the current user's system locale |
| 16 | +#> |
| 17 | +function Get-LocalizedWellKnownPrincipalName { |
| 18 | + param ( |
| 19 | + [Parameter(Mandatory = $true)] |
| 20 | + [Security.Principal.WellKnownSidType] $WellKnownSidType |
| 21 | + ) |
| 22 | + $sid = New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList @($WellKnownSidType, $null) |
| 23 | + $account = $sid.Translate([Security.Principal.NTAccount]) |
| 24 | + |
| 25 | + return $account.Value |
| 26 | +} |
| 27 | + |
| 28 | +<# |
| 29 | +.SYNOPSIS |
| 30 | + ensure a given folder is only writeable by administrative users |
| 31 | +
|
| 32 | +.NOTES |
| 33 | + we need to do this in order to mitigate privilege escalation attacks! |
| 34 | + |
| 35 | + Attack Vector 1: Boxstarter folders are added to PATH, therefore they must be protected in a way so |
| 36 | + that a random user may not put arbitrary files/dlls in these folders. |
| 37 | + (files may be replaces with hijacked/malicious ones) |
| 38 | + |
| 39 | + Attack Vector 2: 'BuildPackages' contains Boxstarter Packages that may be installed after system reboots. |
| 40 | + If a user would be able to modify those packages, it would be easy to run arbitrary PowerShell code with |
| 41 | + SYSTEM privileges. |
| 42 | + |
| 43 | + see Ensure-Permissions |
| 44 | + https://github.com/chocolatey/choco/blob/master/nuget/chocolatey/tools/chocolateysetup.psm1 |
| 45 | +#> |
| 46 | +function Ensure-Permissions { |
| 47 | + [CmdletBinding()] |
| 48 | + param( |
| 49 | + [string]$folder |
| 50 | + ) |
| 51 | + Write-Debug "Ensure-Permissions" |
| 52 | + |
| 53 | + $currentEA = $ErrorActionPreference |
| 54 | + $ErrorActionPreference = 'Stop' |
| 55 | + try { |
| 56 | + # get current acl |
| 57 | + $acl = (Get-Item $folder).GetAccessControl('Access,Owner') |
| 58 | + |
| 59 | + Write-Debug "Removing existing permissions." |
| 60 | + $acl.Access | ForEach-Object { |
| 61 | + Write-Debug "Remove '$($_.FileSystemRights)' for user '$($_.IdentityReference)'" |
| 62 | + $acl.RemoveAccessRuleAll($_) |
| 63 | + } |
| 64 | + |
| 65 | + $inheritanceFlags = ([Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [Security.AccessControl.InheritanceFlags]::ObjectInherit) |
| 66 | + $propagationFlags = [Security.AccessControl.PropagationFlags]::None |
| 67 | + |
| 68 | + $rightsFullControl = [Security.AccessControl.FileSystemRights]::FullControl |
| 69 | + $rightsReadExecute = [Security.AccessControl.FileSystemRights]::ReadAndExecute |
| 70 | + |
| 71 | + Write-Output "Restricting write permissions of '$folder' to Administrators" |
| 72 | + $builtinAdmins = Get-LocalizedWellKnownPrincipalName -WellKnownSidType ([Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid) |
| 73 | + $adminsAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($builtinAdmins, $rightsFullControl, $inheritanceFlags, $propagationFlags, "Allow") |
| 74 | + $acl.SetAccessRule($adminsAccessRule) |
| 75 | + $localSystem = Get-LocalizedWellKnownPrincipalName -WellKnownSidType ([Security.Principal.WellKnownSidType]::LocalSystemSid) |
| 76 | + $localSystemAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($localSystem, $rightsFullControl, $inheritanceFlags, $propagationFlags, "Allow") |
| 77 | + $acl.SetAccessRule($localSystemAccessRule) |
| 78 | + $builtinUsers = Get-LocalizedWellKnownPrincipalName -WellKnownSidType ([Security.Principal.WellKnownSidType]::BuiltinUsersSid) |
| 79 | + $usersAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($builtinUsers, $rightsReadExecute, $inheritanceFlags, $propagationFlags, "Allow") |
| 80 | + $acl.SetAccessRule($usersAccessRule) |
| 81 | + |
| 82 | + Write-Debug "Set Owner to Administrators" |
| 83 | + $builtinAdminsSid = New-Object System.Security.Principal.SecurityIdentifier([Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $null) |
| 84 | + $acl.SetOwner($builtinAdminsSid) |
| 85 | + |
| 86 | + Write-Debug "Removing inheritance with no copy" |
| 87 | + $acl.SetAccessRuleProtection($true, $false) |
| 88 | + |
| 89 | + # enact the changes against the actual |
| 90 | + (Get-Item $folder).SetAccessControl($acl) |
| 91 | + |
| 92 | + } |
| 93 | + catch { |
| 94 | + Write-Warning $_.Exception |
| 95 | + Write-Warning "Not able to set permissions for $folder." |
| 96 | + } |
| 97 | + $ErrorActionPreference = $currentEA |
| 98 | +} |
| 99 | + |
1 | 100 | function Install-Boxstarter($here, $ModuleName, $installArgs = "") { |
| 101 | + |
| 102 | + if (!(Test-Admin)) { |
| 103 | + throw "Installation of Boxstarter requires Administrative permissions. Please run from elevated prompt." |
| 104 | + } |
| 105 | + |
2 | 106 | $boxstarterPath = Join-Path $env:ProgramData Boxstarter |
3 | | - if(!(test-Path $boxstarterPath)){ |
4 | | - mkdir $boxstarterPath |
| 107 | + if (!(test-Path $boxstarterPath)) { |
| 108 | + New-Item -ItemType Directory $boxstarterPath | Out-Null |
5 | 109 | } |
6 | | - $packagePath=Join-Path $boxstarterPath BuildPackages |
7 | | - if(!(test-Path $packagePath)){ |
8 | | - mkdir $packagePath |
| 110 | + $packagePath = Join-Path $boxstarterPath BuildPackages |
| 111 | + if (!(test-Path $packagePath)) { |
| 112 | + New-Item -ItemType Directory $packagePath | Out-Null |
9 | 113 | } |
10 | | - foreach($ModulePath in (Get-ChildItem $here | Where-Object { $_.PSIsContainer })){ |
11 | | - $target=Join-Path $boxstarterPath $modulePath.BaseName |
12 | | - if(test-Path $target){ |
| 114 | + foreach ($ModulePath in (Get-ChildItem $here | Where-Object { $_.PSIsContainer })) { |
| 115 | + $target = Join-Path $boxstarterPath $modulePath.BaseName |
| 116 | + if (test-Path $target) { |
13 | 117 | Remove-Item $target -Recurse -Force |
14 | 118 | } |
15 | 119 | } |
16 | 120 | Copy-Item "$here\*" $boxstarterPath -Recurse -Force -Exclude ChocolateyInstall.ps1, Setup.* |
17 | 121 |
|
| 122 | + # set permissions to mitigate possible privilege escalation |
| 123 | + Ensure-Permissions -folder $boxstarterPath |
| 124 | + |
18 | 125 | PersistBoxStarterPathToEnvironmentVariable "PSModulePath" $boxstarterPath |
19 | 126 | PersistBoxStarterPathToEnvironmentVariable "Path" $boxstarterPath |
20 | 127 | $binPath = "$here\..\..\..\bin" |
@@ -46,7 +153,7 @@ PS:>Get-Help Boxstarter |
46 | 153 | $startMenu = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonStartMenu) |
47 | 154 | $startMenu += "\Programs\Boxstarter" |
48 | 155 | if(!(Test-Path $startMenu)){ |
49 | | - mkdir $startMenu |
| 156 | + New-Item -ItemType Directory $startMenu | Out-Null |
50 | 157 | } |
51 | 158 | $target="powershell.exe" |
52 | 159 | $targetArgs="-ExecutionPolicy bypass -NoExit -Command `"&'$boxstarterPath\BoxstarterShell.ps1'`"" |
|
0 commit comments