[Abstract]

-- ISNprober / 1.02 / Tom Vandepoel (Tom.Vandepoel@ubizen.com) --

ftp://ftp.ubizen.com/tools/isnprober-1.02.tgz

ISNprober is a tool that samples TCP Initial Sequence Numbers or IP ID's 
and can use that information to determine if a set of IP addresses belong 
to the same TCP/IP stack (machine) or not.

This tool has been written for professional penetration testing. 
If you're a kiddie, you're not going to be interested if two vulnerable
IIS's are on one and the same box, so don't waste your time on this tool.

Usage of this tool is completely at your own risk and the author
will not be held liable for any damage.

This script requires Net::RawIP which can be obtained from:
http://quake.skif.net/RawIP/ or http://www.cpan.org

Thanks to Zillion (Zillion@safemode.org) for supplying a skeleton script
to base my code on.

Thanks to HDMoore (hdm@secureaustin.com) for the excellent suggestion of 
using IP ID's instead of TCP ISN's.

The following paper my Michal Zalewski has been very helpful in 
writing this tool:
http://razor.bindview.com/publish/papers/tcpseq.html

Don't hesitate to contact me if you've got suggestions/additions.


[Background]

If you've ever tried to map out a large web farm with lots of IP
address based virtual hosting, you certainly know how tiresome it is
to find out which addresses belong to which server. 
This tool's purpose is to help you with that.

There are many ways you can cross-correlate IP addresses:
  - compare service banners and stuff like HTTP Server: headers
  - compare open services
  - compare TCP fingerprints
  - compare TCP ISN's 
  - compare IP ID's

We focus on the last 2 methods here. They work by comparing samples of ISN's 
(or IP ID's) from one target with another. If they match closely, there's 
a high degree of probability that it's the same host. This works especially 
well when these are not too random. 

In practice, you can get good results with TCP ISN's on anything not Linux or 
OpenBSD 2.9. With IP ID's, anything but OpenBSD works fine.

[Usage]

Single host mode:
 isnprober [options] <ip>|<ip:port>

Compare mode:
 isnprober [options] -c <ip1>|<ip1:port1> <ip2>|<ip2:port2>

Group mode:
 isnprober [options] -g <filename>  

-v prints version number and exit     
-n <iterations>: number of probe iterations [default = 3]
-i <interface>: network interface 
-p <default port>: default port to use if port not specified [default = 80]
-q: suppress ISN output, only display results
-w: timeout to wait for response packet (s) [default = 1]
--ipid: use IP ID's instead of TCP ISN's.
--variate-source-port: use a different source port for each packet sent
(default is to use the same source port for all probes) 

[Single host mode]

Single host mode just probes the same ip:port multiple times and outputs
the ISN's. It's main use is to give you some idea of ISN randomness.
It doesn't do any analysis itself, but I may add that in the future.

The following is the result of probing the SMTP port on a linux (2.0.x kernel)
 box:

./isnprober -n 10 172.16.1.10:25

Host:port           ISN            Delta          
172.16.1.10:25      355289795                     
172.16.1.10:25      355427002      137207         
172.16.1.10:25      355588204      161202         
172.16.1.10:25      355721297      133093         
172.16.1.10:25      356108407      387110         
172.16.1.10:25      356255587      147180         
172.16.1.10:25      356562899      307312         
172.16.1.10:25      356727299      164400         
172.16.1.10:25      356867593      140294         
172.16.1.10:25      356987558      119965         

Doesn't look too random, but that's because we're using the same 
<source ip, source port, destination ip, destination port> tuple.
If we make sure we pick a different source port for each probe, it looks
a lot more random:

./isnprober -n 10 --variate-source-port 172.16.1.10:25

Host:port           ISN            Delta          
172.16.1.10:25      1171036781                    
172.16.1.10:25      -499966651     -1671003432    
172.16.1.10:25      336400131      836366782      
172.16.1.10:25      1466122180     1129722049     
172.16.1.10:25      -604295615     -2070417795    
172.16.1.10:25      94848853       699144468      
172.16.1.10:25      -1869135745    -1963984598    
172.16.1.10:25      1138569380     3007705125     
172.16.1.10:25      -980270231     -2118839611    
172.16.1.10:25      1172376474     2152646705     

Now, lets's use IPID probes instead. While the above results show there is
at least some randomness in TCP ISN generation, the results below show
very predictable IPID's. These are SYN probes to the same open TCP port:
 
./isnprober -n 10 --ipid 172.16.1.10:23

Host:port           IPID           Delta          
172.16.1.10:23      21411                         
172.16.1.10:23      21423          12             
172.16.1.10:23      21440          17             
172.16.1.10:23      21454          14             
172.16.1.10:23      21467          13             
172.16.1.10:23      21476          9              
172.16.1.10:23      21485          9              
172.16.1.10:23      21547          62             
172.16.1.10:23      21614          67             
172.16.1.10:23      21625          11           

With IPID probes you can probe closed ports as well, but they must be closed,
not filtered, as the RST response is needed to read off the IPID.

./isnprober -n 10 --ipid 172.16.1.10:24

Host:port           IPID           Delta          
172.16.1.10:24      23109                         
172.16.1.10:24      23145          36             
172.16.1.10:24      23181          36             
172.16.1.10:24      23200          19             
172.16.1.10:24      23206          6              
172.16.1.10:24      23209          3              
172.16.1.10:24      23220          11             
172.16.1.10:24      23226          6              
172.16.1.10:24      23241          15             
172.16.1.10:24      23246          5           

When no response is received to a probe, isnprober shows an "!". This is a good
indication for packet filtering or bad connectivity.

./isnprober --ipid  172.16.1.11

Host:port           IPID           Delta          
172.16.1.11:80      !                             
172.16.1.11:80      !              !              
172.16.1.11:80      !              !             

Mind you, some TCP/IP stacks show IPID=0 on open ports, e.g. this linux 2.4.2 
box:

./isnprober --ipid  172.16.1.20:22

Host:port           IPID           Delta  
172.16.1.20:22      0                             
172.16.1.20:22      0              0              
172.16.1.20:22      0              0              

I've seen Cisco IOS do this as well. 


[Compare mode]
 
Compare mode alternates probes between two ip:port destinations and 
tries to determine if it's the same TCP/IP stack. 

The criterium it uses is very naive: if any delta between subsequent
probes is negative, it considers it the ip's to belong to a different stack.
If the delta is positive all the time, it reports the ip's to belong to 
the same stack.

E.g. probing two ip's on a single Digital Unix box will give the following 
output:

Host:port          ISN            Delta          
172.16.1.1:80      137671240                     
172.16.1.2:80      138859932      1188692        
172.16.1.1:80      139823817      963885         
172.16.1.2:80      140819682      995865         
172.16.1.1:80      141733020      913338         
172.16.1.2:80      142568341      835321         
172.16.1.1:80      143272322      703981         
172.16.1.2:80      143785983      513661         

172.16.1.1:80 	   <> 172.16.1.2:80   == [+]

Which means the overall result was positive [+]: these ip:port combinations 
are very likely part of the same IP stack. 

Note that this may lead to incorrect results on TCP/IP stacks with a 
fairly decent ISN generation algoritm, especially for negative results.
See the "Pitfalls" section further below.


[Group mode]

Group mode reads a file which contains a list of <ip:port> or <ip> entries
and tries to determine which belong to the same stack using the above
compare algorithm on an one by one basis.

At the end of the probing run, isnprober will report which ip's it thinks
belong together. It also will report which targets were unreliable (either 
the host sent a RST, or the probe did not get a response).

When run in quiet mode (-q) you get a clear summary of the results:

./isnprober.pl -n 4 -q -g list

172.16.1.17:80 	<> 172.16.1.5:21     == [-]
172.16.1.17:80	<> 172.16.1.10:54    == [-]
172.16.1.17:80 	<> 172.16.1.1:2222   == [-]
172.16.1.17:80 	<> 172.16.2.1:2222   == [-]
172.16.1.17:80 	<> 172.16.1.20:80    == [+]
172.16.1.17:80	<> 172.16.3.164:22   == [-]
172.16.1.5:21 	<> 172.16.1.10:54    == [!]
172.16.1.5:21 	<> 172.16.1.1:2222   == [-]
172.16.1.5:21 	<> 172.16.2.1:2222   == [-]
172.16.1.5:21 	<> 172.16.3.164:22   == [-]
172.16.1.10:54 	<> 172.16.1.1:2222   == [!]
172.16.1.10:54 	<> 172.16.2.1:2222   == [!]
172.16.1.10:54 	<> 172.16.3.164:22   == [!]
172.16.1.1:2222 <> 172.16.2.1:2222   == [+]
172.16.1.1:2222 <> 172.16.3.164:22   == [-]

Probable stack groups:

172.16.1.17:80 / 172.16.1.20:80
172.16.1.1:2222 / 172.16.2.1:2222
172.16.1.5:21
172.16.3.164:22

Unreliable targets:

172.16.1.10:54



[Pitfalls]

[Good/better ISN generators]

As mentioned before, our naive algorithm will not work in all cases. TCP/IP
stacks that have a more randomized ISN generator will often break our
tests.

E.g. these are output results of a compare between different ports  
on a single linux box. This is definitely the same box, but each port
appears to have its own sequence of incrementing ISN's.

./isnprober -c 172.16.1.10:25 172.16.1.10:53

Host:port           ISN            Delta          
172.16.1.10:25      1404998591                    
172.16.1.10:53      1612748951     207750360      
172.16.1.10:25      1405265681     -207483270     
172.16.1.10:53      1613023781     207758100      
172.16.1.10:25      1405587179     -207436602     
172.16.1.10:53      1613331387     207744208      

172.16.1.10:25     <> 172.16.1.10:53   == [-]

Again note that if we make sure we use a different source port for each probe
we get a lot more randomness:

./isnprober.pl --variate-source-port -c 172.16.1.10:25 172.16.1.10:53

Host:port           ISN            Delta          
172.16.1.10:25      1153142690                    
172.16.1.10:53      -2141383067    -3294525757    
172.16.1.10:25      -1019184644    1122198423     
172.16.1.10:53      -551393960     467790684      
172.16.1.10:25      -1345164418    -793770458     
172.16.1.10:53      -1723035229    -377870811     

172.16.1.10:25     <> 172.16.1.10:53   == [-]

On the other hand, don't give up hopes to quickly. The following
results are from a single NT box. There are negative deltas, isnprober
reports a negative, but these are IP's on the same box. 

This is detectable, if you look closely: some subsequent ISN's are too close 
to each other to be coincidental. 

./isnprober --variate-source-port -n 10 -c 194.20.64.40 194.20.64.41

Host:port           ISN            Delta          
172.16.1.40:80     2001071207                    
172.16.1.41:80     395914581      -1605156626    
172.16.1.40:80     395914583      2              
172.16.1.41:80     395914599      16             
172.16.1.40:80     395914621      22             
172.16.1.41:80     395914632      11             
172.16.1.40:80     2001071273     1605156641     
172.16.1.41:80     395914675      -1605156598    
172.16.1.40:80     2001071299     1605156624     
172.16.1.41:80     395914711      -1605156588    
172.16.1.40:80     2001071309     1605156598     
172.16.1.41:80     395914768      -1605156541    
172.16.1.40:80     395914819      51             
172.16.1.41:80     395914874      55             
172.16.1.40:80     2001071356     1605156482     
172.16.1.41:80     395914890      -1605156466    
172.16.1.40:80     395914903      13             
172.16.1.41:80     395914943      40             
172.16.1.40:80     2001071465     1605156522     
172.16.1.41:80     395914965      -1605156500    

Even weirder are the results when we keep the source port constant:

The results between subsequent runs will flip-flop between large 
negative/positive deltas and small positive ones:

./isnprober -n 10 -c 172.16.1.40 172.16.1.41

Host:port           ISN            Delta          
172.16.1.40:80     2001460831                    
172.16.1.41:80     396304263      -1605156568    
172.16.1.40:80     2001460844     1605156581     
172.16.1.41:80     396304317      -1605156527    
172.16.1.40:80     2001460898     1605156581     
172.16.1.41:80     396304320      -1605156578    
172.16.1.40:80     2001460910     1605156590     
172.16.1.41:80     396304346      -1605156564    
172.16.1.40:80     2001460937     1605156591     
172.16.1.41:80     396304371      -1605156566    
172.16.1.40:80     2001460962     1605156591     
172.16.1.41:80     396304402      -1605156560    
172.16.1.40:80     2001460995     1605156593     
172.16.1.41:80     396304412      -1605156583    
172.16.1.40:80     2001461004     1605156592     
172.16.1.41:80     396304441      -1605156563    
172.16.1.40:80     2001461045     1605156604     
172.16.1.41:80     396304519      -1605156526    
172.16.1.40:80     2001461104     1605156585     
172.16.1.41:80     396304546      -1605156558    

172.16.1.40:80    <> 172.16.1.41:80    == [-]

./isnprober -n 10 -c 172.16.1.40 172.16.1.41

Host:port           ISN            Delta          
172.16.1.40:80     396304670                     
172.16.1.41:80     396304677      7              
172.16.1.40:80     396304679      2              
172.16.1.41:80     396304691      12             
172.16.1.40:80     396304737      46             
172.16.1.41:80     396304743      6              
172.16.1.40:80     396304754      11             
172.16.1.41:80     396304777      23             
172.16.1.40:80     396304816      39             
172.16.1.41:80     396304838      22             
172.16.1.40:80     396304871      33             
172.16.1.41:80     396304928      57             
172.16.1.40:80     396305034      106            
172.16.1.41:80     396305066      32             
172.16.1.40:80     396305078      12             
172.16.1.41:80     396305109      31             
172.16.1.40:80     396305125      16             
172.16.1.41:80     396305141      16             
172.16.1.40:80     396305149      8              
172.16.1.41:80     396305185      36             

172.16.1.40:80    <> 172.16.1.41:80    == [+]

Very strange.

[IPID problems]

IPID's more often than not are very sequential, thus they often make a better
probe method than TCP ISN's. The only stack implementation that I've come 
across in the wild that generates random IPID's is OpenBSD.

Still, I've seen some implementations (linux 2.4 and IOS) return IPID=0 on an 
open port (see example above). In those cases, the IPID probes aren't reliable.

If there's no firewall filtering in between, your best choice is to switch to 
probing closed ports.

If you have one box returning IPID=0 and other boxes returning proper
IPID's, that is not a problem, as you'll be able to discriminate. If there are
multiple IP's returning IPID=0, they might be the same box or not.
Still, you can cross correlate with other information (service banners) to 
get an exact picture.

Also watch out for intermediate devices that return RST packets on behalf of 
the destination host:port (Firewall-1's 'reject' action). As far as I've seen
these also are IPID=0, but it is not inconceivable they return IPID's of
the intermediate filtering device instead. In that case, filtered host:port
combinations will be tagged as the same host even though they are not.


[Time synchronisation]

Strange things can happen when you're probing systems which run exactly the 
same OS versions (in this case IOS 12.0(5)) and are have their system clock 
synchronized through NTP:

./isnprober --variate-source-port -g ../list
Host:port             ISN            Delta          
172.17.2.254:23       66957759                      
172.17.3.254:23       67101795       144036         
172.17.2.254:23       67229706       127911         
172.17.3.254:23       67371827       142121         
172.17.2.254:23       67497377       125550         
172.17.3.254:23       67621852       124475         

172.17.2.254:23     <> 172.17.3.254:23     == [+]

Probing host: 172.17.2.254 on TCP port 23.
Probing host: 172.17.1.254 on TCP port 23.

Host:port             ISN            Delta          
172.17.2.254:23       67777357                      
172.17.1.254:23       67908452       131095         
172.17.2.254:23       68037547       129095         
172.17.1.254:23       68169060       131513         
172.17.2.254:23       68297756       128696         
172.17.1.254:23       68428466       130710         

172.17.2.254:23     <> 172.17.1.254:23     == [+]

Probable stack groups:

172.17.2.254:23
172.17.3.254:23
172.17.1.254:23

The sequence numbers ramp up nicely even if we variate the source port! 
I guess this could be caused by a time-dependent ISN generator.

[Proxies]

It is important to realize the impact of intermediate (proxying) firewalls 
and (transparent) proxies.

For instance, my cable provider has deployed a transparent proxy. All http 
traffic is transparently redirected to a local proxy machine. To the cable 
users it appears as though they're connecting directly to the remote website
but in reality they're speaking to the proxy's IP stack.

E.g. www.cisco.com and www.sun.com are definately two different machines;
what we're seeing here are the ISN's of my provider's proxy:

Host:port           ISN            Delta          
198.133.219.25:80   -636970563                    
192.18.97.241:80    -636458563     512000         
198.133.219.25:80   -635946563     512000         
192.18.97.241:80    -635370563     576000         
198.133.219.25:80   -634538563     832000         
192.18.97.241:80    -633450563     1088000        

198.133.219.25:80 [+] <> 192.18.97.241:80 [+] == [+]

The same effect can be produced by an intermediate device (reverse proxy, 
firewall-1 security server, plug proxy) at the server's end.

The bottom line is: don't just believe everything this tools reports. 
Always try to cross-correlate with other data. 

[Unintentional Synflooding]

Another issue is the risk of unintentional synflooding of the targets you're 
probing. Normally your own IP stack will sent a RST response back to the 
SYN-ACK you'll receive from a target. The effect is that the target won't 
be bombarded with half-open connections.

08:11:02.619162 eth0 > 172.16.3.164.1133 > 172.16.1.40.www: S 0:0(0) win 65535 (DF) [tos 0x10] 
08:11:02.672176 eth0 < 172.16.1.40.www > 172.16.3.164.1133: S 360359675:360359675(0) ack 1 win 8576 <mss 1460> (DF)
08:11:02.674466 eth0 > 172.16.3.164.1133 > 172.16.1.40.www: R 1:1(0) win 0

However, running a local packet filter might interfere with this 
"auto-cleanup". If you're blocking the SYN-ACK response to your stack (or
the RST response back for that matter), you'll be leaving half-open
connections on the target. 

You're not really synflooding the target, but on older TCP/IP stacks even a few
tens of half-open connections will block a port. So watch out for this.


[Futures]

I'm thinking of adding/changing the following elements:

-> A mode that will probe each host in a list and report IPID/TCPISN 
properties, such as IPID=0 anomalies, dependency on source port and approximate
intervals of increase, in a table like the one below:

		     ISN		IPID
172.17.17.45:80      +369k/random	+1
172.17.17.44:25      +0/+-<200		0		
172.17.17.43:25      +0/+-<200		0
172.17.17.43:465     +0/+-<200		0
172.17.17.41:995     +0/+-<200		0
172.17.17.42:993     +0/+-<200		0
172.17.17.50:443     +0/+-<200		0

This provides a nice overview and helps when cross-correlating with other
gathered data (e.g. service banners) in cases where isnprober doesn't
provide reliable results.
This could be a first step towards other functionality I'm thinking of:

-> OS fingerprinting: as Michal Zalewski's paper shows, 
ISN sequences are very dependent on OS implementation and can potentially
be used to do OS fingerprinting. Such functionality would be a logical 
extension to this tool.
 
-> The compare algorithm is quite naive. A better algorithm could be to first
do an ISN/IPID generator analysis of each target (try to figure out different 
types of generator a la nmap) and then choose an appropriate algorithm to deal
with specific generators. 



------



