March 2012 - Posts

Auto blocking attacking IP address

This is to share my PowerShell code online. I original asked this question on MSDN forum (or TechNet?) here:

http://social.technet.microsoft.com/Forums/en-US/winserversecurity/thread/f950686e-e3f8-4cf2-b8ec-2685c1ed7a77

In short, this is trying to find attacking IP address then add it into Firewall block rule. So I suppose:

1, You are running a Windows Server 2008 facing the Internet.

2, You need to have some port open for service, e.g. TCP 21 for FTP; TCP 3389 for Remote Desktop. You can see in my code I’m only dealing with these two since that’s what I opened. You can add further port number if you like, but the way to process might be different with these two.

3, I strongly suggest you use STRONG password and follow all security best practices, this ps1 code is NOT for adding security to your server, but reduce the nuisance from brute force attack, and make sys admin’s life easier: i.e. your FTP log won’t hold megabytes of nonsense, your Windows system log will not roll back and only can tell you what happened last month.

4, You are comfortable with setting up Windows Firewall rules, in my code, my rule has a name of “MY BLACKLIST”, you need to setup a similar one, and set it to BLOCK everything.

5, My rule is dangerous because it has the risk to block myself out as well. I do have a backup plan i.e. the DELL DRAC5 so that if that happens, I still can remote console to my server and reset the firewall.

6, By no means the code is perfect, the coding style, the use of PowerShell skills, the hard coded part, all can be improved, it’s just that it’s good enough for me already. It has been running on my server for more than 7 MONTHS.

7, Current code still has problem, I didn’t solve it yet, further on this point after the code. :)


 

 #Dong Xie, March 2012
 #my simple code to monitor attack and deal with it
 #Windows Server 2008 Logon Type
 #8: NetworkCleartext, i.e. FTP
 #10: RemoteInteractive, i.e. RDP
 
 $tick = 0;
 "Start to run at: " + (get-date);
 
 $regex1 = [regex] "192\.168\.100\.(?:101|102):3389\s+(\d+\.\d+\.\d+\.\d+)";
 $regex2 = [regex] "Source Network Address:\t(\d+\.\d+\.\d+\.\d+)";
 
 while($True) {
  $blacklist = @();
 
  "Running... (tick:" + $tick + ")"; $tick+=1;
 
 #Port 3389
 $a = @()
 netstat -no | Select-String ":3389" | ? { $m = $regex1.Match($_); `
   $ip = $m.Groups[1].Value; if ($m.Success -and $ip -ne "10.0.0.1") {$a = $a + $ip;} }

 if ($a.count -gt 0) {
   $ips = get-eventlog Security -Newest 1000 | Where-Object {$_.EventID -eq 4625 -and $_.Message -match "Logon Type:\s+10"} | foreach { `
     $m = $regex2.Match($_.Message); $ip = $m.Groups[1].Value; $ip; } | Sort-Object | Tee-Object -Variable list | Get-Unique

   foreach ($ip in $a) { if ($ips -contains $ip) {
     if (-not ($blacklist -contains $ip)) {
       $attack_count = ($list | Select-String $ip -SimpleMatch | Measure-Object).count;
       "Found attacking IP on 3389: " + $ip + ", with count: " + $attack_count;
       if ($attack_count -ge 20) {$blacklist = $blacklist + $ip;}
     } 
    }
   }
 }
 
 
 #FTP
 $now = (Get-Date).AddMinutes(-5); #check only last 5 mins.
    #Get-EventLog has built-in switch for EventID, Message, Time, etc. but using any of these it will be VERY slow.
 $count = (Get-EventLog Security -Newest 1000 | Where-Object {$_.EventID -eq 4625 -and $_.Message -match "Logon Type:\s+8" -and `
             $_.TimeGenerated.CompareTo($now) -gt 0} | Measure-Object).count;
 if ($count -gt 50) #threshold
 {
    $ips = @();
    $ips1 = dir "C:\inetpub\logs\LogFiles\FPTSVC2" | Sort-Object -Property LastWriteTime -Descending `
      | select -First 1 | gc | select -Last 200 | where {$_ -match "An\+error\+occured\+during\+the\+authentication\+process."} `
       | Select-String -Pattern "(\d+\.\d+\.\d+\.\d+)" | select -ExpandProperty Matches | select -ExpandProperty value | Group-Object `
       | where {$_.Count -ge 10} | select -ExpandProperty Name;
 
    $ips2 = dir "C:\inetpub\logs\LogFiles\FTPSVC3" | Sort-Object -Property LastWriteTime -Descending `
      | select -First 1 | gc | select -Last 200 | where {$_ -match "An\+error\+occured\+during\+the\+authentication\+process."} `
       | Select-String -Pattern "(\d+\.\d+\.\d+\.\d+)" | select -ExpandProperty Matches | select -ExpandProperty value | Group-Object `
       | where {$_.Count -ge 10} | select -ExpandProperty Name;
    $ips += $ips1; $ips += $ips2; $ips = $ips | where {$_ -ne "10.0.0.1"} | Sort-Object | Get-Unique;
   
    foreach ($ip in $ips) {
      if (-not ($blacklist -contains $ip)) {
       "Found attacking IP on FTP: " + $ip;
       $blacklist = $blacklist + $ip;
      }
    }
 }
 
 
 
 #Firewall change

<# $current = (netsh advfirewall firewall show rule name="MY BLACKLIST" | where {$_ -match "RemoteIP"}).replace("RemoteIP:", "").replace(" ","").replace("/255.255.255.255",""); #inside $current there is no \r or \n need remove. foreach ($ip in $blacklist) { if (-not ($current -match $ip) -and -not ($ip -like "10.0.0.*")) {"Adding this IP into firewall blocklist: " + $ip; $c= 'netsh advfirewall firewall set rule name="MY BLACKLIST" new RemoteIP="{0},{1}"' -f $ip, $current; Invoke-Expression $c; } } #>
 
 foreach ($ip in $blacklist) {

   $fw=New-object –comObject HNetCfg.FwPolicy2; # http://blogs.technet.com/b/jamesone/archive/2009/02/18/how-to-manage-the-windows-firewall-settings-with-powershell.aspx
   $myrule = $fw.Rules | where {$_.Name -eq "MY BLACKLIST"} | select -First 1; # Potential bug here?

   if (-not ($myrule.RemoteAddresses -match $ip) -and -not ($ip -like "10.0.0.*"))
     {"Adding this IP into firewall blocklist: " + $ip; 
       $myrule.RemoteAddresses+=(","+$ip);
     }
 }
 
 Wait-Event -Timeout 30 #pause 30 secs
 
 } # end of top while loop.

 


Further points:

1, I suppose the server is listening on port 3389 on server IP: 192.168.100.101 and 192.168.100.102, you need to replace that with your real IP.

2, I suppose you are Remote Desktop to this server from a workstation with IP: 10.0.0.1. Please replace as well.

3, The threshold for 3389 attack is 20, you don’t want to block yourself just because you typed your password wrong 3 times, you can change this threshold by your own reasoning.

4, FTP is checking the log for attack only to the last 5 mins, you can change that as well.

5, I suppose the server is serving FTP on both IP address and their LOG path are C:\inetpub\logs\LogFiles\FPTSVC2 and C:\inetpub\logs\LogFiles\FPTSVC3. Change accordingly.

6, FTP checking code is only asking for the last 200 lines of log, and the threshold is 10, change as you wish.

7, the code runs in a loop, you can set the loop time at the last line.


To run this code, copy and paste to your editor, finish all the editing, get it to your server, and open an CMD window, then type powershell.exe –file your_powershell_file_name.ps1, it will start running, you can Ctrl-C to break it.

This is what you see when it’s running:

image

This is when it detected attack and adding the firewall rule:

image


Regarding the design of the code:

1, There are many ways you can detect the attack, but to add an IP into a block rule is no small thing, you need to think hard before doing it, reason for that may include: You don’t want block yourself; and not blocking your customer/user, i.e. the good guy.

2, Thus for each service/port, I double check. For 3389, first it needs to show in netstat.exe, then the Event log; for FTP, first check the Event log, then the FTP log files.

3, At three places I need to make sure I’m not adding myself into the block rule. –ne with single IP, –like with subnet.

 

Now the final bit:

1, The code will stop working after a while (depends on how busy you are attacked, could be weeks, months, or days?!) It will throw Red error message in CMD, don’t Panic, it does no harm, but it also no longer blocking new attack. THE REASON is not confirmed with MS people: the COM object to manage firewall, you can only give it a list of IP addresses to the length of around 32KB I think, once it reaches the limit, you get the error message.

2, This is in fact my second solution to use the COM object, the first solution is still in the comment block for your reference, which is using netsh, that fails because being run from CMD, you can only throw it a list of IP to 8KB.

3, I haven’t worked the workaround yet, some ideas include: wrap that RemoteAddresses setting line with error checking and once it reaches the limit, use the newly detected IP to be the list, not appending to it. This basically reset your block rule to ground zero and lose the previous bad IPs. This does no harm as it sounds, because given a certain period has passed, any these bad IPs still not repent and continue the attack to you, it only got 30 seconds or 20 guesses of your password before you block it again. And there is the benefit that the bad IP may turn back to the good hands again, and you are not blocking a potential customer or your CEO’s home pc because once upon a time, it’s a zombie. Thus the ZEN of blocking: never block any IP for too long.

4, But if you insist to block the ugly forever, my other ideas include: You call MS support, ask them how can we set an arbitrary length of IP addresses in a rule; at least from my experiences at the Forum, they don’t know and they don’t care, because they think the dynamic blocking should be done by some expensive hardware. Or, from programming perspective, you can create a new rule once the old is full, then you’ll have MY BLACKLIST1, MY  BLACKLIST2, MY BLACKLIST3, … etc. Once in a while you can compile them together and start a business to sell your blacklist on the market!

Enjoy the code!

p.s. (PowerShell is REALLY REALLY GREAT!)

Posted by dong | 11 comment(s)