#!/usr/bin/perl -w

#######################################################################
# athena-2k.pl - A Windows 2000 SNMP Auditing Tool
# (C) 2001 jshaw [at] sps [dot] lane [dot] edu
# version: 2001APR26 (For UNIX & WIN32 using ActivePerl)
# url: http://www.sps.lane.edu/~jshaw
#
# Largely coded at the fine Cafe Paradiso in Eugene, Oregon, late at night
#
# I more than welcome ideas, contributions, and constructive criticism
# If you like this program, please consider sending me an email. It's
# rewarding to hear when my software helps other people out, and it
# keeps me going.
#
#
# Part of my G.O.S.H. Toolkit
# GOSH is an acronym for "Groper Of SNMP Hosts", or at your leisure:
# "GOSH! I didn't know the Windows NT SNMP Service gave out that 
# much information!", although if it's your machine, I'm sure you'd
# rather use another four-letter word in place of "GOSH".
#
# I plan to work on more tools as part of the GOSH Kit, specifically
# a Cisco device and HP printer device tool, that essentially does the 
# same thing as this one. Also, a UNIX one would be handy (maybe for the
# UCD-SNMP kit?
#
# Submissions and ideas welcome. I will give you proper credits. :-)
#
#
# Synopsis:
# This program takes a flurry of arguments and returns the data you 
# request. As you can see, it gives you a lot of information, and
# I see it as being valuable to both network administrators and security
# engineers. I created this program, because I couldn't find a tool like
# this for UNIX folks that I liked.
#
#
# Background/Rant/More Information/Etc:
# Most network administrators these days block the handful of NetBIOS
# ports at their firewall. An alarming number of net admins don't bother
# blocking port 161/udp from the outside world. To make it worse, the
# SNMP Service is (perhaps unnecessarily) required for a host of add-on
# programs that come with Windows NT and 2000. Administrators don't bother
# to properly configure the SNMP services with remote host access controls
# or even changing the community strings. 
#
# Some day, I expect Microsoft to get a clue about security, but first,
# their customers do and they have to let Microsoft know they won't settle
# for anything less than proactively secure. They won't change until they
# see a financial incentive to do so.
#
# Now, more than ever, security is an essential part of any network. You
# read in the news daily about company's servers being cracked and 
# credit card databases being stolen due to lax security practices and
# procedures. This is unacceptable.
#
#
# What You Can Do To Fix Your Network:
# Learn the basics of SNMP, there are several good sources on the web.
# Use my snoopy.pl, and this tool to scan your network for open devices.
# Set their community strings to something other than "public" and
# "private" AND set IP access controls so that only specific IP addresses
# can query the equipment in question.
#
#
# A Final Word: (Trying not to make it political ;-) )
#
# Script Kiddy <noun>: a person who uses this script to crack into other
# people's equipment.
#
# Many network and system administrators out there are overworked, under-
# budgetted, and underskilled. But they all generally try to do the best
# they can to support themselves and their families. And the last thing 
# they need is someone most likely a lot less skilled than them, trying
# to break into their network with this script.
#
# You haven't proved anything, except that you can make someone's hard
# job, harder. And if you can't at least follow the above, don't change
# their webpage to point out how lame the system administrator is. 
#
# In short, be good & have some respect and compassion! Thanks :-) 
#
#
# Installation:
# 1. Install the Net::SNMP perl module. Don't worry about Getopt::Std, it 
#    comes with PERL.
# 2. chmod u+x this script and type /path/to/this/script and hit [Enter]. 
#    The program will give you a list of options along with a couple of
#    simple options.
#
#
# Thanks: (Yes, most have funny names.)
# LnkStern - for *lots* of patience, WIN32 testing, and input, he really
#            went out of his way to help me, and it's much appreciated! 
# Rob Beck from @Stake - patience, testing, input, help with some PERLisms
#                        providing a box for testing, thanks :-) 
# SirDystic - for input and ideas to help make this more full-featured 
# rkl - advice, being cool
# Rick Hicks - Documentation reviews, honest criticisms
# SPS Techs - for having test machines ready to go
# rfp, fyodor, dugsong, theo - for all the work you've done to better computer
#                        security, thanks!
#
#
# Hello's To Important People:
# t12, sangfroid, nee, athena, captain skynyrd, ells & the rest of the cDc
# shep@juniper, syke & the rest of NHC, valsmith, diluted, optyx, bronc,
# the networking folks @ the University of Oregon & OPEN, the Casas family
#
#
# License:
# This program is distributed under the GNU Public License. I'm not a GPL
# loon, but I wish for this program to be GPL'd. If you use snippets of
# code from this application, please give me proper credit. I worked
# reasonably hard researching for this program.
#

#
# TO DO:
#		- Implement NT 4 compatibility
#		- Implement comType (Server, Workstation, SQL Server, etc)
#		- Implement IIS, Exchange, SQL Server MIBs
#		- Implement svSessionTable stuff
#		- Find docs on svstatPwErrors (eg. what the hell it is, exactly :-) )
#		  probably "Bad Password Errors", but not absolutely sure.
#		- Find docs on svDescription, haven't found a machine that returns
#		  anything for it, yet.
#		- ??? I'm sure there's more I'm forgetting at the moment...
#


use strict;
use Net::SNMP;										# Get this
use Getopt::Std;									# Comes with PERL

# Program variables
my $version 	= ".9 *Beta* 2001 APR 26";
my $port      	= 161;
my $session;
my $error;
my $response;
my %option;
my $remote;
my $community;
my $timeout;

# MIBs Involved - do not touch
my $mibDescr		= "1.3.6.1.2.1.1.1.0";			# System Description
my $mibNTDomain		= "1.3.6.1.4.1.77.1.4.1.0";		# NT Primary Domain
my $mibUptime		= "1.3.6.1.2.1.1.3.0";			# System Uptime
my $mibContact		= "1.3.6.1.2.1.1.4.0";			# System Contact
my $mibName			= "1.3.6.1.2.1.1.5.0";			# System Name
my $mibLocation		= "1.3.6.1.2.1.1.6.0";			# System Location
my $mibRunning 		= "1.3.6.1.2.1.25.4.2.1.2";		# Running Programs
my $mibRunPid		= "1.3.6.1.2.1.25.4.2.1.1";		# Running PIDs
my $mibInstalled 	= "1.3.6.1.2.1.25.6.3.1.2";		# Installed Programs
my $mibInstDate		= "1.3.6.1.2.1.25.6.3.1.5";		# Installed Date
my $mibServices		= "1.3.6.1.4.1.77.1.2.3.1.1";	# Services (add to it)
my $mibAccounts		= "1.3.6.1.4.1.77.1.2.25";		# User Accounts
my $mibDateTime		= "1.3.6.1.2.1.25.1.2.0";		# System Date & Time
my $mibMemSize		= "1.3.6.1.2.1.25.2.2.0";		# Total System Memory

# Storage
my $mibStorDescr	= "1.3.6.1.2.1.25.2.3.1.3";		# Storage Description
my $mibStorSize		= "1.3.6.1.2.1.25.2.3.1.5";		# Storage Total Size
my $mibStorUsed		= "1.3.6.1.2.1.25.2.3.1.6";		# Storage Used
my $mibPtype		= "1.3.6.1.2.1.25.3.8.1.4";		# Partition Type
my $mibSDType		= "1.3.6.1.2.1.25.2.3.1.2";		# Storage Device Type

# Network
my $mibInt          = "1.3.6.1.2.1.2.2.1.2";        # Network Interfaces
my $mibIntMTU		= "1.3.6.1.2.1.2.2.1.4";		# Net Int MTU Size
my $mibIntSpeed		= "1.3.6.1.2.1.2.2.1.5";        # Net Int Speed
my $mibIntBytesIn	= "1.3.6.1.2.1.2.2.1.10";		# Net Int Octets In
my $mibIntBytesOut	= "1.3.6.1.2.1.2.2.1.16";		# Net Int Octects Out
my $mibIntPhys		= "1.3.6.1.2.1.2.2.1.6";		# Int MAC addr
my $mibAdminStat	= "1.3.6.1.2.1.2.2.1.7";		# Int up/down?
my $mibIPForward	= "1.3.6.1.2.1.4.1.0";			# IP Forwarding?
my $mibIPAddr		= "1.3.6.1.2.1.4.20.1.1";		# Int IP Address
my $mibNetmask		= "1.3.6.1.2.1.4.20.1.3";		# Int IP Netmask

# Shares
my $mibShareName	= "1.3.6.1.4.1.77.1.2.27.1.1";	# Reports Share Names
my $mibSharePath	= "1.3.6.1.4.1.77.1.2.27.1.2";	# Reports Share Path
my $mibShareComm	= "1.3.6.1.4.1.77.1.2.27.1.3";	# Reports Share Comments

# Routing Info
my $mibRouteDest	= "1.3.6.1.2.1.4.21.1.1";		# Route Destinations
my $mibRouteMetric	= "1.3.6.1.2.1.4.21.1.3";		# Route Metric
my $mibRouteNHop	= "1.3.6.1.2.1.4.21.1.7";		# Route Next Hop 
my $mibRouteMask	= "1.3.6.1.2.1.4.21.1.11";		# Route Mask

# TCP Connections
my $mibTCPState		= "1.3.6.1.2.1.6.13.1.1";		# TCP Connect State
my $mibTCPLAddr		= "1.3.6.1.2.1.6.13.1.2";		# TCP Local Address
my $mibTCPLPort		= "1.3.6.1.2.1.6.13.1.3";		# TCP Local Port
my $mibTCPRAddr		= "1.3.6.1.2.1.6.13.1.4";		# TCP Remote Address
my $mibTCPRPort     = "1.3.6.1.2.1.6.13.1.5";       # TCP Remote Port

# UDP Listening
my $mibUDPLAddr		= "1.3.6.1.2.1.7.5.1.1";		# UDP Local Address
my $mibUDPLPort		= "1.3.6.1.2.1.7.5.1.2";		# UDP Local Port


###################### M A I N #######################################

# Duh...
getopts("aghrisunt:c:o:", \%option);

# Start queries...
if(!$option{t}) { &Usage; }							# RTFM
	else { $remote = $option{t}; }
if(!$option{c}) { $community = "public"; }			# Community string
	else { $community = $option{c}; }

&Init;
if($option{g} || $option{a}) { &General; }			# -g
if($option{h} || $option{a}) { &Hardware; }			# -h
if($option{r} || $option{a}) { &Running; }			# -r
if($option{i} || $option{a}) { &Installed; }		# -i
if($option{s} || $option{a}) { &Services; }			# -s
if($option{u} || $option{a}) { &UserAccounts; }		# -u
if($option{n} || $option{a}) { &Network; }			# -n
if($option{o}) { $timeout = $option{o}; } 			# -o
	else { $timeout = "5"; }

# Finished
$session->close;



###################### SUBROUTINES ##################################

sub Init {
	# Establish SNMP session...
	($session, $error) = Net::SNMP->session(
		Hostname    => $remote,
		Community   => $community,
		Port        => $port
	);

	# Die if SNMP session fails to work...
	if(!defined($session))
		{ die "ERROR: $error\nIs the host running SNMP?\n\n"; }

	# added the timeout value as an option at the request of users
	# thanks miah, rev_g, & the security guy at TUEV-Sueddeutschland :-)
	# updated: 2001 apr 26
	$session->timeout($timeout);
	
	print "\n\nathena-2k.pl - A Windows 2000 SNMP Auditing Tool\n";
	print "version: $version by jshaw [at] sps [dot] lane [dot] edu\n";
	print "(C) 2001 Jacob Shaw - This software is licensed under the GPL\n";
	print 'homepage: http://www.sps.lane.edu/~jshaw';
	print "\n\n";
}


sub General {
	my $Name 		= &GetRequest($mibName);
	my $NTDomain	= &GetRequest($mibNTDomain);
	my $Descr		= &GetRequest($mibDescr);
	my $Contact		= &GetRequest($mibContact);
	my $Location	= &GetRequest($mibLocation);
	my $Uptime		= &GetRequest($mibUptime);
	my $DateTime	= &GetDate(&GetRequest($mibDateTime));
		
	print <<"EOF";


Information for $Name [$remote]
-----------------------------------------------------------

Description:
--------------
$Descr

Primary Domain: $NTDomain

Contact: $Contact
Location: $Location

System Uptime: $Uptime
System Date \& Time: $DateTime

EOF
	
	$|++;
}


sub Hardware {
	my $MemSize		= &GetRequest($mibMemSize);		# Total Memory
	my @StorDescr	= &GetTable($mibStorDescr);		# Device Description
	my @SDType		= &GetTable($mibSDType);		# Device Type
	my @Ptype		= &GetTable($mibPtype);			# Partition Type


	print <<"EOF";

Hardware Configuration:
-----------------------------------------------------------

Total Memory: $MemSize KB

Storage:
--------------
EOF

	# Process garbagely looking variables & then print!!!
	for ($a = 0; $a < scalar(@StorDescr); $a++) {

		## Convert device type to human-readable
		if($SDType[$a] eq "1.3.6.1.2.1.25.2.1.3")
			{ $SDType[$a] = "Virtual Memory"; }
		if($SDType[$a] eq "1.3.6.1.2.1.25.2.1.4")
			{ $SDType[$a] = "Fixed Disk"; }
		if($SDType[$a] eq "1.3.6.1.2.1.25.2.1.5")
			{ $SDType[$a] = "Removable Disk"; }
		if($SDType[$a] eq "1.3.6.1.2.1.25.2.1.7")
			{ $SDType[$a] = "Compact Disc"; }

		## fudge for the chocolate cluebies
		#if($StorSize[$a] eq "0") { $StorSize[$a] = "N/A"; }

		# Need to put a rule for FAT32 here, whenever I find a machine with
		# FAT32 on it :-) - e-mail me if you do
        if($Ptype[$a] eq "1.3.6.1.2.1.25.3.9.2") { $Ptype[$a] = "UNKNOWN"; }
        if($Ptype[$a] eq "1.3.6.1.2.1.25.3.9.9") { $Ptype[$a] = "NTFS"; }

		## a kludge, yes i know
		if($StorDescr[$a] ne "Virtual Memory") {

			print <<"EOF";
$StorDescr[$a]
	Device Type:    $SDType[$a]
	Partition Type: $Ptype[$a]

EOF
		}
	}

	$|++;
}


sub Running {
	my $temp;
	my @Running			= GetTable($mibRunning);
	my @RunPid			= GetTable($mibRunPid);

	#my @Running = reverse @RevRunning;
	print "\nRunning Applications:\n";
	print "-----------------------------------------------------------\n";
	printf "\n%10s%20s\n", "Process ID", "Process Name";
	printf "%10s%20s\n", "----------", "------------";
	for ($a = 0; $a < scalar(@RunPid); $a++) {
		if($Running[$a] ne "System Idle Process") {
			printf "%10s%20s\n", $RunPid[$a], $Running[$a];
		}
	}

	$|++;
}


sub Installed {
	my $temp;
	my @Installed		= &GetTable($mibInstalled);
	my @InstDate		= &GetTable($mibInstDate);

    print "\n\nInstalled Applications:\n";
    print "-----------------------------------------------------------\n\n";

	# header for below info
	print "Date Installed\tTime\tName\n";
	print "--------------\t----\t----\n";

	for($a = 0; $a < scalar(@Installed); $a++) {
		my $Date = &GetDate($InstDate[$a]);
		print "$Date\t$Installed[$a]\n";
	}

	$|++;
}


sub Services {
	my $temp;
	my @Services 		= GetTable($mibServices);

    print "\n\nServices:\n";
    print "-----------------------------------------------------------\n";
	foreach $temp (@Services) { print "$temp\n"; }

	$|++;
}


sub UserAccounts {
	my $temp;
	my @Accounts		= GetTable($mibAccounts);

    print "\n\nUser Accounts:\n";
    print "-----------------------------------------------------------\n";
	foreach $temp (@Accounts) { print "$temp\n"; }

	$|++;
}


sub Network {
	my @Int = &GetTable($mibInt);					# interface names
	my @MTU = &GetTable($mibIntMTU);				# interface MTU size
	my @IntSpeed = &GetTable($mibIntSpeed);			# interface speeds
	my @IntBytesIn = &GetTable($mibIntBytesIn);		# interface bytes in
	my @IntBytesOut = &GetTable($mibIntBytesOut);	# interface bytes out
	my @IntPhys = &GetTable($mibIntPhys);			# MAC address
	my @IPAddr = &GetTable($mibIPAddr);				# interface IP addresses
	my @Netmask = &GetTable($mibNetmask);			# interface IP netmask
	my @AdminStat = &GetTable($mibAdminStat);		# admin status
	my $IPForward = &GetRequest($mibIPForward);		# IP Fwding?
		if ($IPForward eq "0" || $IPForward eq "2") 
			{ $IPForward = "no"; }

	# Hell if I know why we need this, chalk it up as a bug
	# For whatever reason, the netmask values return opposite of 
	# what everything else does. *beats head against dead goat*

	print "\n\nNetwork:\n";
    print "-----------------------------------------------------------\n";
	print "IP Forwarding Enabled: $IPForward ";
		print "(Still need to decode int values)\n";

	# Output data
	print "\n\nInterfaces:\n--------------\n";

	for ($a = 0; $a < scalar(@Int); $a++) {

		chomp $Int[$a];

		if($AdminStat[$a] eq "0") 
			{$AdminStat[$a] = "down"} else { $AdminStat[$a] = "up"; }

		$IntSpeed[$a] = $IntSpeed[$a] / 1000000;
		print "Interface: [ $AdminStat[$a] ] $Int[$a]\n";
		print "\tHardware Address: $IntPhys[$a]\n";
		print "\tInterface Speed: $IntSpeed[$a] Mbps\n";
		print "\tIP Address: $IPAddr[$a]\n";
		print "\tNetmask: $Netmask[$a]\n";
		print "\tBytes In: $IntBytesIn[$a]\n";
		print "\tBytes Out: $IntBytesOut[$a]\n";
		print "\n";
	}

	&GetRoutes;
	&TCPConnects;
	&UDPListen;
	&GetShares;
	
	$!++;
}


sub GetShares {
	my @ShareName	= &GetTable($mibShareName);
	my @SharePath	= &GetTable($mibSharePath);
	my @ShareComm	= &GetTable($mibShareComm);

	print "\n\nNon-Administrative Shares:\n";
	print "---------------------------\n";
	print "(No Response likely means its a Win2k Pro box)\n\n";

	for($a = 0; $a < scalar(@ShareName); $a++) {
		print "Share Name: $ShareName[$a]\n";
		print "\tPath: $SharePath[$a]\n";
		print "\tComments: $ShareComm[$a]\n\n";
	}
}


sub GetRoutes {
	my @RouteDest	= &GetTable($mibRouteDest);
	my @RouteNHop	= &GetTable($mibRouteNHop);
	my @RouteMask	= &GetTable($mibRouteMask);
	my @RouteMetric	= &GetTable($mibRouteMetric);

	print "\nRouting Information:\n";
	print "---------------------\n\n";
	print "     Destination:\t Next Hop:\t      Mask:\tMetric:\n";
	print "     ------------\t---------\t      -----\t-------\n";

	for($a = 0; $a < scalar(@RouteDest); $a++) {
		printf "%17s%17s%17s%7s\n", $RouteDest[$a], $RouteNHop[$a],
			$RouteMask[$a], $RouteMetric[$a];
	}
	
	$|++;
}	


sub TCPConnects {
	my @TCPState	= &GetTable($mibTCPState);
	my @TCPLAddr	= &GetTable($mibTCPLAddr); 
	my @TCPLPort	= &GetTable($mibTCPLPort);
	my @TCPRAddr	= &GetTable($mibTCPRAddr);
	my @TCPRPort	= &GetTable($mibTCPRPort);

	print "\nTCP Connections:\n";
	print "---------------------\n";

	print "            Local\t\t\tRemote\n";
	print "        Address:Port\t\t   Address:Port\n";
	for($a = 0; $a < scalar(@TCPState); $a++) {
		if($TCPState[$a] eq "2") { $TCPState[$a] = "(listening)"; }
		if($TCPState[$a] eq "5") { $TCPState[$a] = "(established)"; }
		if($TCPState[$a] eq "8") { $TCPState[$a] = "(Close Wait)"; }
		if($TCPState[$a] eq "11") { $TCPState[$a] = "(time wait)"; }

		printf "%15s:%6s   %17s:%6s\n", $TCPLAddr[$a], $TCPLPort[$a],
			$TCPRAddr[$a], $TCPRPort[$a];
	}

	$|++;
}


sub UDPListen {
	my @UDPLAddr	= &GetTable($mibUDPLAddr);		# Get listening addr
	my @UDPLPort	= &GetTable($mibUDPLPort);		# Get listening port

	print "\nListening UDP Ports:\n";
	print "---------------------\n";
	printf "%20s:%6s\n", "Address", "Port";

	for($a = 0; $a < scalar(@UDPLAddr); $a++) {
		printf "%20s:%6s\n", $UDPLAddr[$a], $UDPLPort[$a];
	}

	$|++;
}


sub GetRequest {
	# Takes only one MIB as an argument!

	my $response;

    if(!defined($response = $session->get_request($_[0]))) {
        #$session->close;
        return "No Reponse";
    }

	my $Return = $response->{$_[0]};
    return $Return;

}


sub GetTable {
	# Takes only one MIB as argument!

	my @Return;
	my $response;

    # Grab all the MIBs under the specified OID
    if(!defined($response = $session->get_table($_[0]))) {
        #$session->close;
        return "No Response";
    }
	my $x = 0;
	my $key;

	foreach $key (sort keys %$response) {
		if($$response{$key} ne "Virtual Memory") {
			$Return[$x] = $$response{$key};
			$x++;
		}
	}

	return @Return;
}


sub GetDate {
	# Converts yay byte string to human-readable
	my $year = hex(substr($_[0], 2, 4));
	my $day = hex(substr($_[0], 6, 2));
	my $month = hex(substr($_[0], 8, 2));
	my $hour = hex(substr($_[0], 10, 2));
	my $minute = hex(substr($_[0], 12, 2));

	if($day < "10") { $day = "0" . $day; }
	if($month < "10") { $month = "0" . $month; }
	if($hour < "10") { $hour = "0" . $hour; }
	if($minute < "10") { $minute = "0" . $minute; }

	my $Return = "$day/$month/$year\t$hour:$minute";
	return $Return;
}


sub Usage {
print <<"EOF";

athena-2k.pl v$version - A Windows 2000 SNMP auditing tool
(C) 2001 jshaw [at] sps [dot] lane [dot] edu
http://www.sps.lane.edu/~jshaw
(NT 4 Support coming!)

Usage:
./athena-2k.pl <options> <target> <community>

	Options:
	--------
	-t	- specify target hostname/ip *REQUIRED*
	----------------------------------------------------------
	*Plus one or more of the below*
	-a	- ALL; lists all information about the remote host
	-g	- provides general information about the remote host
	-h	- provides hardware information about the remote host
	-r	- lists running processes on remote host
	-i	- lists installed applications & install date on remote host
	-c	- lists services on remote host
	-u	- lists user accounts on remote host
	-n	- lists network information from remote host
	-o	- set the timeout value for a response (default: 5 secs)

   Examples:
   --------
   ./athena-2k.pl -a -t 10.0.0.1 -c public
   (Would report ALL information for host 10.0.0.1 using community "public")

   ./athena-2k.pl -gn -t 10.0.0.1 -c public
   (Would only report general & network information for host 10.0.0.1)

EOF

exit;
}
