#!/usr/bin/perl ################################################################### # # induce-arp by concept (concept@ihug.com.au) :: arp-based os detection # # (as found in Napalm issue 6: http://napalm.firest0rm.org/) # # i _need_ webspace. pref. supporting php+sql (web-based fingerprint # submission for this and other tools in the pipeline would be cute), # until then get the latest release from packetstorm. # # induces arp request(s) from host 'a' for host 'b', by spoofing a ping. # ... result is observed and used to stack fingerprint. host 'b' must # not exist, and must be on the same link-layer as you and host 'a'. # # requires Net::RawIP (get it from CPAN or http://quake.skif.net/RawIP/) # if that fails to install, you may need libpcap .. you can get it from # ftp://ftp.ee.lbl.gov/ # # IDEAS: # . include more ping-implementation emulation options # . use ping reply-spoofing instead? # . change local MAC addy too? # . verify that an ARP-capable link-layer is shared with the target # . make optional, and add scan for nonexistant host on shared # link layer # . accept IP input file, or IP range on the command line # # BUGS: # . Net::RawIP's libpcap-ness' pcap timeouts don't seem to work (?), # unless there is traffic flowing, leading to slow reponse times for # certain situations on low-traffic networks. if you have this # problem, generate a couple of packets. # . openbsd 2.5 detection is untested (if you try + it works, tell me!) # . problems if the 'nonexistant' host b is in fact existant # . probably many more # #################################################################### # includes + other misc. init crud $| = 1; $INDUCEARP_VERSION = "v0.27"; $MAXARP = 10; use Net::RawIP qw(:pcap); use Socket; require 'getopts.pl'; print "[ Induce-ARP $INDUCEARP_VERSION by concept :: ARP-based OS detection ]\n"; Getopts('a:b:d:e:'); &showusage unless ($opt_a && $opt_b); # check that the hosts given are valid ($a_name,$a_ip) = (gethostbyname($opt_a))[0,4]; die "$opt_a: host not found" if $?; ($b_name,$b_ip) = (gethostbyname($opt_b))[0,4]; die "$opt_b: host not found" if $?; $a_ip = inet_ntoa($a_ip); $b_ip = inet_ntoa($b_ip); if($opt_d) { $device = $opt_d; } else { $device = 'eth0'; } # check that the emulation requested is implemented $emulation = lc($opt_e); if($emulation) { if($emulation ne '95' && $emulation ne 'openbsd') { print "ERROR: invalid emulation (-e) option.\n"; &showusage; } } # prepare packet for ARP-inducing $inducer = new Net::RawIP ({ ip => { saddr => $b_ip, daddr => $a_ip }, icmp=>{ type => 8 } }); # depending upon ping emulation type (if any), modify ping inducer-packet # Windows 95 B or C if($emulation == '95') { $inducer -> set({ ip=>{ ttl => 32, tos => 0, frag_off => 0 }, icmp=>{ sequence => int(rand(65534)), id => 1, data => "abcdefghijklmnopqrstuvwabcdefghi" } }); } # OpenBSD 2.6 elsif($emulation == 'openbsd') { $inducer -> set({ icmp=>{ sequence => 0, id => int(rand(65534)) } }); } # prepare sniffer $monitor = $inducer -> pcapinit($device,"ether proto \\arp and src host $a_ip and dst host $b_ip",1500,5); # send packet $inducer -> send; # save time packet was sent $lastpacket = timem(); # notify user of what is going on print "Spoofed ping "; if($emulation != '') { print "[$emulation] "; } print "from $opt_b to $opt_a.\n"; print "Induced ARP(s)? "; # loop until $MAXARP or more arps recieved, or we have received an arp and an # attempt to recieve another packet has failed. do { # check for a packet $monitoredpacket = &next($monitor,$temp); # if we got one ... if($monitoredpacket) { $lastpacket = timem(); ®isterpacket(); } } until ($#arp_packets >= $MAXARP || (timem() - $lastpacket > 5)); # if we have recieved packets... if($#arp_packets > 0) { print ".\n"; # OS detect. &classifier(); # show fingerprint. &fingerprint; } else { # whine. print " None. =(\n"; } exit; ######################### FUNCTIONS / SUBS ########################### # show usage information sub showusage { print "\n"; print "Induces ARP request(s) from target to nonexistant machine by spoofing a ping\n"; print "and performs stack fingerprinting based upon the results.\n\n"; print "$0 -a -b [-d ] [-e <95|openbsd>]\n"; print " -a Target of OS detection.\n"; print " -b Nonexistant machine.\n"; print " -d Device. (Outgoing to target by default.)\n"; print " -e Ping-implementation emulation specifier. (None by default).\n"; print "\n"; print "NOTE: Target, nonexistant machine and you (on device specified, or default\n"; print " route to target) must share an ARP-utilising link-layer!\n"; exit; } # register a detected packet as known sub registerpacket { # print "regpacket encountered struct of $#arp_packets size.\n"; if($#arp_packets == -1) { $packetpoz = 0; } else { $packetpoz = $#arp_packets; } # print "regpacket set packetpoz to $packetpoz"; $arp_packets[$packetpoz+1] = timem(); if($#arp_packets > 1) { print ","; } print " "; print "$#arp_packets"; # print " was structsize on regpacket exit.\n"; } # classify the os sub classifier { $unknown_message = "(unknown!)\n Please contribute fingerprint to the database, including exact OS type.\n"; print "probable OS: "; # temporary dodge-classification if($#arp_packets == 1) { print "Unknown. Could be Win95 or OpenBSD 2.6.\n"; } else { if($#arp_packets == 2) { print $unknownmessage; } elsif($#arp_packets == 3) { $p1_p2_diff = $arp_packets[2] - $arp_packets[1]; $p2_p3_diff = $arp_packets[3] - $arp_packets[2]; $diff_diff = abs($p1_p2_diff - $p2_p3_diff); # if the packet diffs are both ~1 second and they # don't vary too much, we've got linux. if($p1_p2_diff > .965 && $p2_p3_diff > .965 && $diff_diff < .2) { print "Linux.\n"; } # if p1 differs from p2 by ~5sec, and p2 differs # from p3 by ~12sec, we've got openbsd. elsif((abs($p1_p2_diff - 5) < .25) && (abs($p2_p3_diff - 12) < .25)) { print "OpenBSD v2.5.\n"; } else { print $unknownmessage; } } } } # output the fingerprint sub fingerprint { print "fingerprint: "; if($#arp_packets == 1) { print "None (only one ARP received).\n"; return; } $packetpoz = 1; do { $this_time = $arp_packets[$packetpoz]; if($packetpoz > 1) { if($packetpoz > 2) { print ":"; } print $this_time - $first_time; } else { $first_time = $this_time; } $packetpoz++; } while ($packetpoz <= $#arp_packets); print "\n"; } ############################# EOF ###############################