Link to home
Start Free TrialLog in
Avatar of ctagle
ctagleFlag for United States of America

asked on

PowerShell Script To Automatically Add IP Addresses to netsh ipsec filterlist in SBS 2003

Hello,

I've been tasked with getting rid of some rather annoying and frequent brute force attempts on our server across RDP.  The way I'm trying to do this is as follows.    

I'm attempting to write a PowerShell script that will go 2 days back in the security logs, grab all 529's, look at the IP Address, determine if it has over 10 failed login attempts, and then if so add it to a filterlist.  The problem I'm having is that when it pulls the event logs it doesn't display the Source Network Address, or any address of any kind.  It only displays 6 fields, Index, Time, Entrytype, Source, InstanceID, and Message.  It would seem that the IP address would be contained within the message field but its truncated in the results and even if it is displayed, I'm not sure how to tell it to select that particular part since its inside of the Message field as text.  As I'm sure you can tell I'm new at scripting.  I'll paste the code that I have so far for pulling the event logs, I'll leave out the script for pulling the 10 failed login attempts for the moment since I can't even get it to display the IP address.  I'll paste an excerpt from the results I get when running the script.

Another question I'd like to see if any one knows the answer to is how to add multiple IP addresses in a single netsh command to a filterlist, I haven't been able to figure that out.

Also if someone has a suggestion for a better method of doing this I'm open to it, but I haven't really been able to find any other way of doing this in as close to real time as possible.  In server 2008 and above and SBS 2003 Premium it wouldn't be a problem since it has the more advanced firewall, plus in 2008 and newer there is also a much better task scheduler, but alas I have neither.

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

$l = Get-EventLog Security -InstanceId 529  -After $DT | Where-Object {$_.EventID -eq 529}

Index Time          EntryType   Source                 InstanceID Message                                                                                                                    
   ----- ----          ---------   ------                 ---------- -------                                                                                                                    
 5429759 Nov 12 21:04  FailureA... Security                      529 Logon Failure:...                                                                                                          
 5429758 Nov 12 21:04  FailureA... Security                      529 Logon Failure:...                                                                                                          
 5429757 Nov 12 21:04  FailureA... Security                      529 Logon Failure:...                                                                                                          
 5429756 Nov 12 21:04  FailureA... Security                      529 Logon Failure:...



Thank you in advance for any help you can provide.

**UPDATE**:  I've realized that I can display the message field by piping it through format-list message.  Is there any way that I can select the ip from the message it pulls?

**UPDATE**:  Ok I've hit a road block that I can't seem to pass, I've managed to figure out how to display only the message, and I can use select-string to search for "source network address" but since its not an object, just a string of text, then i don't know how to tell it to look for the ip addresses.

**UPDATE** woot! I've finally figure out how to display just the source network address, now the issue is that I need to get rid of the part that says "Source Network Address:" and have it only select the ip address itself so it can be stored into a variable and input into the netsh command.  Below is the code that I used to achieve this, i'm sure i'm just missing a command but I've been staring at this thing all day so I'll have to pick it up tomorrow.  You'll notice that i've removed the the date and time, this is merely because -newest provides quicker results and is easier for testing.

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 20

$g = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value}

echo $g #THE ECHO COMMAND IS TO DISPLAY THE DATA STORED IN THE VARIABLE, AND IS ONLY FOR TESTING IN THIS SCRIPT
ASKER CERTIFIED SOLUTION
Avatar of asavener
asavener
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ctagle

ASKER

Thank you very much for the reply, I think you've put me on the right track, but the code seems to be replacing whats after the "Source Network Address:" string with "".  I'll paste the output and code below, perhaps I'm doing something wrong.

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 5

$g = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object $_.Replace {"Source Network Address:",""}

echo $g

OUTPUT:

Source Network Address:

Source Network Address:

Source Network Address:

Source Network Address:

Source Network Address:
Hmm....

http://technet.microsoft.com/en-us/library/ee692804.aspx

Try using parentheses on the replace command instead of brackets.
Avatar of ctagle

ASKER

Thanks again, I have managed to exclude the Source Network Address: by using the code below and the suggestions you provided.  Now I just have a couple of more hurdles, first is getting the data inputted into the netsh command, as I suspected when it attempts to input the data into it i get an error message because it can only handle one ip address per command, so essentially it would seem that within the script itself i would need a way to 'enter down' with each new ip address.  

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 5

$g = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")}

OUTPUT:

x.x.x.x      x.x.x.x      x.x.x.x       x.x.x.x       x.x.x.x

Here is the net sh command I am using:

netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr= $g dstaddr=any

It looks at the $g variable for the value for the srcaddrr, the problem is that when it does, every ip address it pulls it input into the command, which isn't supported it would seem with netsh, so basically i need a way for it to input one ip address, commit it to the filterlist, enter another, commit it to the filter list, etc....until it reaches the end of the list.


I'd also like to include something in the script that would look through for our office's IP address (in case an employee mis typed a password enough times in the specified time period to trigger the block) and skip it if it finds it, and for it to exclude any local ip's it might find, i know there are a couple of times when its returned some internal ip's, it would be rare for them have a bad login 10 times in the time frame i'll specify for the script but its possible.

There's some other things i'll be doing with this particular script, such as having it email reports of offending ip addresses, but that can be saved for another question.
Try this line:

Foreach ($IP in $g) {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr= $IP dstaddr=any)
Or better:

Foreach ($IP in $g) {if ($IP -notlike "*192.168.1.*") {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr= $IP dstaddr=any}}
Avatar of ctagle

ASKER

Sweet!  that line you put pointed me in the right direction and its now inputting the ip addresses correctly.  Now the last hurdle (i think) with this script will be telling to input only ip addresses that occur more than 10 or 15 times, and to input them only once into the filterlist  the original idea i had won't work.  do you have any suggestions as to how to achieve this, below is the code i have so far

CODE:

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

$l = Get-EventLog Security -InstanceId 529 -after $DT

$a = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | Foreach $_ {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$_ dstaddr=any}
http://www.mikepfeiffer.net/2010/04/working-with-duplicate-array-values-in-powershell/

This article has a function that will allow you to derive a count for how many duplicates exist for each value.
Avatar of ctagle

ASKER

i looked over the article but it didn't really help me other than the use of the select-object -unique command.  I think what I want to achieve can be achieved with where-object {$_.count} command but i can't seem to get it to work everytime that command appears anywhere in the script it causes the script to output nothing, i'm sure i'm doing something wrong, i'll paste the code i'm using below.

CODE:

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

$l = Get-EventLog Security -InstanceId 529 -after $dt

$a = $l  | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | where-object {$_.count -gt 10} | select-object -unique | Foreach $_ {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$_ dstaddr=any}

echo $a

#the output is literally this and nothing else.

OUTPUT:

PS C:\Documents and Settings\administrator.HEADQUARTERS\Desktop> C:\Documents and Settings\administrator.HEADQUARTERS\Desktop\rdp_ipblock_beta.ps1


______________________________________________________________________________________________________________________________________________________________________________________________
Try changing

Foreach $_ {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$_ dstaddr=any}

to

Foreach-object {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$_ dstaddr=any}
Avatar of ctagle

ASKER

it does that even when that line is commented out though
Avatar of ctagle

ASKER

honestly i'm thinking that i need to group the results first and then count them, I've tried a couple of thigns, i'll try to post them below.

CODE (WITH GROUP-OBJECT WHERE-OBJECT)

$l = Get-EventLog Security -InstanceId 529 -newest 200

$a = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group-object $_  | where-object {$_ -gt 10} | select-object -unique

OUTPUT: The above gives me this error message, which i think is because when i comment out the where object command it just returns "Microsoft.Powershell.Commands.Groupinfo" from the group-object command, it doesn't actually group anything, so when it goes to count, it doesn't have a valid value for doing so, just a guess though:

Bad argument to operator '-gt': Could not compare "Microsoft.PowerShell.Commands.GroupInfo" to "10". Error: "Cannot convert the "10" value of type "System.Int32" to type "Microsoft.PowerShell.C
ommands.GroupInfo".".
At C:\Documents and Settings\administrator.HEADQUARTERS\Desktop\rdp_ipblock_beta.ps1:5 char:271
+ $a = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.re
place("Source Network Address:", "")} | group-object $_  | where-object {$_ -gt <<<<  10} | select-object -unique #| Foreach $_{netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$_ dst
addr=any}
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : BadOperatorArgument

______________________________________________________

I also tried it with format-table -groupby $_ but when i do this i get two hundred error messages (one for each result) and again i think its because when its trying to look at the results to see if there is more than 10 it can't because the format-table -groupby is returning only "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData". Below is the code and one of the error messages I get

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 200

$a = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | format-table -groupby $_  | where-object {$_ -gt 10} | select-object -unique

ERROR:

Bad argument to operator '-gt': Could not compare "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData" to "10". Error: "Cannot convert the "10" value of type "System.Int32" to type "
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData".".
At C:\Documents and Settings\administrator.HEADQUARTERS\Desktop\rdp_ipblock_beta.ps1:5 char:280
+ $a = $l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*" -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.re
place("Source Network Address:", "")} | format-table -groupby $_  | where-object {$_ -gt <<<<  10} | select-object -unique #| Foreach $_{netsh ipsec static add filter filterlist=RDP_BLOCK srcad
dr=$_ dstaddr=any}
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : BadOperatorArgument
Did you try the function from that page I linked?
Avatar of ctagle

ASKER

Ok I actually managed to figure almost everything out, I've got it to everything i need but there is one final detail that I need to figure out, when it outputs the ip's in the end it has a Name: before each ip address, and i need to get rid of it, is there any way to tell format-list to not include the titles or headers in the output, here is the code so far:

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 2000

$l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*"  -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group -property $_ | where-object {$_.count -gt 10} | select-object name | format-list

And here is the output:

Name :     x.x.x.x

Name :     x.x.x.x

Name :     x.x.x.x

Name :     x.x.x.x

Name :     x.x.x.x

Name :     x.x.x.x

All that needs to be done is get rid of that pesky "Name:"  i'm sure its simple, in fact i've seen someone do it somewhere but it was in one of many many pages and articles i've gone through and i can't remember how to do it.
Avatar of ctagle

ASKER

ok i managed to get rid of the headers using the following code, but now when it does the netsh command, the script tries to pu 'microsoft.powershell.commands.interanl.format.formatentrydata' into the srcaddr=field instead of the ip address. below is the code and the output, thanks once again for helping me out with this.

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 2000

$l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*"  -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group -property $_ | where-object {$_.count -gt 10} | select-object name | format-table -wrap -hidetableheaders | Foreach {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr= $_ dstaddr=any}


OUTPUT:

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatStartData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.GroupStartData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.GroupEndData'

ERR IPSec[01009] : DNS lookup failed for the given dns name 'Microsoft.PowerShell.Commands.Internal.Format.FormatEndData'
Try replacing the last command (Foreach {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr= $_ dstaddr=any}) with Foreach {write-host $_}

This will tell you what the actual contents of the variable are.
Actually, I think the command should be Foreach-object.
Avatar of ctagle

ASKER

it returns this:

Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.GroupEndData
Microsoft.PowerShell.Commands.Internal.Format.FormatEndData

So it would seem thats what's actually being stored within the variable, which if thats the case, is there any way to use the data displayed by the command in the output pane, like converting it into a different form?
Avatar of ctagle

ASKER

ok i'm doing some reading online and some articles are saying that you can't use the data after its been formatted.   So my question then becomes, how would i clean up the output of the following script to show only the ip addresses and still be able to work with it and output the data to commands.

CODE:

$l = Get-EventLog Security -InstanceId 529 -newest 2000

$l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*"  -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group-object -property $_ | where-object {$_.count -gt 10} | select-object -property name

OUTPUT:
This is the cleanest I've been able to get it thus far, you'll notice that there is the "Name" Header a little divider line "------------" an elliptical on each line "..." and there is huge amounts of space characters (i don't know if i'm using the right term, but i mean the character generated when you hit the space bar) this is why there is an extra line break in between each ip address.  I've gotta clean up all of that::

Name                                                                                                                                                                                            
----                                                                                                                                                                                            
    x.x.x.x...                                                                                                                                                                              
    x.x.x.x...                                                                                                                                                                            
    x.x.x.x...                                                                                                                                                                            
    x.x.x.x...                                                                                                                                                                              
    x.x.x.x...
I know you can use powershell to write output to a text file.

Perhaps you can write it to a file, and then import it back into an array.
Avatar of ctagle

ASKER

I finally figured it out!  Here is the finished product, I'm not sure if its the most efficient but it gets the job done quite nicely.  Thank you very much for all your help, you pointed me in the right direction and aided me in debugging in pretty much every answer, you're a testament to this website.

#This script will get the ip addresses from 24 hours ago that have more than 10 bad login attempts (529's) and add them to an ipsec filter list.

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

$l = Get-EventLog Security -InstanceId 529 -after $DT

$l | select-string -inputobject {$_.message} -pattern "Source Network Address:(.)*\.*\.*\.*"  -allmatches | foreach-object {$_.Matches} | foreach-object {$_.Value} | foreach-object {$_.replace("Source Network Address:", "")} | group-object -property $_ | where-object {$_.count -gt 10} | select-object -property name | format-list | out-file c:\rdpblock.txt 

get-content -path c:\rdpblock.txt | foreach-object {$_.replace("Name :", "")} | out-file c:\rdpblockcleaned.txt 

get-content -path c:\rdpblockcleaned.txt | select-object -unique | out-file c:\rdpblocknospaces.txt

$c = get-content -path c:\rdpblocknospaces.txt | select-object -skip 1

$c | foreach-object {$_.replace("     ", "")} | foreach-object {netsh ipsec static add filter filterlist=RDP_BLOCK srcaddr=$($_) dstaddr=any}

Open in new window