Skip to content

Commit 8dea905

Browse files
committed
Fixed bug in Get-ModifiablePath that resulted in spaces being expanded to the current directory location
Fixed other logic bugs in Get-ModifiablePath Fixed bug in Add-ServiceDacl when the [ServiceProcess.ServiceController] wasn't loaded yet by Get-Service Error handling for Get-CachedGPPPassword Changed some Write-Warnings to Write-Verbose Updated Privesc Pester tests for PowerUp
1 parent 4b40e86 commit 8dea905

File tree

2 files changed

+101
-42
lines changed

2 files changed

+101
-42
lines changed

Privesc/PowerUp.ps1

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -865,27 +865,34 @@ function Get-ModifiablePath {
865865
else {
866866
ForEach($SeparationCharacterSet in $SeparationCharacterSets) {
867867
$TargetPath.Split($SeparationCharacterSet) | Where-Object {$_ -and ($_.trim() -ne '')} | ForEach-Object {
868+
868869
if(($SeparationCharacterSet -notmatch ' ')) {
869-
$TempPath = $([System.Environment]::ExpandEnvironmentVariables($_))
870870

871-
if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
872-
$CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
873-
}
874-
else {
875-
# if the path doesn't exist, check if the parent folder allows for modification
876-
try {
877-
$ParentPath = Split-Path $TempPath -Parent
878-
if($ParentPath -and (Test-Path -Path $ParentPath )) {
879-
$CandidatePaths += Resolve-Path -Path $ParentPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
880-
}
871+
$TempPath = $([System.Environment]::ExpandEnvironmentVariables($_)).Trim()
872+
873+
if($TempPath -and ($TempPath -ne '')) {
874+
if(Test-Path -Path $TempPath -ErrorAction SilentlyContinue) {
875+
# if the path exists, resolve it and add it to the candidate list
876+
$CandidatePaths += Resolve-Path -Path $TempPath | Select-Object -ExpandProperty Path
881877
}
882-
catch {
883-
# because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
878+
879+
else {
880+
# if the path doesn't exist, check if the parent folder allows for modification
881+
try {
882+
$ParentPath = (Split-Path -Path $TempPath -Parent).Trim()
883+
if($ParentPath -and ($ParentPath -ne '') -and (Test-Path -Path $ParentPath )) {
884+
$CandidatePaths += Resolve-Path -Path $ParentPath | Select-Object -ExpandProperty Path
885+
}
886+
}
887+
catch {
888+
# trap because Split-Path doesn't handle -ErrorAction SilentlyContinue nicely
889+
}
884890
}
885891
}
886892
}
887893
else {
888-
$CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
894+
# if the separator contains a space
895+
$CandidatePaths += Resolve-Path -Path $([System.Environment]::ExpandEnvironmentVariables($_)) -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path | ForEach-Object {$_.Trim()} | Where-Object {($_ -ne '') -and (Test-Path -Path $_)}
889896
}
890897
}
891898
}
@@ -1024,9 +1031,9 @@ function Add-ServiceDacl {
10241031
service with using the GetServiceHandle Win32 API call and then uses
10251032
QueryServiceObjectSecurity to retrieve a copy of the security descriptor for the service.
10261033

1027-
.PARAMETER Service
1034+
.PARAMETER Name
10281035

1029-
An array of one or more ServiceProcess.ServiceController objects from Get-Service. Required.
1036+
An array of one or more service names to add a service Dacl for. Passable on the pipeline.
10301037

10311038
.EXAMPLE
10321039

@@ -1051,19 +1058,20 @@ function Add-ServiceDacl {
10511058

10521059
[OutputType('ServiceProcess.ServiceController')]
10531060
param (
1054-
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1055-
[ServiceProcess.ServiceController[]]
1061+
[Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
1062+
[Alias('ServiceName')]
1063+
[String[]]
10561064
[ValidateNotNullOrEmpty()]
1057-
$Service
1065+
$Name
10581066
)
10591067

10601068
BEGIN {
10611069
filter Local:Get-ServiceReadControlHandle {
10621070
[OutputType([IntPtr])]
10631071
param (
10641072
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
1065-
[ServiceProcess.ServiceController]
10661073
[ValidateNotNullOrEmpty()]
1074+
[ValidateScript({ $_ -as 'ServiceProcess.ServiceController' })]
10671075
$Service
10681076
)
10691077

@@ -1078,13 +1086,17 @@ function Add-ServiceDacl {
10781086
}
10791087

10801088
PROCESS {
1081-
ForEach ($IndividualService in $Service) {
1089+
ForEach($ServiceName in $Name) {
1090+
1091+
$IndividualService = Get-Service -Name $ServiceName -ErrorAction Stop
1092+
10821093
try {
1094+
Write-Verbose "Add-ServiceDacl IndividualService : $($IndividualService.Name)"
10831095
$ServiceHandle = Get-ServiceReadControlHandle -Service $IndividualService
10841096
}
10851097
catch {
10861098
$ServiceHandle = $Null
1087-
Write-Warning "Error opening up the service handle with read control for: $($IndividualService.Name)"
1099+
Write-Verbose "Error opening up the service handle with read control for $($IndividualService.Name) : $_"
10881100
}
10891101

10901102
if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
@@ -1214,7 +1226,7 @@ function Set-ServiceBinPath {
12141226
}
12151227
catch {
12161228
$ServiceHandle = $Null
1217-
Write-Warning "Error opening up the service handle with read control for $($TargetService.Name) : $_"
1229+
Write-Verbose "Error opening up the service handle with read control for $IndividualService : $_"
12181230
}
12191231

12201232
if ($ServiceHandle -and ($ServiceHandle -ne [IntPtr]::Zero)) {
@@ -1224,7 +1236,7 @@ function Set-ServiceBinPath {
12241236
$Result = $Advapi32::ChangeServiceConfig($ServiceHandle, $SERVICE_NO_CHANGE, $SERVICE_NO_CHANGE, $SERVICE_NO_CHANGE, "$binPath", [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
12251237

12261238
if ($Result -ne 0) {
1227-
Write-Verbose "binPath for $($TargetService.Name) successfully set to '$binPath'"
1239+
Write-Verbose "binPath for $IndividualService successfully set to '$binPath'"
12281240
$True
12291241
}
12301242
else {
@@ -1364,7 +1376,7 @@ function Test-ServiceDaclPermission {
13641376

13651377
ForEach($IndividualService in $Name) {
13661378

1367-
$TargetService = Get-Service -Name $IndividualService | Add-ServiceDacl
1379+
$TargetService = $IndividualService | Add-ServiceDacl
13681380

13691381
if($TargetService -and $TargetService.Dacl) {
13701382

@@ -1381,7 +1393,7 @@ function Test-ServiceDaclPermission {
13811393
ForEach($TargetPermission in $TargetPermissions) {
13821394
# check permissions && style
13831395
if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -ne $AccessMask[$TargetPermission]) {
1384-
Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)"
1396+
# Write-Verbose "Current user doesn't have '$TargetPermission' for $($TargetService.Name)"
13851397
$AllMatched = $False
13861398
break
13871399
}
@@ -1394,7 +1406,7 @@ function Test-ServiceDaclPermission {
13941406
ForEach($TargetPermission in $TargetPermissions) {
13951407
# check permissions || style
13961408
if (($ServiceDacl.AccessRights -band $AccessMask[$TargetPermission]) -eq $AccessMask[$TargetPermission]) {
1397-
Write-Verbose "Current user has '$TargetPermission' for $($TargetService.Name)"
1409+
Write-Verbose "Current user has '$TargetPermission' for $IndividualService"
13981410
$TargetService
13991411
break
14001412
}
@@ -1404,7 +1416,7 @@ function Test-ServiceDaclPermission {
14041416
}
14051417
}
14061418
else {
1407-
Write-Warning "Error enumerating the Dacl for service $($TargetService.Name)"
1419+
Write-Verbose "Error enumerating the Dacl for service $IndividualService"
14081420
}
14091421
}
14101422
}
@@ -1434,14 +1446,15 @@ function Get-ServiceUnquoted {
14341446

14351447
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/local/trusted_service_path.rb
14361448
#>
1449+
[CmdletBinding()] param()
14371450

14381451
# find all paths to service .exe's that have a space in the path and aren't quoted
14391452
$VulnServices = Get-WmiObject -Class win32_service | Where-Object {$_} | Where-Object {($_.pathname -ne $null) -and ($_.pathname.trim() -ne '')} | Where-Object { (-not $_.pathname.StartsWith("`"")) -and (-not $_.pathname.StartsWith("'"))} | Where-Object {($_.pathname.Substring(0, $_.pathname.ToLower().IndexOf(".exe") + 4)) -match ".* .*"}
14401453

14411454
if ($VulnServices) {
14421455
ForEach ($Service in $VulnServices) {
14431456

1444-
$ModifiableFiles = $Service.pathname | Get-ModifiablePath
1457+
$ModifiableFiles = $Service.pathname.split(' ') | Get-ModifiablePath
14451458

14461459
$ModifiableFiles | Where-Object {$_ -and $_.ModifiablePath -and ($_.ModifiablePath -ne '')} | Foreach-Object {
14471460
$ServiceRestart = Test-ServiceDaclPermission -PermissionSet 'Restart' -Name $Service.name
@@ -1487,6 +1500,7 @@ function Get-ModifiableServiceFile {
14871500

14881501
Get a set of potentially exploitable service binares/config files.
14891502
#>
1503+
[CmdletBinding()] param()
14901504

14911505
Get-WMIObject -Class win32_service | Where-Object {$_ -and $_.pathname} | ForEach-Object {
14921506

@@ -1537,6 +1551,7 @@ function Get-ModifiableService {
15371551

15381552
Get a set of potentially exploitable services.
15391553
#>
1554+
[CmdletBinding()] param()
15401555

15411556
Get-Service | Test-ServiceDaclPermission -PermissionSet 'ChangeConfig' | ForEach-Object {
15421557

@@ -1612,7 +1627,7 @@ function Get-ServiceDetail {
16121627
$_
16131628
}
16141629
catch{
1615-
Write-Warning "Error: $_"
1630+
Write-Verbose "Error: $_"
16161631
$null
16171632
}
16181633
}
@@ -3689,12 +3704,15 @@ function Get-CachedGPPPassword {
36893704
# discover any locally cached GPP .xml files
36903705
$XMlFiles = Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
36913706

3692-
if ( -not $XMlFiles ) { throw 'No preference files found.' }
3707+
if ( -not $XMlFiles ) {
3708+
Write-Verbose 'No preference files found.'
3709+
}
3710+
else {
3711+
Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
36933712

3694-
Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
3695-
3696-
ForEach ($File in $XMLFiles) {
3697-
Get-GppInnerFields $File.Fullname
3713+
ForEach ($File in $XMLFiles) {
3714+
Get-GppInnerFields $File.Fullname
3715+
}
36983716
}
36993717
}
37003718

@@ -3839,7 +3857,7 @@ function Invoke-AllChecks {
38393857

38403858
"`n`n[*] Checking %PATH% for potentially hijackable DLL locations..."
38413859
$Results = Find-PathDLLHijack
3842-
$Results | Foreach-Object {
3860+
$Results | Where-Object {$_} | Foreach-Object {
38433861
$AbuseString = "Write-HijackDll -DllPath '$($_.ModifiablePath)\wlbsctrl.dll'"
38443862
$_ | Add-Member Noteproperty 'AbuseFunction' $AbuseString
38453863
$_

Tests/Privesc.tests.ps1

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Describe 'Get-ModifiablePath' {
129129
Describe 'Get-CurrentUserTokenGroupSid' {
130130

131131
if(-not $(Test-IsAdmin)) {
132-
Throw "'Add-ServiceDacl' Pester test needs local administrator privileges."
132+
Throw "'Get-CurrentUserTokenGroupSid' Pester test needs local administrator privileges."
133133
}
134134

135135
It 'Should not throw.' {
@@ -167,21 +167,62 @@ Describe 'Add-ServiceDacl' {
167167
{Get-Service | Add-ServiceDacl} | Should Not Throw
168168
}
169169

170-
It 'Should accept a service as a parameter argument.' {
171-
$Service = Get-Service | Select-Object -First 1
172-
$ServiceWithDacl = Add-ServiceDacl -Service $Service
170+
It 'Should fail for a non-existent service.' {
171+
$ServiceName = Get-RandomName
172+
{$Result = Add-ServiceDacl -Name $ServiceName} | Should Throw
173+
}
174+
175+
It 'Should accept a service name as a parameter argument.' {
176+
$ServiceName = Get-Service | Select-Object -First 1 | Select-Object -ExpandProperty Name
177+
$ServiceWithDacl = Add-ServiceDacl -Name $ServiceName
173178

174179
if(-not $ServiceWithDacl.Dacl) {
175180
Throw "'Add-ServiceDacl' doesn't return a Dacl for a service passed as parameter."
176181
}
177182
}
178183

184+
It 'Should accept an array of service names as a parameter argument.' {
185+
$ServiceNames = Get-Service | Select-Object -First 5 | Select-Object -ExpandProperty Name
186+
$ServicesWithDacl = Add-ServiceDacl -Name $ServiceNames
187+
188+
if(-not $ServicesWithDacl.Dacl) {
189+
Throw "'Add-ServiceDacl' doesn't return Dacls for an array of service names as a parameter."
190+
}
191+
}
192+
179193
It 'Should accept a service object on the pipeline.' {
180194
$Service = Get-Service | Select-Object -First 1
181195
$ServiceWithDacl = $Service | Add-ServiceDacl
182196

183197
if(-not $ServiceWithDacl.Dacl) {
184-
Throw "'Add-ServiceDacl' doesn't return a Dacl for a service passed as parameter."
198+
Throw "'Add-ServiceDacl' doesn't return a Dacl for a service object on the pipeline."
199+
}
200+
}
201+
202+
It 'Should accept a service name on the pipeline.' {
203+
$ServiceName = Get-Service | Select-Object -First 1 | Select-Object -ExpandProperty Name
204+
$ServiceWithDacl = $ServiceName | Add-ServiceDacl
205+
206+
if(-not $ServiceWithDacl.Dacl) {
207+
Throw "'Add-ServiceDacl' doesn't return a Dacl for a service name on the pipeline."
208+
}
209+
}
210+
211+
It 'Should accept multiple service objects on the pipeline.' {
212+
$Services = Get-Service | Select-Object -First 5
213+
$ServicesWithDacl = $Services | Add-ServiceDacl
214+
215+
if(-not $ServicesWithDacl.Dacl) {
216+
Throw "'Add-ServiceDacl' doesn't return Dacls for multiple service objects on the pipeline."
217+
}
218+
}
219+
220+
It 'Should accept multiple service names on the pipeline.' {
221+
$ServiceNames = Get-Service | Select-Object -First 5 | Select-Object -ExpandProperty Name
222+
$ServicesWithDacl = $ServiceNames | Add-ServiceDacl
223+
224+
if(-not $ServicesWithDacl.Dacl) {
225+
Throw "'Add-ServiceDacl' doesn't return Dacls for multiple service names on the pipeline."
185226
}
186227
}
187228

@@ -407,7 +448,7 @@ Describe 'Test-ServiceDaclPermission' {
407448
Describe 'Get-ServiceUnquoted' {
408449

409450
if(-not $(Test-IsAdmin)) {
410-
Throw "'Get-ServicePermission' Pester test needs local administrator privileges."
451+
Throw "'Get-ServiceUnquoted' Pester test needs local administrator privileges."
411452
}
412453

413454
It "Should not throw." {

0 commit comments

Comments
 (0)