Block IP if logon failed X number of times

If you have a public facing server relying only on windows firewall protection, you probably will see windows security log full of attempts to login using different username and password.

For IIS 6.0, you can write a script to block an IP from connection if it failed 10 or more times within the last 24 hours.

See the full discussion here.

I have refactored the script to be more readable and easy to troubleshoot.

   1: # check only last 24 hours

   2: $cutoffDateTime = [DateTime]::Now.AddDays(-1) 


   4: # select Ip addresses that has audit failure 

   5: $l = Get-EventLog -LogName 'Security' -InstanceId 4625 -After $cutoffDateTime | Select-Object @{n='IpAddress';e={$_.ReplacementStrings[-2]} } 


   7: # get ip adresses, that have more than 10 wrong logins

   8: $suspectIPs = $l | group-object -property IpAddress  | where {$_.Count -gt 10} | Select -property Name 


  10: write-host "Suspicious IPs:"

  11: $suspectIPs | foreach {write-host $_}


  13: # get firewall object

  14: $fw = New-Object -ComObject hnetcfg.fwpolicy2 


  16: # get firewall rule named 'BlockAuditFailure' (must be created manually)

  17: $ar = $fw.rules | where {$ -eq 'BlockAuditFailure'} 


  19: #split the existing IPs into an array so we can easily search for existing IPs

  20: $blockedIPs = $ar.RemoteAddresses -split(',') 


  22: write-host "Current Blocked IPs:"

  23: $blockedIPs | foreach {write-host $_}


  25: # get ip addresses that are not already in firewal rule. 

  26: # Include the subnet mask which is automatically added to the firewall remote IP declaration.

  27: $IPsToBlock = $suspectIPs | where {$_.Name.Length -gt 1 -and  !($blockedIPs -contains $_.Name + '/') } 


  29: write-host "IPs to Block:"

  30: $IPsToBlock | foreach {write-host $_}


  32: # add IPs to firewall rule

  33: $IPsToBlock | %{$ar.remoteaddresses += ',' + $_.Name} 


  35: #split the existing IPs into an array so we can easily search for existing IPs

  36: $upToDateBlockedIPs = $ar.RemoteAddresses -split(',') 


  38: write-host "Up-To-Date Blocked IPs:"

  39: $upToDateBlockedIPs | foreach {write-host $_}

For IIS 7.0, you can install Dynamic IP Restrictions Extension, but it deals with DOS attacks primarily.

The other option is to have a smart hardware firewall which already does this for you.



Scheduled Task to do iisreset.exe

We need a scheduled task to run a console app.  This console app will perform some tests and perform an iisreset if necessary.

var proc = new Process
StartInfo = new ProcessStartInfo
FileName = @"c:\windows\system32\iisreset.exe",
Arguments = "",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
Verb = "runas"

StringBuilder sb = new StringBuilder();
while (!proc.StandardOutput.EndOfStream)
logger.Info("iisreset ouput:[" + sb.ToString() + "]", LogCategory);

The scheduled task needs to run under an identity with local administrator right.  I also mark the scheduled task with “Run with highest privileges”.

How to post a string to WEB API

How to post a string to WEB API? 

It is tricky because the client may post the string and specify content-type to some value.

Below is the cleanest way I can come up with:

public HttpResponseMessage ActionA()
string content = this.ControllerContext.Request.Content.ReadAsStringAsync().Result;

Here is another way:

public RespMessage Post(HttpRequestMessage request)
string content = this.ControllerContext.Request.Content.ReadAsStringAsync().Result;

Yet another way:

public RespMessage ActionA([FromBody]string value)
// read the posted string from the request.Content
string content = this.ControllerContext.Request.Content.ReadAsStringAsync().Result;

The official way is a bit tricky on the client.

public RespMessage ActionA([FromBody]string value)
// posted string is stored in “value”

If you want to have “value” populated with the posted string, the client needs to specify

Content-Type: application/x-www-form-urlencoded

And the request body needs to have a “=” prefix.