Snacktime: A Perl Solution
for Remote OS Fingerprinting

Tod Beardsley, Plan B Security
June, 2003
Austin, TX

Background

Franck Veysset, Olivier Courtay, and Olivier Heen of Intranode research noticed that one could fairly reliably detect a wide range of operating systems by timing the retransmission timeout lengths of the TCP handshake. Turns out, this is not only a surprisingly reliable, but has the potential to be extremely stealthy. Their proof-of-concept tool, RING, demonstrated this technique, and I reviewed their research for my GCIA Assignment #1, Ring out the old, RING in the new; see these papers for more in-depth analysis on how RTO timing works.

Being that I'm a chimp, I'm much better with Perl than I am with C, so I ported the concepts over, and added on some extra passive fingerprinting techniques. The result is Snacktime -- a half-open, half-passive OS Fingerprinting tool.

Quick Start

  1. Download Snacktime
  2. Get your minimum requirements in order.
  3. Establish some means of blocking, or otherwise avoiding, resetting connections to your target.
  4. Run perl snacktime -t www.mpaa.org -p 80 -Vm (or whatever options).

And yes, you need to be root in order to do anything useful.

Downloading Snacktime

Snacktime is available for download at http://www.planb-security.net/tools/snacktime.tgz, and includes:
LICENSE GPL happiness
CHANGELOG What's changed between versions
snacktime.html This documentation
snack.fp Snacktime's fingerprint file
snacktime.pl Snacktime itself

Snacktime is also available uncompressed at the end of this document, in case you just want to read through it.

Requirements

Snacktime runs quite reliably on Linux kernels 2.2.x and 2.4.x (I use Debian) with libpcap, and requires the following CPAN Perl modules:
Net::RawIP For crafting and sending packets(.deb)
Net::Pcap For receiving packets(.deb)

Finally, you'll need some method of dropping your own RSTs so they don't spoil everything.

Dropping RSTs

One of the drawbacks of RING is that when you're fingerprinting, there's not a lot else you can do, network-wise. Snacktime doesn't even try to shut up your stack, so you'll have to take care of that on your own. Two methods I've found work well:

Firewall rules

The traditional method for dropping packets is using a firewall rule. There are a few ways to do this, depending on your existing firewall requirements, but either of these rules would work, presuming you have iptables installed:

   iptables -A OUTPUT -p tcp --dport [target port] --tcp-flags RST RST -j DROP
   iptables -A INPUT  -p tcp --sport [target port] --tcp-flags SYN SYN -j DROP

The former will work as long as it's on any device between you and your target, while the latter will only work if the iptables firewall is on your machine.

ARP spoofing

Another way to avoid RSTing the connection is to spoof both your source IP address (using Snacktime's -s option) and your MAC address (using Dug Song's arpspoof (.deb)). This is more fun, and can have the added effect of reducing the number of packets sent to the target with identifying information about your machine from one to zero. To achieve this, get and install dsniff, and run (in another console):

	arpspoof -t [your gateway] [your UNUSED, local fake IP]

A couple caveats: ARP spoofing can be dangerous (some network devices react badly when you start mucking around with their ARP tables), and illegal if you do it with a publicly-addressable Internet address (some states specifically outlaw this sort of spoofing activity through brain dead Super-DMCA legislature).

Syntax and Options

Snacktime has a short usage blurb, gotten at by supplying no options, or -h. Here are some more details:

Common Options

-t [target address]
This is the only required argument (everything else has default values), because you need to pick a target. This can be either an IP address or a DNS name. Note, however, if you use DNS, you may send some extra packets to your target's network, which could impact your stealth. Finally, only one host is allowed on the line -- Snacktime doesn't (yet) have the capability of taking a list of IPs, or a range, or anything like that. In the meantime, use a shell script wrapper.
-p [target port] (default: 80)
The target port. This port must be open and unfiltered, from your point of view. If you pick a closed port (one that sends you a RST), Snacktime will notice and exit.
-w [seconds] (default: 65)
The number of seconds to wait after the last SYN-ACK (this is not the total amount of time to wait). Lowering this value will speed things up, but too low, and you may time out before your target does. (most notably, the ridiculously persistent Sun stacks).
-m (no arguments)
Use more stuff to score results -- specifically, gauge the Time-to-Live (TTL) and TCP Window Size (WinSize) values. This can help break ties between two similar RTOs, but on the flip side, can throw off results if your target has customized these values, or is behind a device that rewrites header information, as some firewalls and load balancers are wont to do.
-v (no arguments)
Be verbose during testing and scoring. Its use is recommended to time new, unknown OSes.
-s [source address] (default: best guess)
A source IP address. Use this if your default Ethernet device is, for some reason, not reporting the "right" IP address. You'll also want to use this if you intend to spoof your source IP, and you have a method of hearing the replies (ie, through arp spoofing, discussed above).

Uncommon Options

-D [ethernet device] (default: best guess)
Your ethernet device, if you have many and Snacktime is guessing wrong. On Linux, this will tend to be eth0, eth1, etc.
-P [source port] (default: random > 1024)
A defined source port. This may be important for your own network's egress rules, or to defeat silly ingress rules on your target's network. For example, some administrators who haven't read a networking book in the last five years may still be allowing any traffic orgininating from ports 20 or 53, regardless of TCP flags.
-f [fingerprint file] (default: ./snack.fp)
Location of your fingerprint file.
-V (no options)
Be very verbose during testing and scoring. Its use implies -v, naturally, and its use is recommended if you don't mind a couple screens full of scoring information.

Reasons For Usage

So, why would you want to use Snacktime? I can think of a few reasons:

Snacktime versus...

As far as I can tell, measuring RTOs seems to be a pretty accurate and stealthy method for profiling networked devices, and Snacktime seems to be a pretty reasonable implementation. Furthermore, masking ones "normal" RTO can be difficult for some operating systems, and given this technique's relative newness, is much more uncommon than more traditional methods of defeating profiling (but see below).

Some comparisons to other fingerprinting techniques:

Nmap
Nmap is the de facto standard for OS profiling. It requires at least two ports on the target machine: one open and one closed. By measuring the ways the target responds to a handful of funny packets sent to each, Nmap makes its guess. However, the days of having machines sitting directly on the network without some kind of filtering device are receding, and Nmap's reliability drops dramatically when it's only given a single open port to work with.
p0f
p0f, by design, doesn't initiate communication, and only cares about initial SYNs. p0f doesn't generate any packets, so it is necessarily perfectly stealthy, but its use requires you to somehow get your target to talk to you.

However, these applications both have at their disposal comprehensive fingerprint databases. Snacktime has only been tested (and lightly, I might add) against a couple dozen different devices. So, if you have any new fingerprints, please feel free to mail them in to snacktime@planb-security.net.

Finally, there are other fingerprinting applications (QueSO, Internet Scanner, Ettercap, etc.), and while they all have their particular interface niceties, they tend make use of Nmap's fingerprint database and techniques (many funny packets to multiple dest ports).

Known bugs, Limitations, and Possible Features

Controlling RSTs internally
This has been a major pain, and probably the biggest limitation of my weak Perl skillz. I used to control iptables directly, but the setting and removing of custom rules had an unfortunate side effect of wiping out any existing rules. However, I'm a fan of the ARP spoofery, and I'm liking that method more these days. Unfortunately, I haven't found a Perl module that lets me drop Ethernet packets directly on the wire, and my attempts at hacking up Net::RawIP have been ugly (they work, but it's really, really lame).
Ranges of targets.
Right now, I handle ranges through a shell script, but it's dumb not to do this internally. However, since my IPC sucks, this turns out to be a little harder than it sounds.
Logging
Once I get ranges down, I'll want to log the results. After these improvements, Snacktime can finally find use as an asset management tool (which was kind of the whole point at the beginning of this project).
Source OS Impersonation
It would be amusing to have the initial Syns conform to a number of different p0f-identifiable fingerprints.
Decoy Source IPs
Since Snacktime never completes the handshake, this could be useful -- Nmap uses decoys to great effect, but I'm having trouble coming up with legitimately, non-evil uses for such a feature.
Cross-platform usefulness
Maybe some day.

Detecting and Defeating Snacktime

Snacktime packets look like this:

  0.000000 192.168.1.21 -> 192.168.1.1  TCP 12676 > 666
  [SYN] Seq=2730301395 Ack=0 Win=64240 Len=0

0000  00 04 5a f6 ed c0 00 10 a4 13 0f f5 08 00 45 00
0010  00 28 05 cd 00 00 40 06 f1 9c c0 a8 01 15 c0 a8
0020  01 01 31 84 02 9a a2 bd 17 d3 00 00 00 00 50 02
0030  fa f0 42 dc 00 00
I haven't made any particular effort to make Snacktime packets look more or less like any other normal SYN packets, but that said, I'd be very surprised if there's an IDS system out there that could reliably detect Snacktime traffic -- after all, little lost SYNs are not all that odd on the Internet.

As for defeating Snacktime (and other RTO profilers), changing the behavior of a device's SYN-ACK retries can range from trivial to impossible. For Linux, Toby Miller's recent paper on passive OS fingerprinting discusses the/proc/sys/net/ip4 directory -- therein, you can edit the tcp_synack_retriesto change the number (but not the timing) of SYN-ACKs sent back to Snacktime.

For Windows NT/2k, Microsoft's KB article 120642 discusses a number of TCP/IP behavioral defaults one could change, including default TTL and WinSize, but the RTO is not one of them. This is the same situation for the BSDs, as far as I can tell -- again, BSD administrators can edit the TTL and WinSize values using sysctl, but it appears the RTOs are off-limits. These measures will certainly muck up evaluating Snacktime scores using the -m (more stuff) option, but Snacktime's core functionality remains unaffected.

References and Further Reading

Snacktime Source

In case you just want to read my lame source (or, more likely, you're Googling around for examples of Net::RawIP or Net::Pcap usage), the current Snacktime source is avaialable here.

#!/usr/bin/perl

##############################################################################
# snacktime.pl is a Perl implementation of RING for OS Fingerprinting.
# Usage: Snacktime.pl -t <target> -p <port> [-s <source address>]
#
# Requirements: Perl v5.6.1 (or later? probably) (Debian package perl)
#               root access (for pcap stuff, and firewall/arp manipulation)
#		write access to cwd (since my IPC structure is lame)
# 		an OS that supports fork() and alarm() (Like Debian GNU/Linux)
#		libpcap by LBNL Research Group (Debian package libpcap0)
#		Net::RawIP by Sergy Kolychev (Debian package: libnet-rawip-perl)
#		Net::Pcap by Tim Potter (Debian package: libnet-pcap-perl)\
#		Some method of ensuring you don't send RSTs, so either:
#			Iptables by Rusty Russel (Debian package: iptables) 
#			Arpspoof by Dug Song (Debian package: dsniff)
#		permission from your network security officer :)
#
#
#
# Shoutouts: Franck Veysset, Olivier Courtay, and Olivier Heen of the
#            Intranode Research Team for their RING proof-of-concept
#	     <http://www.intranode.com/pdf/techno/ring-full-paper.pdf>
#            Stanislav Shanlunov for netkill.pl's and all the syntax and
#            structure I ripped off from it.
#	     <http://people.internet2.edu/~shalunov/netkill/>
#            Dug Song for dsniff and Rusty Russel for iptables
#            <http://naughty.monkey.org/~dugsong/dsniff/>
#            <http://www.netfilter.org/>
#            Mike Dausin for UAT and feature enhancements.
#            <http://www.dausin.com/>
#
# Updates and documentation: http://www.planb-security.net/wp/snacktime.html
#
# Copyright (C) 2002-2003  Tod Beardsley
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# (Some day I'll convert this and everything else to pod.)
##############################################################################

use Net::RawIP;			#For packet crafting.
use Net::Pcap;			#For packet listening.
use Socket;			#For Inet address packing.
use IO::Handle;			#For autoflush;
use Getopt::Std;		#For arguments;
use Math::BigFloat;		#For sensible floating point math
use strict;			#For masochism;

$| = 1 ;			# Turn off IO buffering (it'll mess up timing)

# Filling in all the options.

my %options;
getopts('hvVmt:p:f:w:D:s:P:I:',\%options) || usage();

# Don't bother if you're not root. Really.
my $uid = `id -u`; chomp $uid;
die "You must be root to get a snack. Sorry.\n" if $uid > 0;

if ($options{h} || not $options{t}) {
	print "Snacktime, alpha release v0.5, Jun/2003. (GNU GPL, version 2).\n";
	usage();
}

shift and die "Missing some options. Try $0 -h for help.\n";	# Complain if there's anything unexpected.

my $alarmtimeout = $options{w} || 65;			# Seconds to wait after the last syn-ack.
my $target = $options{t} || "192.168.1.1";		# Target we want to fingerprint.
my $dport  = $options{p} || 80;				# Target's listening port.
my $device = $options{D} || rdev($options{t});		# Ethernet device.
my $source = $options{s} || ${ifaddrlist()}{$device};	# Source address. Required for arpspoof method.
my $sport  = $options{P} || int rand 30000 + 1024;	# Your source port.
my $verbose = $options{v} || $options{V};		# See times, runner up scores.
my $veryverbose = $options {V};				# See scoring details and more packet info.
my $morestuff = $options{m};				# Check more stuff (winsize and ttl)

# Other variable declarations.

my $pid = $$;								# My pid, for tempfiles.
my $parentpid = getppid();						# My parent pid, for child control.
my $filter = "tcp and src host $target and dst port $sport and dst host $source";	# BPF Filter for incoming packets from $target to my $sport.

# Interrupt catcher.

$SIG{INT} = sub {
	unless (getppid() != $parentpid) {			# I'm the parent.
	die "Caught interrupt -- cleaning up.\n";
	} else {						# I'm the child.
	snackcleanup();						# Clean up temp file.
	die "\n";
	}
} ;

# Main.

die "Can't resolve $target! Quitting!\n" unless inet_aton($target);
$target = inet_ntoa(inet_aton($target));

print "It's snacktime for $target on port $dport...\n\n";

if (fork) {		# Gotta fork off a child process to listen.
	sleep 1;	# Take a little nap while the kid sets up.
	getasnack($device, $target, $dport, $source, $sport);
	wait;
} else {
	snackcatcher($alarmtimeout, $device, $filter, $target);
}

snacktimes($target);		# Calculate RTOs.

snackcleanup();			# Clean up temp file.

# Subroutines.


sub getasnack {		# This is where we construct and fire off
			# the initial syn packet to elicit some snacks.
	my $a = shift; 	# Device
	my $b = shift;	# Destination target
	my $c = shift;	# Destination port
	my $d = shift;	# Source host
	my $e = shift;	# Source port

	my $ipid = int rand 5000 + 100;
 	my $seq = int rand 4294967295;
	my @target = split(/\./,$b);			# A bunch of rigamarole to turn
	my @source = split(/\./,$d);			# strings into a network addresses.
	my $dst_ip_bin = pack("C4",@target);		# There's surely a better way to
	my $src_ip_bin = pack("C4",@source);		# go about this.
	my $dst_ip = inet_ntoa($dst_ip_bin);		#
	my $src_ip = inet_ntoa($src_ip_bin);		# Yeah, pretty annoying.


	my $packet = new Net::RawIP({tcp => {}});
	# For the future -- make this much more configurable
	$packet->set(
		{ip=> {saddr=>$src_ip, daddr=>$dst_ip, frag_off=>0,
		tos=>0, id=>$ipid},
		tcp=>{source=>$e, dest=>$c, syn=>1, window=>64240, seq=>$seq}}
		);

	fireprobe($packet);
}

sub fireprobe {			#Fire the packet off.
	my $a = shift;
	$a->send;
}


sub snackcatcher {
	my $a = shift; # Alarm timeout
	my $b = shift; # Device
	my $c = shift; # Pcap filter statement
	my $d = shift; # Target host;
	alarm $a;

# Net::Pcap listener. This is used for better accuracy when pulling the time.

	my $err;	#Error messages for Pcap setup stuff.
	if (defined $err) {
		die;		#Be sure to die on an error.
	}

# Pcap needs to provide its own source address. Which is good since we're faking it sometimes.
	my ($address, $netmask);
	if (Net::Pcap::lookupnet($b, \$address, \$netmask, \$err)) {
		die "Can't figure out the local netmask. I need this.";
	}

	my $object;
	$object = Net::Pcap::open_live($b, 1500, 0, 0, \$err);
	die "Unable to create packet capture object on ", $b, " - ", $err unless $object;

	my $filter_in;
	my $filter_c;

	my $targethost;
	$targethost="$d";

	$filter_in = "$c";
	Net::Pcap::compile(
		$object,
		\$filter_c,
		$filter_in,
		0,
		$netmask
	) && die;

	Net::Pcap::setfilter($object, $filter_c);
	Net::Pcap::loop($object, 0, \&callback, '') || die ;
	Net::Pcap::close($object);

	sub callback {
		unless (getppid() == $parentpid) {					# I'm the child.
			my ($user_data, $header, $packet) = @_;
			my ($snacktime, $snacktime_sec, $snacktime_usec);
			$snacktime_sec = $header->{'tv_sec'} ;
			$snacktime_usec = sprintf("%06d", $header->{'tv_usec'} ); 	# Eek! This method drops leading zeros! Need the sprintf to hang on to them.
			$snacktime = "$snacktime_sec.$snacktime_usec";
			my $ip_packet = unpack("H*",substr($packet,14));		# Drop LLC headers -- this assumes we're on Ethernet -- though these will be useful in the future if we want to type NICs by MAC address.
			my $ip_ttl = hex(substr($ip_packet,16,2));			# The TTL value in decimal.
			my $ip_len = hex(substr($ip_packet,4,4));			# Reported IP packet length in decimal.

			my $tcp_packet = substr($ip_packet,(substr($ip_packet,1,1)*4*2));	# Take the offset from the 2nd byte of the IP header. That *2 at the end is cause we're dealing with a unpacked hex string now.

			my $tcp_flags = hex(substr($tcp_packet,26,2));				# TCP flags in decimal.
			my $bin_tcp_flags = substr(unpack("B32",pack("N",$tcp_flags)),-8);	# Convert flags back to binary.

			my $tcp_win = hex(substr($tcp_packet,28,4));

			if ( substr($bin_tcp_flags,-2,1) && substr($bin_tcp_flags,-5,1)  ) {	# Test the SYN and ACK bits directly.
				print sprintf("%-39s", "\t$d gave me a snack! Yum!");
				print "\[SYN-ACK Time: $snacktime]" if $verbose;
				print "\n";
				open ("SNACKS", ">>tempsnacks-$pid");
				autoflush SNACKS;
				if ($morestuff) {
					print SNACKS "$ip_ttl $tcp_win\n";
				}
				print SNACKS "$snacktime\n";
				alarm $a;
			} elsif (substr($bin_tcp_flags,-3,1)) {					# Test the RST bit.
				print sprintf("%-39s", "\t$d reset the connection.");
				print "Pick a different port.\n";
				alarm 1;

			} else {
				print sprintf("%-39s", "\t$d sent something weird.");
				print "I'm outta here.\n";
				alarm 1;
			}
			if ($veryverbose) {
				print " " x 46; print "[Flags: $bin_tcp_flags] [Length: $ip_len]\n"; # Display flags and packet length.
				print " " x 46; print "[WinSize: $tcp_win] ";	# Display WinSize.
				print "[TTL: $ip_ttl]\n";
			}
		}
	}
}

sub snacktimes {
	my $a = shift;	# Target host

	open ("SNACKS" , "tempsnacks-$pid" );

	my $ttl; my $winsize;
	my @snacktimes;
	while (<SNACKS>) {
		chomp;
		push @snacktimes, $_ if /\./;
		if ($morestuff) {
			unless (/\./) {
			$ttl = "TTL:".(split)[0];
			$winsize = "WinSize:".(split)[1];
			}
		}
	}
	if ($morestuff) {
		snackmath(@snacktimes,$ttl,$winsize);
	} else {
		snackmath(@snacktimes);
	}

}

sub snackmath {
	my @datapoints = @_;
	my $datapoint; my $seen_ttl ; my $seen_winsize; my @snacktimes;
	if ($morestuff) {
		foreach $datapoint (@datapoints) {
			push (@snacktimes,$datapoint) unless $datapoint =~ /:/;
			if ($datapoint =~ /\:/) {
				$seen_winsize = $datapoint if substr($datapoint,0,1) eq "W";
				$seen_ttl = (split(/\:/,$datapoint))[1] if substr($datapoint,0,1) eq "T";
			}
		}
	} else {
		@snacktimes = @datapoints;
	}

	my $thissnack;
	my $lastsnack;
	my @rttdeltas;
	if ( $snacktimes[1]) {					# At least two snacks.
		print "\nFirst snack at:\t\t\t\t$snacktimes[0]\n" if $verbose;
		for my $current (1..@snacktimes-1) {
			$thissnack = new Math::BigFloat $snacktimes[$current];
			$lastsnack = new Math::BigFloat $snacktimes[$current-1];
			push @rttdeltas, $thissnack - $lastsnack;
			if ($verbose) {
				print "Retry $current delta: ";
				print $thissnack - $lastsnack;
				print " secs\t\t$thissnack\n";
			}
		}
	} elsif ( $snacktimes[1] == 0 ) {
		return;
	} else {
		print "Not enough snacks. I need at least two to compare.\n\n";
		return;
	}

	my $fprints = $options{f} || "./snack.fp";
	open("FINGERPRINTS",$fprints) || die "Fingerprint file missing. No analysis for you!\n";
	my @fingerprints;
	while (<FINGERPRINTS>) {
		chomp;
		push @fingerprints, $_;
	}
	my $stack;
	my $osname;
	my @knownrtts;
	my $rtt;
	my $known_winsize;
	my $known_maxttl;
	my %scores;
	my $score;

	foreach $stack (@fingerprints) {
		if ($stack =~ m/^[\w+]/) {
			if ($stack =~ /WinSize:/ && $stack =~ /MaxTTL:/) {
				($osname,$known_winsize,$known_maxttl,@knownrtts) = split (/\s+/,$stack) ;
			} else {
				($osname,@knownrtts) = split (/\s+/,$stack) ;
			}
			if (@rttdeltas == @knownrtts) {
				$score++;  # Start with a point if it's the same number of retries.
				print "\n\t$osname:\n\t--> Expected retry count for this OS. Start with a point.\n" if $veryverbose;

				if ($morestuff)	{
					if ($seen_winsize eq $known_winsize) {
						$score = $score+3;
						print "\t--> Matching WinSize for this OS. Three extra points.\n" if $veryverbose;
					}

					$known_maxttl = (split(/:/,$known_maxttl))[1];
					if ($seen_ttl <= $known_maxttl) {
						$score++;
						print "\t--> Possible TTL for this OS. Extra point.\n" if $veryverbose;
						if ($seen_ttl >= ($known_maxttl-32) ) {
							$score++;
							print "\t--> Within 32 hops of Max TTL for this OS. Extra point.\n" if $veryverbose;
						}
					}
				}

				if ($osname =~ m/^Generic/) {
					$score--; 	 # But lose a point if you're "Generic."
					print "\t--> (You're a \"Generic\" OS. Lose a point.)\n" if $veryverbose;
				}

				for $rtt (0..(@rttdeltas-1)) {
					my $subsecond;
					my @precision = qw(1 0.1 0.01 0.001 0.0001 0.00001 0.000001);
					foreach $subsecond (@precision) {
						if ( ( ($knownrtts[$rtt] - $subsecond) <= $rttdeltas[$rtt] )
							and
						     ( $rttdeltas[$rtt] <= ($knownrtts[$rtt] + $subsecond) ) ) {
							$score++; 		# Get a point if you're within so many subseconds*2 on this retry.
							if ($veryverbose) {
								print "\t--> ";
								print $knownrtts[$rtt]-$subsecond;
								print " >= ";
								print $rttdeltas[$rtt];
								print " >= ";
								print $knownrtts[$rtt]+$subsecond;
								print ": Within ". $subsecond * 2 ." secs.\n";
							}
						}
					}
				}
				$scores{$osname} = $score;
				print "\t----> Total Points: $score\n" if $veryverbose;
				undef $score;
			}
		}
	}
	unless (%scores) {
		print "\nNo good guesses! ";
		print "(Maybe try again with verbose (-v) enabled.)\n" unless $verbose;
		print "\n";
		} else {
		print "\n";
		}

	my $possible; my $winner; my $lastscore;	# Winner counters;
	my @guesses = qw(Lame Weak Alright Good Educated Solid Elite Super-Leet);
	foreach $possible (sort { $scores{$b} <=> $scores{$a} } keys %scores) {
		$winner++;

		if ( $winner == 1 ) {
			$lastscore = $scores{$possible};
			if ($scores{$possible} >= 24) {
				$scores{$possible} = 24;	# Max out the scores at 24.
			}
			print "@guesses[int($scores{$possible} / 3 )] guess: $target is $possible";
			print " ($scores{$possible})" if $verbose;
			print "\n";
		} elsif ($winner == 2 ) {
			if ($scores{$possible} > 24) {
				$scores{$possible} = 24;
			}
			if ($verbose) {
				print "Tied: $possible" if ($lastscore == $scores{$possible});
				print "Runner up (@guesses[int($scores{$possible} / 3)] guess): $possible" unless ($lastscore == $scores{$possible});
				print " ($scores{$possible})";
			}
			print "\n";
		}
	}

}

sub snackcleanup {
	unlink "tempsnacks-$pid" if (-T "tempsnacks-$pid");
}


sub usage {
die <<EOF;
Usage: snacktime.pl -t target host [-p target port] [-w secs] [-v] [-V]
 [-D dev] [-S source IP] [-P source port] [-I iptables] [-F fingerprints]
Common options:
 -t : Target device (name or IP). No ranges, yet. *REQUIRED*
 -p : Target port. It needs to be open. (default: 80)
 -w : Wait seconds for last retry. Lower it for less accuracy. (default: 65)
 -m : More stuff. Check win/ttl. Might be mangled by firewalls!
 -v : Verbose. Its use is recommended to ID unknown OSes.
 -s : Source IP. (default: real IP. Change if you're arpspoofing.)
Uncommon options:
 -D : Ethernet device for Tx/Rx (default: best guess, usually eth0)
 -P : Source port. (default: random > 1024)
 -f : Fingerprint file. (default: ./snack.fp)
 -V : Very verbose. Use to see the scoring and packet details.
 -h : This help, version info, etc.
Examples:
 snacktime.pl -t 68.163.90.12 -p 80 -v -m            # Normal usage.
 snacktime.pl -p 53 -S 53 -f fprints.txt             # Using a source port.
 snacktime.pl -t 146.82.174.12 -p 80 -s 192.168.1.10 # Using a spoofed IP.
| See README for details.       |      Copyright (c) Tod Beardsley, 2002-2003 |
EOF
}


Copyright © Tod Beardsley, 2002-2003