I saw this in someone’s signature on one of the Technet forums and though it was a really clever idea; nobody wants to put their email address in plaintext on the internet because it’ll just get hoovered up by spammers and most common obfuscation techniques are easily worked around by spambots, so how about…

[string](0..21|%{[char][int](23+("74778682874174878091987477868287237688239484").substring(($_*2),2))})-replace " "

Clever, isn’t it.

You can generate your own with the following code:

$out = $null
$email = "user@example.com"
foreach($char in $($email.tochararray())){$out += @([System.Convert]::ToUInt32($char)-23)}
$string = [string]$out -replace " "
"Your code is: [string](0..$($($out.length)-1)|%{[char][int](23+(`"$string`").substring((`$_*2),2))})-replace `" `""

Which should give an output that looks something like:

[string](0..15|%{[char][int](23+("94927891417897748689857823768886").substring(($_*2),2))})-replace " "

Email addresses with certain symbol characters may not encode properly.



Update: Please note that the up-to-date version of this blocklist is maintained here, this post will not be updated with any future changes to the Nobis ranges.

Just when you think you’ve finally blocked all of the Nobis Technology Group’s (aka Ubiquity Server Solutions) IP ranges they go and take over another range and carry on merrily spamming your comments section.

So, I’ve taken the nuclear option and blacklisted every IP range associated with AS15003 (which is their AS) as per the very helpful http://bgp.he.net/AS15003#_prefixes – yes, they really do have 867 IPv4 netblocks allocated to them.

I’ve collapsed the ranges down to a slightly more manageable 119 (there are a lot of contiguous blocks in there) and added it to my blocklist page, it’s also reproduced below, so help yourselves.

#Nobis/Ubiquity Ranges - http://bgp.he.net/AS15003#_prefixes
Deny from 23.19.0.0/16
Deny from 23.80.0.0/16
Deny from 23.81.0.0/17
Deny from 23.81.128.0/18
Deny from 23.81.192.0/20
Deny from 23.81.216.0/21
Deny from 23.81.224.0/19
Deny from 23.82.0.0/16
Deny from 23.83.0.0/17
Deny from 23.83.128.0/18
Deny from 23.83.192.0/20
Deny from 23.104.0.0/14
Deny from 23.108.0.0/15
Deny from 23.110.0.0/16
Deny from 23.111.251.0/24
Deny from 23.224.0.0/15
Deny from 23.226.48.0/20
Deny from 23.235.128.0/18
Deny from 23.248.192.0/18
Deny from 64.120.1.0/24
Deny from 64.120.2.0/23
Deny from 64.120.4.0/22
Deny from 64.120.8.0/21
Deny from 64.120.16.0/20
Deny from 64.120.32.0/19
Deny from 64.120.64.0/19
Deny from 64.120.96.0/20
Deny from 64.120.112.0/21
Deny from 64.120.122.0/23
Deny from 64.120.124.0/22
Deny from 67.201.0.0/21
Deny from 67.201.48.0/23
Deny from 69.31.107.0/24
Deny from 69.147.224.0/23
Deny from 69.147.227.0/24
Deny from 69.147.228.0/22
Deny from 69.147.232.0/21
Deny from 69.147.240.0/22
Deny from 69.147.244.0/24
Deny from 69.147.246.0/23
Deny from 69.147.248.0/21
Deny from 69.174.60.0/22
Deny from 70.32.32.0/20
Deny from 72.37.204.0/24
Deny from 72.37.221.0/24
Deny from 72.37.222.0/23
Deny from 72.37.224.0/21
Deny from 72.37.237.0/24
Deny from 72.37.242.0/23
Deny from 72.37.246.0/23
Deny from 74.113.144.0/22
Deny from 108.62.0.0/16
Deny from 108.171.33.0/24
Deny from 108.171.34.0/23
Deny from 108.171.36.0/23
Deny from 108.171.38.0/24
Deny from 108.171.43.0/24
Deny from 108.171.44.0/22
Deny from 108.171.53.0/24
Deny from 108.171.54.0/23
Deny from 108.171.56.0/24
Deny from 108.171.59.0/24
Deny from 108.171.60.0/23
Deny from 108.171.63.0/24
Deny from 108.177.128.0/17
Deny from 108.187.0.0/16
Deny from 142.91.0.0/16
Deny from 142.234.0.0/16
Deny from 147.255.0.0/16
Deny from 162.209.128.0/17
Deny from 162.222.68.0/22
Deny from 172.240.0.0/15
Deny from 172.247.0.0/16
Deny from 172.255.0.0/16
Deny from 173.208.0.0/18
Deny from 173.208.64.0/19
Deny from 173.208.96.0/21
Deny from 173.208.104.0/24
Deny from 173.208.106.0/23
Deny from 173.208.108.0/22
Deny from 173.208.112.0/20
Deny from 173.234.0.0/18
Deny from 173.234.64.0/20
Deny from 173.234.80.0/21
Deny from 173.234.88.0/23
Deny from 173.234.90.0/24
Deny from 173.234.92.0/22
Deny from 173.234.96.0/21
Deny from 173.234.105.0/24
Deny from 173.234.109.0/24
Deny from 173.234.110.0/23
Deny from 173.234.112.0/20
Deny from 173.234.128.0/19
Deny from 173.234.160.0/20
Deny from 173.234.176.0/21
Deny from 173.234.184.0/22
Deny from 173.234.188.0/24
Deny from 173.234.190.0/23
Deny from 173.234.192.0/18
Deny from 174.34.128.0/19
Deny from 174.34.160.0/20
Deny from 174.34.176.0/21
Deny from 174.34.184.0/22
Deny from 174.34.188.0/23
Deny from 174.34.190.0/24
Deny from 192.151.224.0/20
Deny from 192.151.248.0/21
Deny from 192.161.80.0/20
Deny from 192.163.160.0/19
Deny from 192.229.64.0/18
Deny from 192.238.128.0/17
Deny from 192.253.240.0/21
Deny from 196.45.112.0/22
Deny from 198.48.96.0/20
Deny from 198.48.112.0/22
Deny from 216.6.224.0/20
Deny from 216.151.31.0/24
Deny from 216.152.224.0/20
Deny from 216.193.237.0/24
#End of Nobis/Ubiquity Ranges

No doubt they’ll buy up more IP space as needed so they can continue to sell their services to spammers, malware distributors and other disgusting excuses for human beings.



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


My spam Blocklist has been slowly growing since I created it in order to stem the tide of comment spam coming from Nobis/Ubiquity Server-owned address blocks. Ultimately I made the choice to block all the netblocks that they had allocated from ARIN and that seemed to have worked, up until today when I started getting comment spam from some brand new Nobis/Ubiquity addresses.

It would seem that they’ve got themselves a netblock from RIPE and started using that for spamming as well; the range in question is 176.31.50.64/27 but given their apparent dedication to illegal activity, it wouldn’t surprise me if others start popping up here and there as well. Thankfully, the relative scarcity of available IPv4 blocks is making it much tougher for these spamming fuckers to evade blocking mechanisms without resorting to botnets.

That said, when you’re getting more than 10 times as many spam comments as legitimate ones, it doesn’t exactly fill you with confidence that we’ll ever get a real handle on the problem.



Given that my blog is relatively low traffic, it’s remarkable just how many spam comments and hacking attempts I log daily. A good 50% or more of all the spam comments I get originate from the same place: Ubiquity Server Solutions/Nobis Technology Group, who share a couple of overlapping IP ranges and are somewhat notorious if my brief Googling is anything to go by. I’m a big fan of Hanlon’s Razor, but in this case I’m really not sure either way.

So, as of today, their entire ranges are blacklisted:

Deny from 173.208.100.0/22 #Ubiquity Server Solutions
Deny from 108.62.0.0/16 #Nobis Technology Group

I don’t like having to block entire /16 ranges because I know there are bound to be false positives in there somewhere, but frankly it’s the only way to make things manageable right now.

I expect to see my error.log grow exponentially over the next few days.

Update: And another range of theirs that was still spamming me…

Deny from 173.234.0.0/16 #Nobis Technology Group

Update: And yet another…

Deny from 23.19.0.0/16 #Nobis Technology Group

Update: Guess who…

Deny from 64.120.0.0/17 #Nobis Technology Group

Update: Right, let’s make this simple; courtesy of ARIN’s WHOIS Database

#All Nobis/Ubiquity ARIN Netblocks
Deny from 70.32.32.0/20
Deny from 67.201.48.0/23
Deny from 72.37.145.0/24
Deny from 173.208.0.0/17
Deny from 69.174.60.0/22
Deny from 174.34.128.0/18
Deny from 173.234.0.0/16
Deny from 108.62.0.0/16
Deny from 72.37.224.0/21
Deny from 23.19.0.0/16
Deny from 72.37.237.0/24
Deny from 72.37.218.0/23
Deny from 72.37.222.0/23
Deny from 72.37.221.0/24
Deny from 67.201.0.0/21
Deny from 72.37.242.0/23
Deny from 67.201.40.0/24
Deny from 72.37.246.0/23
Deny from 216.6.224.0/20
Deny from 72.37.204.0/24
Deny from 69.147.224.0/23
Deny from 64.120.0.0/17

Update: My complete comment spam blocklist is now available here.