Import & Update Spamhaus DROP List in ISA/TMG



Along with their anti-spam Block Lists, Spamhaus also provide a DROP list, which is a list of stolen ‘hijacked’ netblocks and netblocks controlled entirely by criminals and professional spammers. As nothing good can come out of these ranges, it’s handy to block them outright at the firewall level, if that firewall happens to be TMG or ISA you can make use of the following script to keep the DROP list up-to-date.

The IP address conversion functions in this script are all courtesy of Chris Dent of www.indented.co.uk and were a godsend. To make use of this script you will need to create your Network object in ISA/TMG and update the network name in the following code to match $ipranges = $server.NetworkConfiguration.Networks.item("Spamhaus DROP List").IpRangeSet. You will also need to set $dropfile to match the location where you will be storing the downloaded DROP List, this needs to be retained so that updates can easily do a diff on the file without having to query ISA/TMG, which is much slower. Create a blank text file matching this filename & path before running the script for the first time.

<#
Copyright (c) 2013, Adam Beardwood
All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: 
 
1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer. 
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution. 
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
 
#Grab Spamhaus DROP List and update TMG network sets based on changes
#IP Conversion Functions Courtesy of Chris Dent - http://www.indented.co.uk/index.php/2010/01/23/powershell-subnet-math/
#Adam Beardwood 26/03/2013
#v1.0 - Initial Release
#v1.1 - Considerable improvements to error checking
 
Function ConvertTo-DottedDecimalIP {
  <#
    .Synopsis
      Returns a dotted decimal IP address from either an unsigned 32-bit integer or a dotted binary string.
    .Description
      ConvertTo-DottedDecimalIP uses a regular expression match on the input string to convert to an IP address.
    .Parameter IPAddress
      A string representation of an IP address from either UInt32 or dotted binary.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [String]$IPAddress
  )
 
  Process {
    Switch -RegEx ($IPAddress) {
      "([01]{8}\.){3}[01]{8}" {
        Return [String]::Join('.', $( $IPAddress.Split('.') | ForEach-Object { [Convert]::ToUInt32($_, 2) } ))
      }
      "\d" {
        $IPAddress = [UInt32]$IPAddress
        $DottedIP = $( For ($i = 3; $i -gt -1; $i--) {
          $Remainder = $IPAddress % [Math]::Pow(256, $i)
          ($IPAddress - $Remainder) / [Math]::Pow(256, $i)
          $IPAddress = $Remainder
         } )
 
        Return [String]::Join('.', $DottedIP)
      }
      default {
        Write-Error "Cannot convert this format"
      }
    }
  }
}
 
Function ConvertTo-DecimalIP {
  <#
    .Synopsis
      Converts a Decimal IP address into a 32-bit unsigned integer.
    .Description
      ConvertTo-DecimalIP takes a decimal IP, uses a shift-like operation on each octet and returns a single UInt32 value.
    .Parameter IPAddress
      An IP Address to convert.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress
  )
 
  Process {
    $i = 3; $DecimalIP = 0;
    $IPAddress.GetAddressBytes() | ForEach-Object { $DecimalIP += $_ * [Math]::Pow(256, $i); $i-- }
 
    Return [UInt32]$DecimalIP
  }
}
 
Function Get-BroadcastAddress {
  <#
    .Synopsis
      Takes an IP address and subnet mask then calculates the broadcast address for the range.
    .Description
      Get-BroadcastAddress returns the broadcast address for a subnet by performing a bitwise AND 
      operation against the decimal forms of the IP address and inverted subnet mask. 
      Get-BroadcastAddress expects both the IP address and subnet mask in dotted decimal format.
    .Parameter IPAddress
      Any IP address within the network range.
    .Parameter SubnetMask
      The subnet mask for the network.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress, 
 
    [Parameter(Mandatory = $True, Position = 1)]
    [Alias("Mask")]
    [Net.IPAddress]$SubnetMask
  )
 
  Process {
    Return ConvertTo-DottedDecimalIP $((ConvertTo-DecimalIP $IPAddress) -BOr `
      ((-BNot (ConvertTo-DecimalIP $SubnetMask)) -BAnd [UInt32]::MaxValue))
  }
}
 
#Create ISA COM Objects
$root = new-object -comObject "FPC.Root" -strict
#Get our array (well, there is only one in this case)
$server = $root.Arrays | Select-Object -first 1
 
#Get IPRangeSet for the Spamhaus DROP List network
$ipranges = $server.NetworkConfiguration.Networks.item("Spamhaus DROP List").IpRangeSet
$dropfile = "c:\users\adam\desktop\drop.txt"
 
#If local copy of DROP file is missing, remove all addresses and start clean
if($(test-path $dropfile) -eq $false){
 
	$ipranges.removeall()
 
	#Download DROP List from webserver
	$source = "http://www.spamhaus.org/drop/drop.txt"
	$destination = $dropfile
	$wc = New-Object System.Net.WebClient
	$wc.DownloadFile($source, $destination)
 
	$drop = ""
	$difference = Compare-Object $drop $(gc $dropfile)
 
}else{
 
	$drop = gc $dropfile
	if($drop -eq $null){$drop = ";"}
 
	#Download DROP List from webserver
	$source = "http://www.spamhaus.org/drop/drop.txt"
	$destination = $dropfile
	$wc = New-Object System.Net.WebClient
	$wc.DownloadFile($source, $destination)
 
	$difference = Compare-Object $drop $(gc $dropfile)
}
 
if($difference -ne $null){
 
	$difference
 
	foreach($diff in $difference){
		if ($diff.inputobject.startswith(";")){
		}else{
			$ip = $($diff.inputobject.split(" ; ")[0]).split("/")[0]
			$cidr = $($diff.inputobject.split(" ; ")[0]).split("/")[1]
			$mask = ConvertTo-DottedDecimalIP ([Convert]::ToUInt32($(("1" * $cidr).PadRight(32, "0")), 2))
			$bcast = Get-BroadcastAddress $ip $mask
 
			Switch ($diff.SideIndicator){
				"=>" {if(!$ipranges.isipinset($ip)){$ipranges.add($ip,$bcast)}else{write-host "IP Address Range $ip - $bcast already in DROP List" -fore red}} #Not in local
				"<=" {if($ipranges.isipinset($ip)){$ipranges.remove($ip,$bcast)}else{write-host "IP Address Range $ip - $bcast not present in Network Object" -fore yellow}} #Not in remote
			}
		}
	}
 
}else{
	write-host "No Changes Since Last Update" -fore green
	}
 
$ipranges.save()
 
write-host "Update Complete" -fore green

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.