-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathBlackhole-DNS.ps1
334 lines (288 loc) · 15.9 KB
/
Blackhole-DNS.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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
####################################################################################
#.Synopsis
# Creates and deletes blackhole domains in Windows DNS servers.
#
#.Description
# Script takes fully-qualified domain names (FQDNs) and/or simple domain
# names, then uses them to create primary zones (not Active Directory
# integrated) in a Windows DNS server, setting all zones to resolve to a
# single chosen IP address. The IP address might be "0.0.0.0" or the IP
# of an internal server configured for logging. The intention is to
# prevent clients from resolving the correct IP address of unwanted FQDNs
# and domain names, such as for malware and phishing sites. Such names
# are said to be "blackholed" since they often resolve to 0.0.0.0.
#
#.Parameter InputFile
# Path to a file which contains the FQDNs and domain names to blackhole.
# File can have blank lines, comment lines (# or ;), multiple FQDNs or
# domains per line (space- or comma-delimited), and can be a hosts file
# with IP addresses too (addresses and localhost entires will be ignored).
# You can also include wildcards to input multiple files, e.g.,
# "*bad*.txt", or pass in an array of file objects instead of a string.
#
#.Parameter Domain
# One or more FQDNs or domains to blackhole, separated by spaces or commas.
#
#.Parameter BlackHoleIP
# The IP address to which all blackholed names will resolve. The
# default is "0.0.0.0", but perhaps is better set to an internal server.
# Remember, there is only one IP for ALL the blackholed domains.
#
#.Parameter DnsServerName
# FQDN of the Windows DNS server. Defaults to localhost. If specified,
# please always use a fully-qualified domain name (FQDN), especially if
# the DNS server is a stand-alone or in a different domain.
#
#.Parameter IncludeWildCard
# Will add a wildcard host record (*) to every blackhole domain, which
# will match all possible hostnames within that domain. Keep in mind
# that blackholing "www.sans.org" will treat the "www" as a domain
# name, so a wildcard is not needed to match it; but blackholing just
# "sans.org" will not match "www.sans.org" or "ftp.sans.org" without
# the wildcard. If you only want to blackhole the exact FQDN or domain
# name supplied to the script, then don't include a wildcard record.
# If you are certain that you do not want to resolve anything whatsoever
# under the blackholed domains, then include the wildcard DNS record.
#
#.Parameter ReloadBlackHoleDomains
# Will cause every blackholed domain in your DNS server to re-read the
# one shared zone file they all use. This is the zone file for the
# 000-blackholed-domain.local domain. Keep in mind that the DNS
# graphical management tool shows you what is cached in memory, not
# what is in the zone file. Reload the blackhole domains if you,
# for example, change the blackhole IP address. Using this switch
# causes any other parameters to be ignored.
#
#.Parameter DeleteBlackHoleDomains
# Will delete all blackhole domains, but will not delete any regular
# non-blackhole domains. Strictly speaking, this deletes any domain
# which uses a zone file named "000-blackholed-domain.local.dns".
# The zone file itself is not deleted, but it only 1KB in size.
# Using this switch causes any other parameters to be ignored.
#
#.Parameter RemoveLeadingWWW
# Some lists of blackhole names are simple domain names, while other
# lists might prepend "www." to the beginning of many of the names.
# Use this switch to remove the "www." from the beginning of any
# name to be blackholed, then consider using -IncludeWildCard too.
# Note that "www.", "www1.", "www2." ... "www9." will be cut too,
# but only for a single digit after the "www" part (1-9 only).
#
#.Parameter Credential
# An "authority\username" string to explicitly authenticate to the
# DNS server instead of using single sign-on with the current
# identity. The authority is either a server name or a domain name.
# You will be prompted for the passphrase. You can also pass in
# a variable with a credential object from Get-Credential.
#
#.Example
# .\Blackhole-DNS.ps1 -Domain "www.sans.org"
#
# This will create a primary DNS domain named "www.sans.org"
# which will resolve to "0.0.0.0". DNS server is local.
#
#.Example
# .\Blackhole-DNS.ps1 -Domain "www.sans.org" -BlackHoleIP "10.1.1.1"
#
# This will create a primary DNS domain named "www.sans.org"
# which will resolve to "10.1.1.1". DNS server is local.
#
#.Example
# .\Blackhole-DNS.ps1 -InputFile file.txt -IncludeWildCard
#
# This will create DNS domains out of all the FQDNs and domain
# names listed in file.txt, plus add a wildcard (*) record.
#
#.Example
# .\Blackhole-DNS.ps1 -ReloadBlackHoleDomains
#
# Perhaps after changing the blackhole IP address, this will cause
# all blackholed domains to re-read their shared zone file.
#
#.Example
# .\Blackhole-DNS.ps1 -DeleteBlackHoleDomains
#
# This will delete all blackholed domains, but will not delete
# any other domains. This does not delete the blackhole zone file.
#
#.Example
# .\Blackhole-DNS.ps1 -InputFile file.txt -DnsServerName `
# "server7.sans.org" -Credential "server7\administrator"
#
# This will create blackholed domains from file.txt on a remote
# DNS server named "server7.sans.org" with explicit credentials.
# You will be prompted for the passphrase.
#
#.Example
# $Cred = Get-Credential -Credential "server7\administrator"
#
# .\Blackhole-DNS.ps1 -InputFile *evil*.txt `
# -DnsServerName "server7.sans.org" -Credential $Cred
#
# This will create blackholed domains from *evil*.txt on a remote
# DNS server named "server7.sans.org" with explicit credentials
# supplied in a credential object ($Cred) which can be reused again.
# Multiple input files may match "*evil*.txt".
#
#Requires -Version 2.0
#
#.Notes
# Author: Jason Fossen (http://www.sans.org/windows-security/)
# Version: 1.0
# Updated: 30.Aug.2010
# LEGAL: PUBLIC DOMAIN. SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR
# GUARANTEES OF ANY KIND, INCLUDING BUT NOT LIMITED TO
# MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. ALL
# RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF THE AUTHOR,
# SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF
# ANY SUCH DAMAGE. IF YOUR STATE DOES NOT PERMIT THE COMPLETE
# LIMITATION OF LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE
# NOW PROHIBITED TO HAVE IT. TEST ON NON-PRODUCTION SERVERS.
####################################################################################
Param ($InputFile, [String] $Domain, [String] $BlackHoleIP = "0.0.0.0", [String] $DnsServerName = ".", [Switch] $IncludeWildCard,
[Switch] $ReloadBlackHoleDomains, [Switch] $DeleteBlackHoleDomains, [Switch] $RemoveLeadingWWW, $Credential)
# Check for common help switches.
if (($InputFile -ne $Null) -and ($InputFile.GetType().Name -eq "String") -and ($InputFile -match "/\?|-help|--h|--help"))
{
If ($Host.Version.Major -ge 2) { get-help -full .\blackhole-dns.ps1 }
Else {"`nPlease read this script's header in Notepad for the help information."}
Exit
}
# Confirm PowerShell 2.0 or later.
If ($Host.Version.Major -lt 2) { "This script requires PowerShell 2.0 or later.`nDownload the latest version from http://www.microsoft.com/powershell`n" ; Exit }
# If necessary, prompt user for domain\username and a passphrase.
If ($Credential) { $Cred = Get-Credential -Credential $Credential }
Function Main
{
# Test access to WMI at target server ($ZoneClass is used later too).
$ZoneClass = GetWMI -Query "SELECT * FROM META_CLASS WHERE __CLASS = 'MicrosoftDNS_Zone'"
If (-not $? -or $ZoneClass.Name -ne "MicrosoftDNS_Zone") { Throw("Failed to connect to WMI service or the WMI DNS_Zone namespace!") ; Exit }
##### Parse input domains, but exclude the following: localhost, any IP addresses, blank lines.
#Process any -Domain args.
[Object[]] $Domains = @($Domain -Split "[\s\;\,]")
#Process any -InputFile arguments and expand any wildcards.
If (($InputFile -ne $Null) -and ($InputFile.GetType().Name -eq "String")) { $InputFile = dir $InputFile }
If ($InputFile -ne $Null) { $InputFile | ForEach { $Domains += Get-Content $_ | Where { $_ -notmatch "^[\#\;\<]" } | ForEach { $_ -Split "[\s\;\,]" } } }
#If -RemoveLeadingWWW was used, edit out those "www." strings.
If ($RemoveLeadingWWW) { 0..$([Int] $Domains.Count - 1) | ForEach { $Domains[$_] = $Domains[$_] -Replace "^www[1-9]{0,1}\.","" } }
#Convert to lowercase, remove redundants, exclude blank lines, exclude IPs, exclude anything with a colon in it, e.g., IPv6.
$Domains = $Domains | ForEach { $_.Trim().ToLower() } | Sort -Unique | Where { $_.Length -ne 0 -and $_ -notmatch "^localhost$|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\:" }
If ($Domains.Count -le 0) { "No domains specified!" ; exit } Else { "`n" + [String] $Domains.Count + " domain(s) to blackhole to $BlackHoleIP." }
#####
##### Get or create the master blackhole zone: 000-blackholed-domain.local.
$WmiPath = "Root\MicrosoftDNS:MicrosoftDNS_Zone.ContainerName='000-blackholed-domain.local',DnsServerName='" + $DnsServerName + "',Name='000-blackholed-domain.local'"
If (InvokeWmiMethod -ObjectPath $WmiPath -MethodName GetDistinguishedName) #Zone exists.
{
"`nThe 000-blackholed-domain.local zone already exists, deleting its existing DNS records."
$ExistingRecords = @(GetWMI -Query "SELECT * FROM MicrosoftDNS_AType WHERE ContainerName = '000-blackholed-domain.local'")
If (-not $?) { Throw("Failed to query the A records of the 000-blackholed-domain.local domain!") ; exit }
If ($ExistingRecords.Count -gt 0) { $ExistingRecords | ForEach { $_.Delete() } }
}
Else #Zone does not exist.
{
"`nCreating the 000-blackholed-domain.local zone and its zone file."
$RR = $ZoneClass.CreateZone("000-blackholed-domain.local",0,$false,$null,$null,"only-edit.000-blackholed-domain.local.")
If (-not $? -or $RR.__CLASS -ne "__PARAMETERS")
{
If ($Credential -And ($DnsServerName.Length -ne 0) -And ($DnsServerName -NotLike "*.*")) { "Did you forget to use a FQDN for the DNS server name?" }
Throw("Failed to create the 000-blackholed-domain.local domain!")
Exit
}
}
#####
##### Create DNS records in master blackhole zone.
# Create the default A record with the blackhole IP address.
$RecordClass = $Null # Defaults to "IN" class of record.
$TTL = 120 # Seconds. Defaults to zone default if you set this to $null.
$ATypeRecords = GetWMI -Query "SELECT * FROM META_CLASS WHERE __CLASS = 'MicrosoftDNS_AType'"
If (-not $? -or $ATypeRecords.Name -ne "MicrosoftDNS_AType") { "`nFailed to query A type records, but continuing..." }
$ARecord = $ATypeRecords.CreateInstanceFromPropertyData($DnsServerName,"000-blackholed-domain.local","000-blackholed-domain.local",$RecordClass,$TTL,$BlackHoleIP)
If ($?) { "Created default DNS record for the 000-blackholed-domain.local zone ($BlackHoleIP)." }
Else { "Failed to create default A record for the 000-blackholed-domain.local zone, but continuing..." }
# Create the wildcard A record if the -IncludeWildCard switch was used.
If ($IncludeWildCard)
{
$ARecord = $ATypeRecords.CreateInstanceFromPropertyData($DnsServerName,"000-blackholed-domain.local","*.000-blackholed-domain.local",$RecordClass,$TTL,$BlackHoleIP)
If ($?) { "Created the wildcard (*) record for the 000-blackholed-domain.local zone ($BlackHoleIP)." }
Else { "Failed to create the wildcard (*) record for the 000-blackholed-domain.local zone, but continuing..." }
}
# Update zone data file on disk after adding the A record(s).
If (InvokeWmiMethod -ObjectPath $WmiPath -MethodName WriteBackZone)
{ "Updated the zone file for 000-blackholed-domain.local." }
Else
{
Start-Sleep -Seconds 2 #Just seems to help...
$ItWorked = InvokeWmiMethod -ObjectPath $WmiPath -MethodName WriteBackZone
If ($ItWorked) { "Updated the zone file for 000-blackholed-domain.local." }
Else {"`nFailed to update the server data file for the 000-blackholed-domain.local zone, but continuing..." }
}
#####
##### Create the blackholed domains using the 000-blackholed-domain.local.dns zone file.
$Created = $NotCreated = 0
$CurrentErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
$Domains | ForEach `
{
$ZoneClass.CreateZone($_,0,$false,"000-blackholed-domain.local.dns",$null,"only-edit.000-blackholed-domain.local.") | Out-Null
If ($?) { $Created++ } Else { $NotCreated++ }
}
$ErrorActionPreference = $CurrentErrorActionPreference
"`nBlackhole domains created at the DNS server: $Created"
"`nDomains NOT created (maybe already existed): $NotCreated`n"
} #End of Main
Function GetWMI ([String] $Query)
{
# This is a helper function for the sake of -Credential.
If ($Credential) { Get-WmiObject -Query $Query -Namespace "Root/MicrosoftDNS" -ComputerName $DnsServerName -Credential $Cred }
Else { Get-WmiObject -Query $Query -Namespace "Root/MicrosoftDNS" -ComputerName $DnsServerName }
}
Function InvokeWmiMethod ([String] $ObjectPath, [String] $MethodName)
{
# This is a helper function for the sake of -Credential.
$ErrorActionPreference = "SilentlyContinue"
If ($Credential) { Invoke-WmiMethod -Path $ObjectPath -Name $MethodName -ComputerName $DnsServerName -Credential $Cred }
Else { Invoke-WmiMethod -Path $ObjectPath -Name $MethodName -ComputerName $DnsServerName }
$? #Returns
}
Function Reload-BlackHoledDomains
{
# Function causes blackholed domains to be reloaded from the shared master zone file, perhaps after a -BlackHoleIP change.
# You may get errors if zone is temporarily locked for updates, but the DNS server's default lock is only two minutes.
"`nReloading blackholed domains from the 000-blackholed-domain.local.dns zone file, `nwhich may take a few minutes if you have 10K+ domains..."
$BHDomains = @(GetWMI -Query "SELECT * FROM MicrosoftDNS_Zone WHERE DataFile = '000-blackholed-domain.local.dns'")
If (-not $?) { Throw("Failed to connect to WMI service!") ; exit }
"`nBlackholed domains to be reloaded from the zone file: " + [String] $BHDomains.Count
$Locked = @() #Index numbers of zones which are temporarily locked and cannot be reloaded yet.
$i = 0
$ErrorActionPreference = "SilentlyContinue"
If ($BHDomains.Count -gt 0) { $BHDomains | ForEach { $_.ReloadZone() ; If (-not $?) { $Locked += $i } ; $i++ } }
If ($Locked.Count -gt 0)
{
"`n" + [String] $Locked.Count + " zone(s) are still temporarily locked, will try those again in two minutes.`nPlease wait two minutes or hit Ctrl-C to cancel..."
Start-Sleep -Seconds 60
"Just one more minute... Thank you for holding, your function call is appreciated."
Start-Sleep -Seconds 30
"Just 30 more seconds... Your patience is admirable, and you're good looking too!"
Start-Sleep -Seconds 35
$Locked | ForEach { $BHDomains[$_].ReloadZone() ; if (-not $?) { "`n" + [String] $BHDomains[$_].ContainerName + " is still locked and has not reloaded yet." } }
}
"`nThe other blackholed domains were successfully reloaded.`n"
} #End
Function Delete-BlackHoledDomains
{
# Delete all blackholed zones, including 000-blackholed-domain.local, but
# note that this does not delete the (tiny) zone file on the drive.
"`nDeleting all blackholed domains, which may take a few minutes if you have 10K+ domains..."
$BHDomains = @(GetWMI -Query "SELECT * FROM MicrosoftDNS_Zone WHERE DataFile = '000-blackholed-domain.local.dns'")
If (-not $?) { Throw("Failed to connect to WMI service!") ; exit }
"`nBlackhole domains to be deleted: " + [String] $BHDomains.Count
$i = 0
If ($BHDomains.Count -gt 0) { $BHDomains | ForEach { $_.Delete() ; If ($?){$i++} } }
"Blackhole domains deleted count: $i`n"
} #End
########################
# MAIN #
########################
If ($ReloadBlackHoleDomains) { Reload-BlackHoledDomains ; Exit }
If ($DeleteBlackHoleDomains) { Delete-BlackHoledDomains ; Exit }
Main