A developer in one the departments I support requested 50 test mailboxes. Below is the PowerShell script I ran to quickly take care of the task.
# AD Setting for Multi Domain Forest
Set-ADServerSettings -ViewEntireForest $true
$password = ConvertTo-SecureString -String 'CreativeThoughtGoesHere2011$' -AsPlainText -Force
for ($i=1; $i -le 50; $i++)
{
$un = "AppTestAccount" + $i
$upn = $un + "@mydomain.edu"
New-Mailbox -UserPrincipalName $upn -Alias $un -Database "EX-MDB-001" -Name $un -OrganizationalUnit "OU=RESOURCEACCOUNTS,DC=MYDOMAIN,DC=EDU" -Password $password -DisplayName $un -ResetPasswordOnNextLogon $false
}
Friday, April 29, 2011
Assign Recursive Rights To Public Folder
Day 90 of new job and finally able to take a break to blog some code. Here is a little diddy that I had to use to grant recursive rights on a Exchange public folder to a distribution group.
# AD Setting for Multi Domain Forest
Set-ADServerSettings -ViewEntireForest $true
$pfs = get-publicfolder -identity "\DeptPublicFolder" -recurse
foreach ($pf in $pfs)
{
# Grant Distribution Group Owner Access to Each Public Folder. Use Groups Exchange Alias
Add-PublicFolderClientPermission -Identity $pf.identity -AccessRights owner -User "DistGroupAlias"
}
# AD Setting for Multi Domain Forest
Set-ADServerSettings -ViewEntireForest $true
$pfs = get-publicfolder -identity "\DeptPublicFolder" -recurse
foreach ($pf in $pfs)
{
# Grant Distribution Group Owner Access to Each Public Folder. Use Groups Exchange Alias
Add-PublicFolderClientPermission -Identity $pf.identity -AccessRights owner -User "DistGroupAlias"
}
Tuesday, January 11, 2011
VBScript: OU Users Password Never Expires
Due to a user migration project, my unit needed a quick way of removing "User must change password at next logon" setting caused by the migration tool for all users in a specific OU. Found that this setting is kept in AD under the userAccountControl property of the user object. Below is link that explains the values. One issue that needed a work around was disabled accounts. If you set the value for "Password Never Expires" then a disabled account is re-enabled, which wouldn't be a good thing for accounts that needed to say disabled. Solved this by a quick if statement that looks for only enabled accounts or accounts with the "User must change..." setting.
'***********************************************************************************
'Script Name: OU_Users_Password_Never_Expires.vbs
'Author: Dean Bunn
'Created: 01/10/11
'Description: Set All Enabled User Accounts in an OU to Password Never Expires
'***********************************************************************************
dim objOU, objUser, intPwdChg
intPwdChg = 66048
objOU="OU=staff,DC=mynetwork,DC=com"
set objOU = GetObject("LDAP://" & objOU)
for each objUser in objOU
if objUser.Class="user" Then
set objUser = GetObject("LDAP://" & objUser.distinguishedName)
if objUser.userAccountControl = 512 OR objUser.userAccountControl = 544 then
objUser.Put "userAccountControl", intPwdChg
objUser.setInfo
end if
end if
next
wscript.echo "All Done"
Active Directory userAccountControl Values
http://rajnishbhatia19.blogspot.com/2008/11/active-directory-useraccountcontrol.html
'***********************************************************************************
'Script Name: OU_Users_Password_Never_Expires.vbs
'Author: Dean Bunn
'Created: 01/10/11
'Description: Set All Enabled User Accounts in an OU to Password Never Expires
'***********************************************************************************
dim objOU, objUser, intPwdChg
intPwdChg = 66048
objOU="OU=staff,DC=mynetwork,DC=com"
set objOU = GetObject("LDAP://" & objOU)
for each objUser in objOU
if objUser.Class="user" Then
set objUser = GetObject("LDAP://" & objUser.distinguishedName)
if objUser.userAccountControl = 512 OR objUser.userAccountControl = 544 then
objUser.Put "userAccountControl", intPwdChg
objUser.setInfo
end if
end if
next
wscript.echo "All Done"
Wednesday, December 29, 2010
VBScript: Hyper-V VM Status Script
I did a little research on how to start and shutdown a virtual machine via WMI. Below are two links to the classes that control these Hyper-V functions. Using these classes, came up with a script that is basically an interface for starting, stopping, or viewing the status of Hyper-V virtual machines. This should allow someone more control of our Windows Core Hyper-V servers when they lose network connection or they have to shut down all the servers.
Msvm_ComputerSystem Class
http://msdn.microsoft.com/en-us/library/cc136822%28v=VS.85%29.aspx
Msvm_ShutdownComponent Class
http://msdn.microsoft.com/en-us/library/cc136893%28VS.85%29.aspx
'***********************************************************************************************************
'Script Name: Hyper-V_VM_Status.vbs
'Author: Dean Bunn
'Last Edited: 12/29/10
'Description: Interface for Starting, Stopping, or Viewing Status of Hyper-V Virtual Machines
'***********************************************************************************************************
on error resume next
dim strOption, strHVServer, strVMState, strStatus
''Variable for Hyper-V Server Name..."." is for Local System
strHVServer = "."
''Connect to Hyper-V Server
set WMIService = GetObject("winmgmts:\\" & strHVServer & "\root\virtualization")
''Error Checking on the Connection to Hyper-V Server
if Err.Number <> 0 then
msgbox "Problem Accessing the Hyper-V Server. The Script will Quit Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Do Loop to Ease of Use When Starting or Stopping Numerous Systems
do
''Prompt User: Start, Shutdown, or Status Only
strOption = inputbox("Type an Option:" & VbCrLf & VbCrLf & """Start"" to Start a Virtual Machine" _
& VbCrLf & VbCrLf & """Stop"" to Shutdown a Virtual Machine" & VbCrLf & VbCrLf _
& """Status"" to View Status Only" & VbCrLf & VbCrLf _
& "Press Cancel to Quit the Script" & VbCrLf & VbCrLf, "Hyper-V VM Status")
''If User Clicks Cancel or Closes Window Script Quits
if strOption = vbEmpty then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Query Hyper-V Server for All Virtual Systems, Which Includes Hyper-V Server)
set VMList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem")
''Start Status of Virtual Systems Message
strStatus = "Current Virtual Machine Status:" & VbCrLf & VbCrLf
''Loop Through All VMs and Add System Name and Current State to Status
''Excluding Hyper-V Server by Checking for Non Matching ElementName and Name
for each VM in VMList
if VM.ElementName <> VM.Name then
select case VM.EnabledState
case 0
strVMState = "unknown"
case 2
strVMState = "running"
case 3
strVMState = "powered off"
case 32768
strVMState = "paused"
case 32770
strVMState = "starting"
case 32773
strVMState = "saving"
case 32774
strVMState = "stopping"
case 32776
strVMState = "pausing"
case 32777
strVMState = "resuming"
case else
strVMState = "don't know"
end select
''Check Length of Virtual System Name for Status Report Formatting
if Len(VM.ElementName) <= 6 then
strStatus = strStatus & VM.ElementName & vbtab & vbtab & vbtab & strVMState & VbCrLf
else
strStatus = strStatus & VM.ElementName & vbtab & vbtab & strVMState & VbCrLf
end if
end if
next
''Remove Extra Spaces and Lowercase Option Choice
strOption = Lcase(Trim(strOption))
''If Option is Start or Stop then Prompt for System Name
if strOption = "start" OR strOption = "stop" then
strStatus = strStatus & VbCrLf & VbCrLf & "Type in the Name of the VM to " & Ucase(strOption)
''Inputbox for VM System Name
strVMSystem = inputbox(strStatus, "Hyper-V VM Status")
''If User Clicks Cancel or Close Script Quits
if strVMSystem = vbEmpty then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Remove Extra Spaces and Lowercase VM System Name
strVMSystem = Lcase(Trim(strVMSystem))
''Start Option Uses the RequestStateChange Method with 2 (Start Up)
if strOption = "start" then
set VMStartList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & strVMSystem & "'")
VMStartList.ItemIndex(0).RequestStateChange(2)
''Stop Option Uses InitiateShutdown Method Which is a Clean Shutdown
elseif strOption = "stop" then
set VMStopList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & strVMSystem & "'")
vmGUID = VMStopList.ItemIndex(0).Name
set VMShutdown = WMIService.ExecQuery("SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" & vmGUID & "'")
varResult = VMShutdown.ItemIndex(0).InitiateShutdown(True,"Shutdown")
end if
''Informs User of Command Status and on Error Quits Script
if Err.Number = 0 then
msgbox UCase(strOption) & " Command Successfully Sent" , vbOkOnly , "Hyper-V VM Status"
else
msgbox "Problem Sending Command for " & strVMSystem & ". Please Try Script Again" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Status Option Reports Only Status of Systems
elseif strOption = "status" then
''Variable to Check If User Clicks Cancel or Closes Window
varStat = msgbox(strStatus, vbOKCancel , "Hyper-V VM Status")
''If Cancel or Close Quits Script
if varStat = vbCancel then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Error Checking for Non Option Typed In
else
msgbox "You Didn't Type Start, Stop, or Status. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
loop
Msvm_ComputerSystem Class
http://msdn.microsoft.com/en-us/library/cc136822%28v=VS.85%29.aspx
Msvm_ShutdownComponent Class
http://msdn.microsoft.com/en-us/library/cc136893%28VS.85%29.aspx
'***********************************************************************************************************
'Script Name: Hyper-V_VM_Status.vbs
'Author: Dean Bunn
'Last Edited: 12/29/10
'Description: Interface for Starting, Stopping, or Viewing Status of Hyper-V Virtual Machines
'***********************************************************************************************************
on error resume next
dim strOption, strHVServer, strVMState, strStatus
''Variable for Hyper-V Server Name..."." is for Local System
strHVServer = "."
''Connect to Hyper-V Server
set WMIService = GetObject("winmgmts:\\" & strHVServer & "\root\virtualization")
''Error Checking on the Connection to Hyper-V Server
if Err.Number <> 0 then
msgbox "Problem Accessing the Hyper-V Server. The Script will Quit Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Do Loop to Ease of Use When Starting or Stopping Numerous Systems
do
''Prompt User: Start, Shutdown, or Status Only
strOption = inputbox("Type an Option:" & VbCrLf & VbCrLf & """Start"" to Start a Virtual Machine" _
& VbCrLf & VbCrLf & """Stop"" to Shutdown a Virtual Machine" & VbCrLf & VbCrLf _
& """Status"" to View Status Only" & VbCrLf & VbCrLf _
& "Press Cancel to Quit the Script" & VbCrLf & VbCrLf, "Hyper-V VM Status")
''If User Clicks Cancel or Closes Window Script Quits
if strOption = vbEmpty then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Query Hyper-V Server for All Virtual Systems, Which Includes Hyper-V Server)
set VMList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem")
''Start Status of Virtual Systems Message
strStatus = "Current Virtual Machine Status:" & VbCrLf & VbCrLf
''Loop Through All VMs and Add System Name and Current State to Status
''Excluding Hyper-V Server by Checking for Non Matching ElementName and Name
for each VM in VMList
if VM.ElementName <> VM.Name then
select case VM.EnabledState
case 0
strVMState = "unknown"
case 2
strVMState = "running"
case 3
strVMState = "powered off"
case 32768
strVMState = "paused"
case 32770
strVMState = "starting"
case 32773
strVMState = "saving"
case 32774
strVMState = "stopping"
case 32776
strVMState = "pausing"
case 32777
strVMState = "resuming"
case else
strVMState = "don't know"
end select
''Check Length of Virtual System Name for Status Report Formatting
if Len(VM.ElementName) <= 6 then
strStatus = strStatus & VM.ElementName & vbtab & vbtab & vbtab & strVMState & VbCrLf
else
strStatus = strStatus & VM.ElementName & vbtab & vbtab & strVMState & VbCrLf
end if
end if
next
''Remove Extra Spaces and Lowercase Option Choice
strOption = Lcase(Trim(strOption))
''If Option is Start or Stop then Prompt for System Name
if strOption = "start" OR strOption = "stop" then
strStatus = strStatus & VbCrLf & VbCrLf & "Type in the Name of the VM to " & Ucase(strOption)
''Inputbox for VM System Name
strVMSystem = inputbox(strStatus, "Hyper-V VM Status")
''If User Clicks Cancel or Close Script Quits
if strVMSystem = vbEmpty then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Remove Extra Spaces and Lowercase VM System Name
strVMSystem = Lcase(Trim(strVMSystem))
''Start Option Uses the RequestStateChange Method with 2 (Start Up)
if strOption = "start" then
set VMStartList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & strVMSystem & "'")
VMStartList.ItemIndex(0).RequestStateChange(2)
''Stop Option Uses InitiateShutdown Method Which is a Clean Shutdown
elseif strOption = "stop" then
set VMStopList = WMIService.ExecQuery("SELECT * FROM Msvm_ComputerSystem WHERE ElementName='" & strVMSystem & "'")
vmGUID = VMStopList.ItemIndex(0).Name
set VMShutdown = WMIService.ExecQuery("SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='" & vmGUID & "'")
varResult = VMShutdown.ItemIndex(0).InitiateShutdown(True,"Shutdown")
end if
''Informs User of Command Status and on Error Quits Script
if Err.Number = 0 then
msgbox UCase(strOption) & " Command Successfully Sent" , vbOkOnly , "Hyper-V VM Status"
else
msgbox "Problem Sending Command for " & strVMSystem & ". Please Try Script Again" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Status Option Reports Only Status of Systems
elseif strOption = "status" then
''Variable to Check If User Clicks Cancel or Closes Window
varStat = msgbox(strStatus, vbOKCancel , "Hyper-V VM Status")
''If Cancel or Close Quits Script
if varStat = vbCancel then
msgbox "User Cancelled Script. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
''Error Checking for Non Option Typed In
else
msgbox "You Didn't Type Start, Stop, or Status. Quitting Now" , vbExclamation + vbOkOnly , "Hyper-V VM Status"
wscript.quit
end if
loop
Tuesday, December 21, 2010
VBScript: IPv4 Network Cards MAC Addresses
Since both ipconfig -all and getmac commands didn't make it easy for users to tell us their network card(s) MAC address, my unit needed to come up with a quick script that would report all the IPv4 network cards and their MAC addresses on a Windows system. This way a user can easily download the script from a website and then run it and send us the reporting text file. The script will save the results file in the same directory as the script and doesn't require admin rights to get the information.
'********************************************************************************
'Script Name: Network_Cards.vbs
'Last Edited: 12/21/10
'Description: Reports via text file all IPv4 network cards
'********************************************************************************
const for_appending = 8
'Creates the text File in the Current Directory
set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile = objFSO.OpenTextFile(".\Network_Cards.txt", for_appending, true)
'Query the local system for all IPv4 Network Cards with a MAC Address
set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
set colNetworkCards = objWMIService.ExecQuery _
("Select * from Win32_NetworkAdapterconfiguration Where MACAddress is Not Null")
for each netcard in colNetworkCards
objFile.WriteLine("Network Card: " & netcard.Description)
objFile.WriteLine("MAC Address: " & netCard.MACAddress)
objFile.WriteBlankLines(1)
next
objFile.Close
msgbox "Information was saved to the Network_Cards.txt file", vbOkOnly , "Network Cards"
'********************************************************************************
'Script Name: Network_Cards.vbs
'Last Edited: 12/21/10
'Description: Reports via text file all IPv4 network cards
'********************************************************************************
const for_appending = 8
'Creates the text File in the Current Directory
set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile = objFSO.OpenTextFile(".\Network_Cards.txt", for_appending, true)
'Query the local system for all IPv4 Network Cards with a MAC Address
set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
set colNetworkCards = objWMIService.ExecQuery _
("Select * from Win32_NetworkAdapterconfiguration Where MACAddress is Not Null")
for each netcard in colNetworkCards
objFile.WriteLine("Network Card: " & netcard.Description)
objFile.WriteLine("MAC Address: " & netCard.MACAddress)
objFile.WriteBlankLines(1)
next
objFile.Close
msgbox "Information was saved to the Network_Cards.txt file", vbOkOnly , "Network Cards"
Tuesday, September 28, 2010
PF Block Report (Perl Script) V2
I’ve been running PF Blocked Report script for well over a month on my firewalls and have made a few improvements. Version 1.0 of the script didn’t take into account legitimate blocked traffic (e.g. FIN ACK web server traffic). So I modified the tcpdump command to only look for TCP with only SYN flag set, ICMP, and UDP traffic. Additionally, I added general reporting for outbound hosts. The script is designed to be ran as a cron job, right before the log rotates. The size of your report will depend of what numbers you choose for the blocked limits. I’m using 100 as the inbound threshold to limit the size of my reports and taking into consideration even a quick NMAP scan will create well over a thousand entries it should catch port scans.
----------pfblocked.pl-------------------------------
#!/usr/bin/perl -w
# pfblocked.pl version 3.0
# last modified 09/28/10
use strict;
use File::Copy;
use Net::SMTP;
use Sys::Hostname;
my @log = ();
my @blockTemp = ();
my @obTemp = ();
my %blockedIPs;
my %obIPs;
my $pktLimit = 100;
my $obPktLimit = 50;
my ($year, $month, $day, $hour, $min) = (localtime)[5,4,3,2,1];
my $timestamp = sprintf ("%02d-%02d-%02d %02d:%02d", $month+1, $day, $year+1900, $hour, $min);
my $to = "userid\@mycompany.com";
#my $to2 = "deptaccount\@mycompany.com"; #additional recipient (if needed)
my $from = "pfblocked\@mycompany.com";
my $smtpserver = "smtp.mycompany.com";
my $hstname = hostname();
my $subject = "PF Block Report for $hstname";
#start of html report
my $message = "<p>PF Block Report for $hstname on $timestamp</p> \n" .
"Inbound Hosts<br /> \n" .
"<table border\=\"2\" width\=\"650\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked In</td>" .
"<td align\=\"center\">Passed In</td><td align\=\"center\">Blocked Out</td><td align\=\"center\">Passed Out</td></tr> \n";
#copy over current pf log file
#run tcpdump on log file and create output file
#then load array with output file data
copy("/var/log/pflog","pflogfile");
system("tcpdump -neltttr pflogfile 'tcp[13] & 2 !=0' or icmp or udp > tcpdumpfile.txt");
open(TDF,"tcpdumpfile.txt");
@log = <TDF>;
close TDF;
#parse each entry with regex looking for IPv4 blocked traffic
#load those source IPs into arrays
foreach my $line(@log)
{
my ($action, $srcIP);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)(.*)/)
{
($action, $srcIP) = ($4, $6);
if ($action eq "block in")
{
push(@blockTemp,$srcIP);
}
if ($action eq "block out")
{
push(@obTemp,$srcIP);
}
}
}
#load hash to get unique blocked in IPs
#IP address is the hash key and 1 is value for all
%blockedIPs = map { $_ => 1 } @blockTemp;
#find how many blocked entries per blocked in IP
#add that to the value of the hash
foreach my $key (keys(%blockedIPs))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@blockTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$blockedIPs{$key} = $pkt;
}
#look for blocked IPs that are over the bad entries limit
#then go back through logfile and check to see how many entries
#have those IPs as source with pass in or destination with pass out.
#then report formatting check based upon length of IP address
foreach my $key (sort (keys(%blockedIPs)))
{
my $blkIP = $key;
my $x = length ($blkIP);
my $pktNum = $blockedIPs{$key};
my $inCnt = 0;
my $outCnt = 0;
my $boCnt = 0;
if ($pktNum > $pktLimit)
{
foreach my $line(@log)
{
my ($action, $srcHost, $dstHost);
#regex for tcp and udp traffic
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)\.(\d+) > (\d+\.\d+\.\d+\.\d+)\.(\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $8);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
#regex for icmp
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+) > (\d+\.\d+\.\d+\.\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $7);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
}
$message = $message . "<tr><td>$key</td><td align\=\"center\">$blockedIPs{$key}</td>" .
"<td align\=\"center\">$inCnt</td><td align\=\"center\">$boCnt</td>" .
"<td align\=\"center\">$outCnt</td></tr> \n";
}
}
$message = $message . "</table>";
#load hash to get unique blocked out IPs
#IP address is the hash key and 1 is value for all
%obIPs = map { $_ => 1 } @obTemp;
$message = $message . "<br /><br /> \n" .
"Outbound Hosts \n".
"<table border\=\"2\" width\=\"300\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked Out</td></tr> \n";
#find how many entries per blocked out IP
#add that to the value of the hash
#if value is over outbound limit add to report data
foreach my $key (sort(keys(%obIPs)))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@obTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$obIPs{$key} = $pkt;
if($pkt > $obPktLimit)
{
$message = $message . "<tr><td>$key</td><td align\=\"center\">$obIPs{$key}</td></tr> \n";
}
}
$message = $message . "</table> \n";
#open SMTP connection and mail information
my $smtp = Net::SMTP->new($smtpserver);
$smtp->mail( $from );
$smtp->to( $to ); # for single recipient
#$smtp->to( $to,$to2 ); # for two or more recipients
$smtp->data();
$smtp->datasend("MIME-Version: 1.0\n");
$smtp->datasend("Content-Type: text/html; charset=us-ascii\n");
$smtp->datasend("From: " . $from . "\n");
$smtp->datasend("To: " . $to . "\n");
$smtp->datasend("Subject: " . $subject . "\n");
$smtp->datasend("\n");
$smtp->datasend("\n");
$smtp->datasend( $message );
$smtp->datasend("\n");
$smtp->dataend();
$smtp->quit();
#delete tcpdump output and copied over pflog files
unlink("tcpdumpfile.txt");
unlink("pflogfile");
-------------------------------------------------------------------
----------pfblocked.pl-------------------------------
#!/usr/bin/perl -w
# pfblocked.pl version 3.0
# last modified 09/28/10
use strict;
use File::Copy;
use Net::SMTP;
use Sys::Hostname;
my @log = ();
my @blockTemp = ();
my @obTemp = ();
my %blockedIPs;
my %obIPs;
my $pktLimit = 100;
my $obPktLimit = 50;
my ($year, $month, $day, $hour, $min) = (localtime)[5,4,3,2,1];
my $timestamp = sprintf ("%02d-%02d-%02d %02d:%02d", $month+1, $day, $year+1900, $hour, $min);
my $to = "userid\@mycompany.com";
#my $to2 = "deptaccount\@mycompany.com"; #additional recipient (if needed)
my $from = "pfblocked\@mycompany.com";
my $smtpserver = "smtp.mycompany.com";
my $hstname = hostname();
my $subject = "PF Block Report for $hstname";
#start of html report
my $message = "<p>PF Block Report for $hstname on $timestamp</p> \n" .
"Inbound Hosts<br /> \n" .
"<table border\=\"2\" width\=\"650\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked In</td>" .
"<td align\=\"center\">Passed In</td><td align\=\"center\">Blocked Out</td><td align\=\"center\">Passed Out</td></tr> \n";
#copy over current pf log file
#run tcpdump on log file and create output file
#then load array with output file data
copy("/var/log/pflog","pflogfile");
system("tcpdump -neltttr pflogfile 'tcp[13] & 2 !=0' or icmp or udp > tcpdumpfile.txt");
open(TDF,"tcpdumpfile.txt");
@log = <TDF>;
close TDF;
#parse each entry with regex looking for IPv4 blocked traffic
#load those source IPs into arrays
foreach my $line(@log)
{
my ($action, $srcIP);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)(.*)/)
{
($action, $srcIP) = ($4, $6);
if ($action eq "block in")
{
push(@blockTemp,$srcIP);
}
if ($action eq "block out")
{
push(@obTemp,$srcIP);
}
}
}
#load hash to get unique blocked in IPs
#IP address is the hash key and 1 is value for all
%blockedIPs = map { $_ => 1 } @blockTemp;
#find how many blocked entries per blocked in IP
#add that to the value of the hash
foreach my $key (keys(%blockedIPs))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@blockTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$blockedIPs{$key} = $pkt;
}
#look for blocked IPs that are over the bad entries limit
#then go back through logfile and check to see how many entries
#have those IPs as source with pass in or destination with pass out.
#then report formatting check based upon length of IP address
foreach my $key (sort (keys(%blockedIPs)))
{
my $blkIP = $key;
my $x = length ($blkIP);
my $pktNum = $blockedIPs{$key};
my $inCnt = 0;
my $outCnt = 0;
my $boCnt = 0;
if ($pktNum > $pktLimit)
{
foreach my $line(@log)
{
my ($action, $srcHost, $dstHost);
#regex for tcp and udp traffic
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)\.(\d+) > (\d+\.\d+\.\d+\.\d+)\.(\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $8);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
#regex for icmp
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+) > (\d+\.\d+\.\d+\.\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $7);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
}
$message = $message . "<tr><td>$key</td><td align\=\"center\">$blockedIPs{$key}</td>" .
"<td align\=\"center\">$inCnt</td><td align\=\"center\">$boCnt</td>" .
"<td align\=\"center\">$outCnt</td></tr> \n";
}
}
$message = $message . "</table>";
#load hash to get unique blocked out IPs
#IP address is the hash key and 1 is value for all
%obIPs = map { $_ => 1 } @obTemp;
$message = $message . "<br /><br /> \n" .
"Outbound Hosts \n".
"<table border\=\"2\" width\=\"300\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked Out</td></tr> \n";
#find how many entries per blocked out IP
#add that to the value of the hash
#if value is over outbound limit add to report data
foreach my $key (sort(keys(%obIPs)))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@obTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$obIPs{$key} = $pkt;
if($pkt > $obPktLimit)
{
$message = $message . "<tr><td>$key</td><td align\=\"center\">$obIPs{$key}</td></tr> \n";
}
}
$message = $message . "</table> \n";
#open SMTP connection and mail information
my $smtp = Net::SMTP->new($smtpserver);
$smtp->mail( $from );
$smtp->to( $to ); # for single recipient
#$smtp->to( $to,$to2 ); # for two or more recipients
$smtp->data();
$smtp->datasend("MIME-Version: 1.0\n");
$smtp->datasend("Content-Type: text/html; charset=us-ascii\n");
$smtp->datasend("From: " . $from . "\n");
$smtp->datasend("To: " . $to . "\n");
$smtp->datasend("Subject: " . $subject . "\n");
$smtp->datasend("\n");
$smtp->datasend("\n");
$smtp->datasend( $message );
$smtp->datasend("\n");
$smtp->dataend();
$smtp->quit();
#delete tcpdump output and copied over pflog files
unlink("tcpdumpfile.txt");
unlink("pflogfile");
-------------------------------------------------------------------
Sunday, August 15, 2010
PF Block Report (Perl Script)
I wanted to come up with an easy solution to help me review the PF logs on my OpenBSD firewalls. A quick search came up with Hatchet; however, it required SQLite modules and I really wanted to write something myself. So I borrowed a few regexes from Hatchet and wrote my own little Perl script which reports on IPs with over a certain number of blocked entries in the PF log.
How the script works:
This script is designed to be ran as a cron job, right before the log rotates.
----------pfblockreport.pl------------------------
#!/usr/bin/perl -w
use strict;
use File::Copy;
use Net::SMTP;
use Sys::Hostname;
my @blockTemp = ();
my @log = ();
my %blockedIPs;
my $pktLimit = 100;
my $crdt = localtime;
my $to = "dean\@company.com";
#additional recipient (if needed)
#my $to2 = "deptaccount\@company.com";
my $from = "pfblocked\@company.com";
my $hstname = hostname();
my $subject = "PF Block Report for $hstname";
#start of html report
my $message = "<p>PF Block Report for $hstname on $crdt</p> \n" .
"<table border\=\"2\" width\=\"650\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked In</td>" .
"<td align\=\"center\">Passed In</td><td align\=\"center\">Blocked Out</td><td align\=\"center\">Passed Out</td></tr> \n";
#copy over current pf log file
#run tcpdump on output file
#then load array with data
copy("/var/log/pflog","pflogfile");
system("tcpdump -neltttr pflogfile > tcpdumpfile.txt");
open(TDF,"tcpdumpfile.txt");
@log = <TDF>;
close TDF;
#parse each entry with regex looking for IPv4 blocked in only
#load those source IPs into array
foreach my $line(@log)
{
my ($action, $srcIP);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)(.*)/)
{
($action, $srcIP) = ($4, $6);
if ($action eq "block in")
{
push(@blockTemp,$srcIP);
}
}
}
#load hash to get unique blocked IPs
#IP address is the hash key and 1 is value for all
%blockedIPs = map { $_ => 1 } @blockTemp;
#find how many blocked entries per blocked IP
#add that to the value of the hash
foreach my $key (keys(%blockedIPs))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@blockTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$blockedIPs{$key} = $pkt;
}
#look for blocked IPs that are over the bad entries limit
#then go back through logfile and check to see how many entries
#have those IPs as source with pass in or destination with pass or out
foreach my $key (sort (keys(%blockedIPs)))
{
my $blkIP = $key;
my $x = length ($blkIP);
my $pktNum = $blockedIPs{$key};
my $inCnt = 0;
my $outCnt = 0;
my $boCnt = 0;
if ($pktNum > $pktLimit)
{
foreach my $line(@log)
{
my ($action, $srcHost, $dstHost);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)\.(\d+) > (\d+\.\d+\.\d+\.\d+)\.(\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $8);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+) > (\d+\.\d+\.\d+\.\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $7);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
}
$message = $message . "<tr><td>$key</td><td align\=\"center\">$blockedIPs{$key}</td>" .
"<td align\=\"center\">$inCnt</td><td align\=\"center\">$boCnt</td>" .
"<td align\=\"center\">$outCnt</td></tr> \n";
}
}
$message = $message . "</table>";
#open SMTP connection and mail information
my $smtp = Net::SMTP->new("smtp.company.com");
$smtp->mail( $from );
#for single recipient
$smtp->to( $to );
#for two or more recipients
#$smtp->to( $to,$to2 );
$smtp->data();
$smtp->datasend("MIME-Version: 1.0\n");
$smtp->datasend("Content-Type: text/html; charset=us-ascii\n");
$smtp->datasend("From: " . $from . "\n");
$smtp->datasend("To: " . $to . "\n");
$smtp->datasend("Subject: " . $subject . "\n");
$smtp->datasend("\n");
$smtp->datasend("\n");
$smtp->datasend( $message );
$smtp->datasend("\n");
$smtp->dataend();
$smtp->quit();
#delete tcpdump output and copied over pflog files
unlink("tcpdumpfile.txt");
unlink("pflogfile");
How the script works:
- Copies over the current pflog file to the working directory
- Runs tcpdump on the copied pflog file and creates a tcpdump text file
- Creates an array of the file and then goes through it looking for all blocked in traffic
- Creates a hash table of the unique blocked IPs and then determines how many blocked entries for each IP
- For those blocked IPs with over a certain number of blocked entries it goes back through the log array and finds how many passed in, blocked out, and passed out entries
- Reports that information via an HTML formatted email to an address of your choice
- Deletes the tcpdump output and copied over pflog files
This script is designed to be ran as a cron job, right before the log rotates.
----------pfblockreport.pl------------------------
#!/usr/bin/perl -w
use strict;
use File::Copy;
use Net::SMTP;
use Sys::Hostname;
my @blockTemp = ();
my @log = ();
my %blockedIPs;
my $pktLimit = 100;
my $crdt = localtime;
my $to = "dean\@company.com";
#additional recipient (if needed)
#my $to2 = "deptaccount\@company.com";
my $from = "pfblocked\@company.com";
my $hstname = hostname();
my $subject = "PF Block Report for $hstname";
#start of html report
my $message = "<p>PF Block Report for $hstname on $crdt</p> \n" .
"<table border\=\"2\" width\=\"650\"><tr><td>Blocked IP</td><td align\=\"center\">Blocked In</td>" .
"<td align\=\"center\">Passed In</td><td align\=\"center\">Blocked Out</td><td align\=\"center\">Passed Out</td></tr> \n";
#copy over current pf log file
#run tcpdump on output file
#then load array with data
copy("/var/log/pflog","pflogfile");
system("tcpdump -neltttr pflogfile > tcpdumpfile.txt");
open(TDF,"tcpdumpfile.txt");
@log = <TDF>;
close TDF;
#parse each entry with regex looking for IPv4 blocked in only
#load those source IPs into array
foreach my $line(@log)
{
my ($action, $srcIP);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)(.*)/)
{
($action, $srcIP) = ($4, $6);
if ($action eq "block in")
{
push(@blockTemp,$srcIP);
}
}
}
#load hash to get unique blocked IPs
#IP address is the hash key and 1 is value for all
%blockedIPs = map { $_ => 1 } @blockTemp;
#find how many blocked entries per blocked IP
#add that to the value of the hash
foreach my $key (keys(%blockedIPs))
{
my $pkt = 0;
my $host = $key;
foreach my $tmpip(@blockTemp)
{
if ($host eq $tmpip)
{
$pkt++;
}
}
$blockedIPs{$key} = $pkt;
}
#look for blocked IPs that are over the bad entries limit
#then go back through logfile and check to see how many entries
#have those IPs as source with pass in or destination with pass or out
foreach my $key (sort (keys(%blockedIPs)))
{
my $blkIP = $key;
my $x = length ($blkIP);
my $pktNum = $blockedIPs{$key};
my $inCnt = 0;
my $outCnt = 0;
my $boCnt = 0;
if ($pktNum > $pktLimit)
{
foreach my $line(@log)
{
my ($action, $srcHost, $dstHost);
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+)\.(\d+) > (\d+\.\d+\.\d+\.\d+)\.(\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $8);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
if ($line =~ /(\w+ \d+ \d+:.\d:.\d+)\.(\d+) rule (\d+)\/\(match\) (\w+ \w+) \w+ (\w+)\: (\d+\.\d+\.\d+\.\d+) > (\d+\.\d+\.\d+\.\d+)\:(.*)/)
{
($action, $srcHost, $dstHost) = ($4, $6, $7);
if ($blkIP eq $srcHost and $action eq "pass in")
{
$inCnt++;
}
if ($blkIP eq $dstHost and $action eq "pass out")
{
$outCnt++;
}
if ($blkIP eq $dstHost and $action eq "block out")
{
$boCnt++;
}
}
}
$message = $message . "<tr><td>$key</td><td align\=\"center\">$blockedIPs{$key}</td>" .
"<td align\=\"center\">$inCnt</td><td align\=\"center\">$boCnt</td>" .
"<td align\=\"center\">$outCnt</td></tr> \n";
}
}
$message = $message . "</table>";
#open SMTP connection and mail information
my $smtp = Net::SMTP->new("smtp.company.com");
$smtp->mail( $from );
#for single recipient
$smtp->to( $to );
#for two or more recipients
#$smtp->to( $to,$to2 );
$smtp->data();
$smtp->datasend("MIME-Version: 1.0\n");
$smtp->datasend("Content-Type: text/html; charset=us-ascii\n");
$smtp->datasend("From: " . $from . "\n");
$smtp->datasend("To: " . $to . "\n");
$smtp->datasend("Subject: " . $subject . "\n");
$smtp->datasend("\n");
$smtp->datasend("\n");
$smtp->datasend( $message );
$smtp->datasend("\n");
$smtp->dataend();
$smtp->quit();
#delete tcpdump output and copied over pflog files
unlink("tcpdumpfile.txt");
unlink("pflogfile");
Subscribe to:
Posts (Atom)