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

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"

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");












-------------------------------------------------------------------

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:

  1. Copies over the current pflog file to the working directory

  2. Runs tcpdump on the copied pflog file and creates a tcpdump text file

  3. Creates an array of the file and then goes through it looking for all blocked in traffic

  4. Creates a hash table of the unique blocked IPs and then determines how many blocked entries for each IP

  5. 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

  6. Reports that information via an HTML formatted email to an address of your choice

  7. 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");

Tuesday, June 29, 2010

OpenBSD 4.7 PF Changes for Gateway Firewall

So yesterday evening I finally got around to updating one of my OpenBSD gateway firewalls to 4.7. On the gateway the PF rules only needed changes in the scrub and proxy sections. Below are the changes. Hopefully this will help you out if you haven’t already updated.

######4.6 pf.conf###############
# Normalize all incoming/outgoing traffic to prevent malformed packets
scrub in on $ext_if all random-id fragment reassemble no-df min-ttl 5
scrub out on $ext_if all random-id fragment reassemble no-df

# FTP-Proxy
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr on $int_if proto tcp from $mynet to any port 21 -> 127.0.0.1 port 8021

# Squid Redirect
rdr on $int_if inet proto tcp from <webproxyusers> to !<nonproxysites> port { 80 8080 }-> 127.0.0.1 port 3128

#######4.7 pf.conf##########

# Normalizing packets
match in on $ext_if scrub (random-id min-ttl 5 no-df)
match out on $ext_if scrub (random-id no-df)

# FTP-Proxy

anchor "ftp-proxy/*"
pass in quick on $int_if proto tcp from $mynet to any port 21 rdr-to 127.0.0.1 port 8021

# Squid Redirect
pass in quick on $int_if proto tcp from <webproxyusers> to !<nonproxysites> port { 80 8080 } rdr-to 127.0.0.1 port 3128



Export Man Page to Text File

Needed a quick way to exporting a man page to a text file. Below is the command for exporting the man page for pf.conf (need to research a few things before upgrading to OpenBSD 4.7)

$ man pf.conf | col -bx > pf.conf.txt

Monday, June 21, 2010

C# ARP Request

I started working on version 3 of my ARPCheck application yesterday. Forgot that I never got around to posting a code example.

==========ARPCheckDemo.cs============

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;

namespace ArpCheckDemo
{
class Program
{
//Configure the SendARP from the iphlpapi.dll
//Currently .NET Doesn't have a Framework Method for Sending an ARP Request
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(int dstIP, int srcIP, [Out] byte[] macAddr, ref int hwAddrLength);

static void Main(string[] args)
{
//IP Address for Destination IP
IPAddress addr = IPAddress.Parse("192.168.2.1");
//Byte Array for MAC Address of Destination IP
byte[] mac = new byte[6];
//Variable for the Hardware Address Length (in Octets)
int hwLength = 6;
//Sending ARP Request for Destination IP Address and Getting Back MAC Byte Array
SendARP(BitConverter.ToInt32(addr.GetAddressBytes(),0), 0, mac, ref hwLength);
//Converting the Byte Array to Hexademical String
String macAddress = BitConverter.ToString(mac, 0, hwLength);
//Display MAC Address for Remote IP
Console.WriteLine(macAddress);
Console.ReadLine();
}
}
}