-
Notifications
You must be signed in to change notification settings - Fork 0
/
IIS-Machine-Key-Audit.ps1
194 lines (172 loc) · 7.25 KB
/
IIS-Machine-Key-Audit.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<#
.SYNOPSIS
The Zeroed.Tech IIS Machine Key Auditer
.DESCRIPTION
Identifies all generated IIS machine keys and logs their generation dates and paths.
By running this script, you are acknowledging that you've taken a backup of this host.
Zeroed.Tech takes no responsibility if things go wrong.
.EXAMPLE
.\IIS-Machine-Key-Audit.ps1
.\IIS-Machine-Key-Audit.ps1 | Format-Table
.\IIS-Machine-Key-Audit.ps1 | Select ApplicationPool, UserName, MachineKeyFound,Created,Path | Format-Table
#>
$ErrorActionPreference = 'Continue'
Import-Module IISAdministration
<#
Improved error logging from https://stackoverflow.com/questions/38064704/how-can-i-display-a-naked-error-message-in-powershell-without-an-accompanying
#>
function Write-Error($message) {
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine($message)
[Console]::ResetColor()
}
function ResolveSidFromUsername($username) {
return (New-Object System.Security.Principal.NTAccount($username)).Translate([System.Security.Principal.SecurityIdentifier]).Value
}
function GetUsernameForApplicationPool($applicationPool) {
switch ($applicationPool.ProcessModel.IdentityType) {
"ApplicationPoolIdentity" {
return $applicationPool.Name
}
"SpecificUser" {
return $applicationPool.ProcessModel.UserName
}
"LocalSystem" {
return "system"
}
Default {
return $applicationPool.ProcessModel.IdentityType
}
}
}
function GetSidForApplicationPool($applicationPool) {
switch ($applicationPool.ProcessModel.IdentityType) {
"ApplicationPoolIdentity" {
return $applicationPool.Attributes["applicationPoolSid"].Value
}
Default {
return ResolveSidFromUsername(GetUsernameForApplicationPool($applicationPool))
}
}
}
function GetIISMachineKeyLocationForApplicationPool($applicationPool) {
switch ($applicationPool.ProcessModel.IdentityType) {
"LocalSystem" {
try {
$keyLocation = "HKLM:\SECURITY\Policy\Secrets\L`$ASP.NETAutoGenKeysV44.0.30319.0"
# Confirm key is present, Throws an exception if not/we can't access it
Get-ChildItem -Path $keyLocation -ErrorAction Stop | Out-Null
$keyTime = [datetime]::FromFileTimeUtc([System.BitConverter]::ToInt64([byte[]](Get-ItemPropertyValue -Path "$keyLocation\CupdTime" -Name "(Default)"), 0))
return [pscustomobject]@{
Path = $keyLocation
Date = $keyTime
}
}
catch [System.Security.SecurityException] {
Write-Error "Permission denied whilst accessing $keyLocation. Please rerun as an SYSTEM"
}
catch [System.Management.Automation.ItemNotFoundException] {
# Not found
return $null;
}
}
Default {
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS | Out-Null
# Check for the presence of a key under the application pools user profile
# Check if the user profile is mounted and if not, mount it
$profileMounted = $false;
$sid = GetSidForApplicationPool($applicationPool)
$keyLocation = "HKU:\$sid\Software\Microsoft\ASP.NET\4.0.30319.0"
$keyTime = $null
if (!(Test-Path -Path "HKU:\$sid")) {
# Registry is not mounted, check if it exists
if (MountRegistryForSid($sid)) {
$profileMounted = $true
}
}
# The profile should now be mounted
# Check for the presence of a machine key under this profile
$keyTime = GetMachineKeyTimeFromKey($keyLocation)
Remove-PSDrive -Name HKU -PSProvider Registry
if ($profileMounted) {
# We mounted a users profile so lets unmount it
UnmountRegistryForSid($sid);
}
# If we found a key, return it
if ($null -ne $keyTime) {
return [pscustomobject]@{
Path = $keyLocation
Date = $keyTime
}
}
# A user profile could not be found or a machine key could not be found within it, check under the local machine key
$keyLocation = "HKLM:\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\$sid"
$keyTime = GetMachineKeyTimeFromKey($keyLocation)
if ($null -ne $keyTime) {
return [pscustomobject]@{
Path = $keyLocation
Date = $keyTime
}
}
return $null
}
}
}
function GetMachineKeyTimeFromKey($key) {
if (Test-Path -Path $key) {
try {
# Test for the presence of a machine key, we don't need the actual value
Get-ItemProperty -Path $keyLocation | Select-Object -ExpandProperty "AutoGenKeyV4" -ErrorAction Stop | Out-Null
# Now that we know theres a machine key present, pull out its timestamp
$keyTime = Get-ItemPropertyValue -Path $keyLocation -Name "AutoGenKeyCreationTime"
return [datetime]::FromFileTimeUtc($keyTime)
}
catch {
}
}
return $null
}
function MountRegistryForSid($sid) {
try {
# Locate the path to the users profile
$profilePath = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid" -Name ProfileImagePath -ErrorAction Stop
$profileRegPath = "$profilePath\NTUSER.dat"
# Mount the users profile
reg load HKU\$sid $profileRegPath | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "A user profile for $sid was found at $profilRegPath but an error was encountered whilst mounting it. Error $LASTEXITCODE"
return $false
}
# The registry hive should be mounted now, verify we can access it
if (Test-Path -Path "HKU:\$sid") {
return $true
}
Write-Error "A user profile for $sid was successfully mounted but could not be accessed. Please reboot your host to ensure it's in a clean state"
return $false
}
catch [System.Management.Automation.ItemNotFoundException] {
# A user profile was not found for this sid
return $false
}
}
function UnmountRegistryForSid($sid) {
# Ensure nothing is holding a reference to the hive we're unloading
[gc]::collect()
# Mount the users profile
reg unload HKU\$sid $profileRegPath | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to unmount user profile for $sid. Please reboot your host to ensure it's in a clean state. Error $LASTEXITCODE"
}
}
Get-IISAppPool | ForEach-Object {
$appPool = $_
$iisMachineKey = GetIISMachineKeyLocationForApplicationPool($appPool)
[pscustomobject]@{
ApplicationPool = $appPool.Name
UserName = GetUsernameForApplicationPool($appPool)
SID = GetSidForApplicationPool($appPool)
MachineKeyFound = $($iisMachineKey -ne $null)
Created = $iisMachineKey.Date
Path = $iisMachineKey.Path
}
}