# kst-gpl
# This script was written by Michel Arboi <mikhail@nessus.org>
# GPL...
#


  desc = "
Synopsis :

The remote service implements TCP timestamps.

Description :

The remote host implements TCP timestamps, as defined by RFC1323.
A side effect of this feature is that the uptime of the remote 
host can sometimes be computed.

See also :

http://www.ietf.org/rfc/rfc1323.txt

Risk factor : 

None";


if (description)
{
  script_id(80091);;
  script_version("$Revision: 1.5 $");
  script_tag(name:"risk_factor", value:"None");

  script_name( "TCP timestamps");
  script_summary( "Look at RFC1323 TCP timestamps"); 
 
  script_description( desc);
 
  script_category(ACT_GATHER_INFO);
  script_family( "General");

  script_copyright("This script is Copyright (C) 2007 Michel Arboi");

  exit(0);
}


include("global_settings.inc");
include("network_func.inc");

if ( TARGET_IS_IPV6 ) exit(0);

function test(seq)
{
 local_var	ip, tcp, options, filter, ms, r, sport;

 sport = rand() % (65536 - 1024) + 1024;
 ip = forge_ip_packet(   ip_v: 4,	ip_hl: 5,	ip_tos: 0,
	 		ip_len: 20,	ip_id: rand(),	ip_p: IPPROTO_TCP,
			ip_ttl: 255,	ip_off: 0,	ip_src: saddr);

 options = strcat(
'\x08',			# Timestamp option
'\x0A',			# length
htonl(n: seq),		# TSVal
'\0\0\0\0',		# TSecr is invalid as ACK is not set
'\x01\x01');		# NOP padding

 tcp = forge_tcp_packet(ip: ip,	    th_sport: sport,	th_dport: dport,   
			th_flags: TH_SYN,	th_seq: rand(),
			th_ack: 0,	th_x2: 0,	th_off: 8,     
			th_win: 512,	th_urp: 0,	data: options	);


 filter = strcat('tcp and src ', daddr, ' and dst ', saddr, ' and src port ', dport, ' and dst port ', sport);
 r = send_packet(tcp, pcap_active: TRUE, pcap_filter: filter);
 ms = ms_since_midnight();

 tsval = tcp_extract_timestamp(ip: r);
 if (isnull(tsval)) return NULL;
 return make_list(ms, tsval);
}

function tcp_extract_timestamp(ip)
{
 local_var	hl, hlen, tcp, flags, opt, lo, i, n, tsval, tsecr, len;
 if (isnull(ip) || strlen(ip) < 20) return NULL;

 hl = ord(ip[0]);
 hlen = (hl & 0xF) * 4;
 tcp = substr(ip, hlen);

### dump(ddata: i, dtitle: 'IP'); dump(ddata: tcp, dtitle: 'TCP');

 if (strlen(tcp) <= 20) return NULL;
 flags = ord(tcp[14]);
 if (! (flags & TH_ACK)) return NULL;

 opt = substr(tcp, 20);
###dump(ddata: opt, dtitle: 'TCP options');
 lo = strlen(opt);
 for (i = 0; i < lo; )
 {
  n = ord(opt[i]);
  if (n == 8)	# Timestamp
  {
   tsval = ntohl(n: substr(opt, i+2, i+5));
   tsecr = ntohl(n: substr(opt, i+6, i+9));
   debug_print(level: 2, "TSVal=", tsval, " TSecr=", tsecr, "\n");
   return tsval;
  }
  else if (n == 1)	# NOP
   i ++;
  else
  {
   len = ord(opt[i+1]);
   if ( len == 0 ) break;
   i += len;
  }
 }
 return NULL;
}

function sec2ascii(txt, s)
{
 if (s < 60) return '';
 if (s < 3600)
  return strcat(txt, (s + 29) / 60, ' min');
 else if (s < 86400)
  return strcat(txt, (s + 1799) / 3600, ' hours');
 else
  return strcat(txt, (s + 23199) / 86400, ' days');
}

####

dport = get_host_open_port(); 
if (! dport) exit(0);

daddr = get_host_ip();
saddr = this_host();

v1 = test(seq: 1);

if (isnull(v1)) exit(0);

# A linear regression would not be more precise and NASL is definitely not
# designed for computation! We would need floating point.
sleep(1);	# Bigger sleep values make the test more precise

v2 = test(seq: 2);
if (isnull(v2)) exit(1); # ???
else
{
 dms = v2[0] - v1[0];
 dseq = v2[1] - v1[1];
 if (TRUE || dseq == 0 || v2[1] < 0)
 {
  security_note();
 }
 else
 {
  hz = dseq * 1000 / dms; hz0 = hz;
  # Round clock speed
  if (hz > 500) { hz = (hz + 25) / 50; hz *= 50; }
  else if (hz > 200) { hz = (hz + 5) / 10; hz *= 10; }
  else if (hz > 50) { hz = (hz + 2) / 5; hz *= 5; }
  debug_print('dms = ', dms, ' - dseq = ', dseq, ' - clockspeed = ', hz0, ' rounded = ', hz, '\n');
  uptime = v2[1] / hz;
  #uptime = v2[1] * (dms / dseq) / 1000;
  txt = '';
  txt = sec2ascii(txt: ', i.e. about ', s: uptime);
  ov = (1 << 30) / hz; ov <<= 2;
  txt = strcat(txt, '.\n\n(Note that the clock is running at about ', 
	hz, ' Hz', 
	' and will\noverflow in about ', ov, 's', 
	sec2ascii(txt: ', that is ', s: ov));
  security_note(port: port, 
	data: strcat(desc, 
		'\n\nPlugin output :\n\nThe uptime was estimated to ', 
		uptime, 's', 
		txt, ')') );
 }
}
