-
Notifications
You must be signed in to change notification settings - Fork 4
/
HaveIBeenPwned_User_Audit.ps1
220 lines (184 loc) · 7.36 KB
/
HaveIBeenPwned_User_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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#Get the required params
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[String]$Username,
[Parameter(Mandatory=$true)]
[String]$Password,
[Parameter(Mandatory=$true)]
[String]$tenantId,
[Parameter(Mandatory=$true)]
[String]$clientId,
[Parameter(Mandatory=$true)]
[String]$ClientSecret,
[Parameter(Mandatory=$true)]
[String]$HaveIBeenPwndAPIKey,
[Parameter(Mandatory=$true)]
[String]$emailTo
)
# Set the web request protocol
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Azure AD OAuth Token for Graph API
# Body params
$granttype = 'password'
$scope = 'https://graph.microsoft.com/.default'
# Construct URI
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Construct Body
$body = @{
client_id = $clientId
scope = $scope
client_secret = $clientSecret
grant_type = $granttype
username = $Username
password = $Password
}
# Get OAuth 2.0 Token
$tokenRequest = Invoke-WebRequest -Method 'POST' -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
# Access Token
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
## Null the arrays ##
[array]$breachResult = $Null
[array]$List2 = $Null
# Get the organization name for the HaveIBeenPwned headers
$Select = "$" + "Select=displayName"
$URI = "https://graph.microsoft.com/v1.0/organization?$Select"
$Head = @{
Authorization = "Bearer $token"
}
$Org = Invoke-WebRequest -Method 'GET' -Uri $URI -Headers $Head -UseBasicParsing | ConvertFrom-Json
$OrgName = $Org.value | Select-Object -ExpandProperty DisplayName
# Build the header
$headers = @{
"User-Agent" = $OrgName + " Breached User Account Check"
"hibp-api-key" = $HaveIBeenPwndAPIKey
}
# Get a list of user email addresses in the tenant
$Filter = "$" + "Select=mail"
$URL = "https://graph.microsoft.com/v1.0/users?$Filter"
$Header = @{
Authorization = "Bearer $token"
'Content-type' = 'application/json'
}
$User = Invoke-WebRequest -Method 'GET' -Uri $URL -Headers $Header -UseBasicParsing | ConvertFrom-Json
$UPN = $User.value | Select-Object -ExpandProperty Mail
# Find breached user accounts and build the email tables #
foreach ($U in $UPN)
{
$uriEncodeEmail = [uri]::EscapeDataString($U)
$uri = "https://haveibeenpwned.com/api/v3/breachedaccount/$uriEncodeEmail"
$breachResult = $null
try {
[array]$breachResult = Invoke-RestMethod -Uri $uri -Headers $headers -ErrorAction SilentlyContinue
}
catch {
if($error[0].Exception.response.StatusCode -match "NotFound"){
Write-Host "No Breach detected for $U" -ForegroundColor Green
}else{
Write-Host "Cannot retrieve results due to rate limiting or suspect IP. You may need to try a different computer or wait 24 hours."
}
}
if ($breachResult)
{
$breachList = foreach ($breach in $breachResult) {
$breach | Add-Member -MemberType NoteProperty -Name Email -Value "$U"
$breach | Add-Member -MemberType NoteProperty -Name BreachedName -Value "$($breach.Title)"
$breach | Add-Member -MemberType NoteProperty -Name BreachedDate -Value "$($breach.BreachDate)"
$breach | Select-Object Email, BreachedName, BreachedDate, LastPasswordChange
}
foreach ($B in $breachList)
{
# Get 30 days from the Breach Date
$BD = $breach.BreachDate
$Today = Get-Date -format yyyy-MM-dd
$30DA = (Get-Date).AddDays(-30).ToString('yyyy-MM-dd')
$PassDate = Get-Date $user.LastPasswordChangeTimestamp -format yyyy-MM-dd
# If breach date was within the last 30 days, format the email #
if ($BD -le $Today -and $BD -gt $30DA)
{
$List1 = New-Object PSObject
$List1 | Add-Member -MemberType NoteProperty -Name Pwned_Email -Value "$email"
$List1 | Add-Member -MemberType NoteProperty -Name Breach_Name -Value "$($breach.BreachedName)"
$List1 | Add-Member -MemberType NoteProperty -Name Breach_Date -Value "$($BD)"
$List1 | Add-Member -MemberType NoteProperty -Name Password_Change_Date -Value "$($PassDate)"
[array]$List2 += $List1
}
}
}
# API limiting: one request per IP every 1500 milliseconds. Limit violations will be blocked for 24 hours. Set scheduled task to prevent multiple instances #
Start-sleep -Milliseconds 2000
}
## Send the email for compromised accounts ##
if($BD -le $Today -and $BD -gt $30DA)
{
## Set the Date ##
$Date = Get-Date -Format 'MMMM dd yyyy'
## Set the CSS for the email ##
$CSS = @"
table.rosyBrownTable {
border: 4px solid #BA8697;
background-color: #555555;
width: 400px;
text-align: center;
border-collapse: collapse;
}
table.rosyBrownTable td, table.rosyBrownTable th {
border: 1px solid #555555;
padding: 4px 4px;
}
table.rosyBrownTable tbody td {
font-size: 13px;
font-weight: bold;
color: #FFFFFF;
}
table.rosyBrownTable tr:nth-child(even) {
background: #BA8697;
}
table.rosyBrownTable td:nth-child(even) {
background: #BA8697;
}
table.rosyBrownTable tfoot {
font-weight: bold;
background: #BA8697;
border-top: 1px solid #444444;
}
table.rosyBrownTable tfoot .links {
text-align: right;
}
table.rosyBrownTable tfoot .links a{
display: inline-block;
background: #FFFFFF;
color: #BA8697;
padding: 2px 8px;
border-radius: 5px;
}
"@
## Add originating information. (HaveIBeenPwned ULA requires credit and link to the website) ##
$HIBP = "<a href='https://haveibeenpwned.com'>HaveIBeenPwned</a>"
$Footer = "This report uses API and data provided by $HIBP <br>"
$emailList = ( $List2 | Sort-Object -unique Pwned_Email, Breach_Name, Breach_Date, Password_Change_Date | Select Pwned_Email, Breach_Name, Breach_Date, Password_Change_Date | ConvertTo-Html -Head $CSS)
$Date = Get-Date -Format 'MMMM dd yyyy'
[string][ValidateNotNullOrEmpty()]$passwd = $Password
$secpasswd = ConvertTo-SecureString -String $passwd -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($Username, $secpasswd)
$From = $Username
$Subject = "HaveIBeenPwned User Audit " + $Date
$emailBody = "$emailList $Footer"
$SMTPPort = "587"
$SMTPServer = "smtp.office365.com"
Send-MailMessage -From $From -to $emailTo -Subject $Subject -Body $emailBody -BodyAsHTML -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $cred
}
## If no compromised accounts are found ##
else
{
$Date = Get-Date -Format 'MMMM dd yyyy'
[string][ValidateNotNullOrEmpty()]$passwd = $Password
$secpasswd = ConvertTo-SecureString -String $passwd -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($Username, $secpasswd)
$From = $Username
$Subject = "HaveIBeenPwned User Audit " + $Date
$emailBody = "Good news! No user accounts have been breached in the last 30 days." + $Footer
$SMTPPort = "587"
$SMTPServer = "smtp.office365.com"
Send-MailMessage -From $From -to $emailTo -Subject $Subject -Body $emailBody -BodyAsHTML -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $cred
}