Nmap Development mailing list archives

Re: [NSE] False timestamp in ssl-date


From: Daniel Miller <bonsaiviking () gmail com>
Date: Fri, 1 Aug 2014 16:58:58 -0500

nnposter,

Thanks for another reality-based patch! I appreciate your feedback on how
Nmap's scripts are handled by a variety of implementations that we haven't
tested against. I applied this change in r33403, but had to follow it up
with a change in r33404 to remove SCRIPT_NAME from some of the stdnse.debug
calls, since stdnse.debug already includes that info.

Dan


On Fri, Aug 1, 2014 at 4:13 PM, <nnposter () users sourceforge net> wrote:

The current version of ssl-date.nse does not try to validate whether
the TLS server randomness in fact represents time. Recent versions of
OpenSSL are prominent examples where this is not true. It would be
desirable to differentiate between target clocks that are simply off
and cases where TLS does not give back the timestamp at all.

The patch below changes the script behavior as follows:

1: Take a TLS time sample
2: (original version of the script simply reports the result and exits)
3: Compare it with the scanner clock
4: If the difference is less than 15 minutes then report the time and exit
5: Otherwise take a second TLS time sample
6: Compare it with the scanner clock
7: If the two differences from steps #3 and #6 are consistent (differ in
less than 5 seconds) then report the time and exit
8: Otherwise fail the script

It could be debated whether step #4 should be implemented but for me it
was a compromise between accuracy and having to take the second sample
every single time.

I have also implemented two unrelated changes:

* Calls to stdnse.print_debug have been replaced with stdnse.debug
* Changed term "local time" to "scanner time" to avoid any confusion
with respect to local time reflecting a time zone.


Results from a "positive" target:

PORT    STATE SERVICE REASON
443/tcp open  https   syn-ack
|_ssl-date-orig: 2014-08-01T20:50:33+00:00; +1m48s from local time.
|_ssl-date: 2014-08-01T20:50:33+00:00; +1m48s from scanner time.


Results from a "negative" target:

PORT    STATE SERVICE REASON
443/tcp open  https   syn-ack
|_ssl-date-orig: 2000-10-20T18:00:16+00:00; -13y285d1h21m39s from local
time.


Results from a "negative" target with -d:

PORT    STATE SERVICE REASON
443/tcp open  https   syn-ack
|_ssl-date-orig: 1901-12-13T20:45:52+00:00; -112y230d23h12m39s from local
time.
| ssl-date:
|_  ERROR: TLS randomness does not represent time


Results from a "negative" target with -dd:

NSE: [ssl-date X.X.X.X:443] Sample #1 time difference is 1960996270 seconds
NSE: [ssl-date X.X.X.X:443] Sample #2 time difference is -84839085 seconds
PORT    STATE SERVICE REASON
443/tcp open  https   syn-ack
|_ssl-date-orig: 2091-09-04T19:19:15+00:00; +77y33d22h42m41s from local
time.
| ssl-date:
|_  ERROR: TLS randomness does not represent time



Cheers,
nnposter



Patch against revision 33394 follows:

--- scripts/ssl-date.nse.orig   2014-08-01 13:17:36.902341200 -0600
+++ scripts/ssl-date.nse        2014-08-01 14:36:16.370138900 -0600
@@ -1,6 +1,7 @@
 local shortport = require "shortport"
 local stdnse = require "stdnse"
 local table = require "table"
+local math = require "math"
 local nmap = require "nmap"
 local os = require "os"
 local string = require "string"
@@ -12,7 +13,8 @@


 In many TLS implementations, the first four bytes of server randomness
-are a Unix timestamp.
+are a Unix timestamp. The script will test whether this is indeed true
+and report the time only if it passes this test.

 Original idea by Jacob Appelbaum and his TeaTime and tlsdate tools:
 * https://github.com/ioerror/TeaTime
@@ -32,7 +34,7 @@
 -- <elem key="date">2012-08-02T18:29:31+00:00</elem>
 -- <elem key="delta">4</elem>

-author = "Aleksandar Nikolic"
+author = "Aleksandar Nikolic, nnposter"
 license = "Same as Nmap--See http://nmap.org/book/man-legal.html";
 categories = {"discovery", "safe", "default"}

@@ -75,7 +77,7 @@
     status, err = sock:connect(host, port)
     if not status then
       sock:close()
-      stdnse.print_debug("Can't send: %s", err)
+      stdnse.debug("Can't send: %s", err)
       return false
     end
   else
@@ -89,7 +91,7 @@
   -- Send Client Hello to the target server
   status, err = sock:send(cli_h)
   if not status then
-    stdnse.print_debug("Couldn't send: %s", err)
+    stdnse.debug("Couldn't send: %s", err)
     sock:close()
     return false
   end
@@ -97,7 +99,7 @@
   -- Read response
   status, response, err = tls.record_buffer(sock)
   if not status then
-    stdnse.print_debug("Couldn't receive: %s", err)
+    stdnse.debug("Couldn't receive: %s", err)
     sock:close()
     return false
   end
@@ -109,7 +111,7 @@
 local extract_time = function(response)
   local i, record = tls.record_read(response, 0)
   if record == nil then
-    stdnse.print_debug("%s: Unknown response from server", SCRIPT_NAME)
+    stdnse.debug("%s: Unknown response from server", SCRIPT_NAME)
     return nil
   end

@@ -120,27 +122,51 @@
       end
     end
   end
-  stdnse.print_debug("%s: Server response was not server_hello",
SCRIPT_NAME)
+  stdnse.debug("%s: Server response was not server_hello", SCRIPT_NAME)
   return nil
 end

-action = function(host, port)
-  local status, response
-
+local get_time_sample = function (host, port)
   -- Send crafted client hello
-  status, response = client_hello(host, port)
-  local now = os.time()
-  if status and response then
-    -- extract time from response
-    local result
-    status, result = extract_time(response)
-    if status then
-      local output = {
-        date = stdnse.format_timestamp(result, 0),
-        delta = os.difftime(result, now),
-      }
-      return output, string.format("%s; %s from local time.", output.date,
-        stdnse.format_difftime(os.date("!*t",result),os.date("!*t", now)))
+  local rstatus, response = client_hello(host, port)
+  local stm = os.time()
+  if not (rstatus and response) then return nil end
+  -- extract time from response
+  local tstatus, ttm = extract_time(response)
+  if not tstatus then return nil end
+  return ttm, stm
+end
+
+
+action = function(host, port)
+  local ttm, stm = get_time_sample(host, port)
+  if not ttm then
+    return stdnse.format_output(false, "Unable to obtain data from the
target")
+  end
+  local delta = os.difftime(ttm, stm)
+  stdnse.debug(2, "Sample #1 time difference is %d seconds", delta)
+
+  if math.abs(delta) > 15*60 then
+    -- time difference is suspect
+    -- get a second sample to determine if the clock is simply off
+    -- or if the TLS randomness does not represent the time at all
+    ttm, stm = get_time_sample(host, port)
+    if not ttm then
+      return stdnse.format_output(false, "Unable to obtain data from the
target")
+    end
+    local origdelta = delta
+    delta = os.difftime(ttm, stm)
+    stdnse.debug(2, "Sample #2 time difference is %d seconds", delta)
+    if math.abs(origdelta - delta) > 5 then
+      return stdnse.format_output(false, "TLS randomness does not
represent time")
     end
   end
+
+  local output = {
+                 date = stdnse.format_timestamp(ttm, 0),
+                 delta = delta,
+                 }
+  return output,
+         string.format("%s; %s from scanner time.", output.date,
+                 stdnse.format_difftime(os.date("!*t",ttm),os.date("!*t",
stm)))
 end


_______________________________________________
Sent through the dev mailing list
http://nmap.org/mailman/listinfo/dev
Archived at http://seclists.org/nmap-dev/

_______________________________________________
Sent through the dev mailing list
http://nmap.org/mailman/listinfo/dev
Archived at http://seclists.org/nmap-dev/


Current thread: