#!/usr/bin/env perl
#
# Copyright 2006-2011 Roy Hills
#
# This file is part of arp-scan.
# 
# arp-scan 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.
# 
# arp-scan 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 arp-scan.  If not, see <http://www.gnu.org/licenses/>.
#
# $Id: arp-fingerprint 18122 2011-02-22 09:00:52Z rsh $
#
# arp-fingerprint -- Perl script to fingerprint system with arp-scan
#
# Author: Roy Hills
# Date: 30th May 2006
#
# This script uses arp-scan to fingerprint the operating system on the
# specified target.
#
# It sends various different ARP packets to the target, and records which
# ones it responds to.  From this, it constructs a fingerprint string
# which is used to match against a hash containing known fingerprints.
#
use warnings;
use strict;
use Getopt::Std;
#
my $arpscan="arp-scan -q -r 1";
#
# Hash of known fingerprints
#
# These fingerprints were observed on:
#
# FreeBSD 7.0	FreeBSD 7.0 on VMware
# FreeBSD 5.3	FreeBSD 5.3 on VMware
# FreeBSD 4.3	FreeBSD 4.3 on VMware
# DragonflyBSD 2.0	Dragonfly BSD 2.0.0 on VMware
# Win 3.11	Windows for Workgroups 3.11/DOS 6.22 on VMware
# 95		Windows 95 OSR2 on VMware
# Win98		Windows 98 SE on VMware
# WinME		Windows ME on VMware
# Windows7	Windows 7 Professional 6.1.7600 Build 7600 on Dell Vostro 220
# NT 3.51	Windows NT Server 3.51 SP0 on VMware
# NT4		Windows NT Workstation 4.0 SP6a on Pentium
# 2000		Windows 2000
# XP		Windows XP Professional SP2 on Intel P4
# 2003		Windows 2003 Server SP1 on Intel P4
# Vista		Windows Vista Beta 2 Build 5384 on VMware
# Vista		Windows Vista SP1 Build 6001 on Dell Inspiron
# 2008		Windows 2008 Server Beta on i386
# Linux 2.0	Linux 2.0.29 on VMware (debian 1.3.1)
# Linux 2.2	Linux 2.2.19 on VMware (debian potato)
# Linux 2.4	Linux 2.4.29 on Intel P3 (debian sarge)
# Linux 2.6	Linux 2.6.15.7 on Intel P3 (debian sarge)
# Cisco IOS	IOS 11.2(17) on Cisco 2503
# Cisco IOS	IOS 11.3(11b)T2 on Cisco 2503
# Cisco IOS	IOS 12.0(8) on Cisco 1601
# Cisco IOS	IOS 12.1(27b) on Cisco 2621
# Cisco IOS	IOS 12.2(32) on Cisco 1603
# Cisco IOS	IOS 12.3(15) on Cisco 2503
# Cisco IOS	IOS 12.4(3) on Cisco 2811
# Solaris 2.5.1	Solaris 2.5.1 (SPARC) on Sun SPARCstation 20
# Solaris 2.6	Solaris 2.6 (SPARC) on Sun Ultra 5
# Solaris 7	Solaris 7 (x86) on VMware
# Solaris 8	Solaris 8 (SPARC) on Sun Ultra 5 (64 bit)
# Solaris 9	Solaris 9 (SPARC) on Sun Ultra 5 (64 bit)
# Solaris 10	Solaris 10 (x86) on VMware
# ScreenOS 5.0	Juniper ScreenOS 5.0.0r9 on NetScreen 5XP
# ScreenOS 5.1	Juniper ScreenOS 5.1.0r1.0 on NetScreen 5GT
# ScreenOS 5.3	Juniper ScreenOS 5.3.0r4.0 on NetScreen 5GT
# ScreenOS 5.4	Juniper ScreenOS 5.4.0r1.0 on NetScreen 5GT
# MacOS 10.4	MacOS 10.4.6 on powerbook G4
# MacOS 10.3	MacOS 10.3.9 on imac G3
# IRIX 6.5	IRIX64 IRIS 6.5 05190004 IP30 on SGI Octane
# SCO OS 5.0.7	SCO OpenServer 5.0.7 on VMware
# 2.11BSD	2.11BSD patch level 431 on PDP-11/73 (SIMH simulated)
# 4.3BSD	4.3BSD (Quasijarus0c) on MicroVAX 3000 (SIMH simulated)
# OpenBSD 3.1	OpenBSD 3.1 on VMware
# OpenBSD 3.9	OpenBSD 3.9 on VMware
# NetBSD 2.0.2	NetBSD 2.0.2 on VMware
# NetBSD 4.0	NetBSD 4.0 on VMware
# IPSO 3.2.1	IPSO 3.2.1-fcs1 on Nokia VPN 210
# Netware 6.5	Novell NetWare 6.5 on VMware
# HP-UX 11	HP-UX B.11.00 A 9000/712 (PA-RISC)
# PIX OS	PIX OS (unknown vsn) on Cisco PIX 525
# PIX OS 4.4	PIX OS 4.4(4) on Cisco PIX 520
# PIX OS 5.1	PIX OS 5.1(2) on Cisco PIX 520
# PIX OS 5.2	PIX OS 5.2(9) on Cisco PIX 520
# PIX OS 5.3	PIX OS 5.3(2) on Cisco PIX 520
# PIX OS 6.0	PIX OS 6.0(4) on Cisco PIX 520
# PIX OS 6.1	PIX OS 6.1(5) on Cisco PIX 520
# PIX OS 6.2	PIX OS 6.2(4) on Cisco PIX 520
# PIX OS 6.3	PIX OS 6.3(5) on Cisco PIX 520
# PIX OS 7.0(1)	PIX OS 7.0(1) on Cisco PIX 515E
# PIX OS 7.0(2)	PIX OS 7.0(2) on Cisco PIX 515E
# PIX OS 7.0(4)	PIX OS 7.0(4) on Cisco PIX 515E
# PIX OS 7.0(6)	PIX OS 7.0(6) on Cisco PIX 515E
# PIX OS 7.1	PIX OS 7.1(1) on Cisco PIX 515E
# PIX OS 7.2	PIX OS 7.2(1) on Cisco PIX 515E
# PIX OS 8.0	PIX OS 8.0(2) on Cisco PIX 515E
# Minix 3	Minix 3 1.2a on VMware
# Nortel Contivity 6.00	Nortel Contivity V06_00 (VxWorks based)
# Nortel Contivity 6.05	Nortel Contivity V06_05.135
# AIX 4.3	IBM AIX Version 4.3 on RS/6000 7043-260
# AIX 5.3	IBM AIX Version 5.3 on RS/6000 7043-260
# Cisco VPN Concentrator 4.7	Cisco VPN Concentrator 3030 4.7.2E
# Cisco IP Phone 79xx SIP 5.x,6.x,7.x	7940 SIP firmware version 5.3
# Cisco IP Phone 79xx SIP 5.x,6.x,7.x	7940 SIP firmware version 6.3
# Cisco IP Phone 79xx SIP 5.x,6.x,7.x	7940 SIP firmware version 7.5
# Cisco IP Phone 79xx SIP 8.x	7940 SIP firmware version 8.6
# Catalyst 1900	Cisco Catalyst 1900 V9.00.03 Standard Edition
# Catalyst IOS 12.2	Cisco Catalyst 3550-48 running IOS 12.2(35)SE
# Catalyst IOS 12.0	Cisco Catalyst 2924-XL running IOS 12.0(5)WC17
# Catalyst IOS 12.1	Cisco Catalyst 3550-48 running IOS 12.1(11)EA1a SMI
# FortiOS 3.00	FortiGate 100A running FortiOS 3.00,build0406,070126
# Plan9		Plan9 release 4 on VMware
# Blackberry OS	Blackberry OS v5.0.0.681 on Blackberry 8900
#
my %fp_hash = (
   '11110100000' => 'FreeBSD 5.3, 7.0, DragonflyBSD 2.0, Win98, WinME, NT4, 2000, XP, 2003, Catalyst IOS 12.0, 12.1, 12.2, FortiOS 3.00',
   '01000100000' => 'Linux 2.2, 2.4, 2.6',
   '01010100000' => 'Linux 2.2, 2.4, 2.6, Vista, 2008, Windows7', # Linux only if non-local IP is routed
   '00000100000' => 'Cisco IOS 11.2, 11.3, 12.0, 12.1, 12.2, 12.3, 12.4',
   '11110110000' => 'Solaris 2.5.1, 2.6, 7, 8, 9, 10, HP-UX 11',
   '01000111111' => 'ScreenOS 5.0, 5.1, 5.3, 5.4',
   '11110000000' => 'Linux 2.0, MacOS 10.4, IPSO 3.2.1, Minix 3, Cisco VPN Concentrator 4.7, Catalyst 1900',
   '11110100011' => 'MacOS 10.3, FreeBSD 4.3, IRIX 6.5, AIX 4.3, AIX 5.3',
   '10010100011' => 'SCO OS 5.0.7',
   '10110100000' => 'Win 3.11, 95, NT 3.51',
   '11110000011' => '2.11BSD, 4.3BSD, OpenBSD 3.1, OpenBSD 3.9, Nortel Contivity 6.00, 6.05',
   '10110110000' => 'NetBSD 2.0.2, 4.0',
   '10110111111' => 'PIX OS 4.4, 5.1, 5.2, 5.3',
   '11110111111' => 'PIX OS 6.0, 6.1, 6.2, ScreenOS 5.0 (transparent), Plan9, Blackberry OS',
   '00010110011' => 'PIX OS 6.3, 7.0(1), 7.0(2)',
   '01010110011' => 'PIX OS 7.0(4)-7.0(6), 7.1, 7.2, 8.0',
   '00000110000' => 'Netware 6.5',
   '00010100000' => 'Unknown 1', # 14805 79.253 Cisco
   '00000110011' => 'Cisco IP Phone 79xx SIP 5.x,6.x,7.x',
   '11110110011' => 'Cisco IP Phone 79xx SIP 8.x', # Also 14805 63.11 Fujitsu Siemens
   );
#
my $usage =
qq/Usage: arp-fingerprint [options] <target>
Fingerprint the target system using arp-scan.

'options' is one or more of:
        -h Display this usage message.
        -v Give verbose progress messages.
	-o <option-string> Pass specified options to arp-scan
/;
my %opts;
my $user_opts="";
my $verbose;
my $fingerprint="";
my $fp_name;
#
# Process options
#
die "$usage\n" unless getopts('hvo:',\%opts);
if ($opts{h}) {
   print "$usage\n";
   exit(0);
}
$verbose=$opts{v} ? 1 : 0;
if ($opts{o}) {
   $user_opts = $opts{o};
}
#
if ($#ARGV != 0) {
   die "$usage\n";
}
my $target=shift;
#
# Check that the target is not an IP range or network.
#
if ($target =~ /\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+/ ||
    $target =~ /\d+\.\d+\.\d+\.\d+\/\d+/ ||
    $target =~ /\d+\.\d+\.\d+\.\d+:\d+\.\d+\.\d+\.\d+/) {
   die "argument must be a single IP address or hostname\n";
}
#
# Check that the system responds to an arp-scan with no options.
# If it does, then fingerprint the target.
#
if (&fp("","$target") eq "1") {
# 1: source protocol address = localhost
   $fingerprint .= &fp("--arpspa=127.0.0.1","$target");
# 2: source protocol address = zero
   $fingerprint .= &fp("--arpspa=0.0.0.0","$target");
# 3: source protocol address = broadcast
   $fingerprint .= &fp("--arpspa=255.255.255.255","$target");
# 4: source protocol address = non local (network 1 is reserved)
   $fingerprint .= &fp("--arpspa=1.0.0.1","$target");	# Non-local source IP
# 5: invalid arp opcode
   $fingerprint .= &fp("--arpop=255","$target");
# 6: arp hardware type = IEEE_802.2
   $fingerprint .= &fp("--arphrd=6","$target");
# 7: invalid arp hardware type
   $fingerprint .= &fp("--arphrd=255","$target");
# 8: invalid arp protocol type
   $fingerprint .= &fp("--arppro=0xffff","$target");
# 9: arp protocol type = Novell IPX
   $fingerprint .= &fp("--arppro=0x8137","$target");
# 10: invalid protocol address length
   $fingerprint .= &fp("--arppln=6","$target");
# 11: Invalid hardware address length
   $fingerprint .= &fp("--arphln=8","$target");
#
   if (defined $fp_hash{$fingerprint}) {
      $fp_name = "$fp_hash{$fingerprint}";
   } else {
      $fp_name = "UNKNOWN";
   }
   print "$target\t$fingerprint\t$fp_name\n";
} else {
   print "$target\tNo Response\n";
}
#
# Scan the specified IP address with arp-scan using the given options.
# Return "1" if the target responds, or "0" if it does not respond.
#
sub fp ($$) {
   my $ip;
   my $options;
   my $response = "0";
   ($options, $ip) = @_;

   open(ARPSCAN, "$arpscan $user_opts $options $ip |") || die "arp-scan failed";
   while (<ARPSCAN>) {
      if (/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\t/) {
         $response = "1";
         last;
      }
   }
   close(ARPSCAN);

   if ($verbose && $options ne "") {
      if ($response) {
         print "$options\tYes\n";
      } else {
         print "$options\tNo\n";
      }
   }

   return $response;
}
