From 3b4fa342574add08c590807a4df3c25201e76628 Mon Sep 17 00:00:00 2001 From: tomsellers Date: Tue, 13 Oct 2009 01:19:33 +0000 Subject: [PATCH] [NSE] Modified NSE script ssl-cert.nse to support TLS negotiation against SMTP ports that support it. Depends on Patrick's addition the of the reconnect_ssl method. [Tom Sellers, David] --- CHANGELOG | 11 ++++++ scripts/ssl-cert.nse | 94 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ff7f9f15d..d40fe656a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,16 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Modified NSE script ssl-cert.nse to support TLS negotiation + against SMTP ports that support it. Depends on Patrick's addition + of the reconnect_ssl method. [Tom Sellers, David] + +o [NSE] Added the reconnect_ssl method for sockets. We sometimes need + to reconnect a socket with SSL because the initial communication on + the socket is done without SSL. See this thread [1] for more details. + [Patrick, Tom Sellers] + + [1] http://seclists.org/nmap-dev/2009/q4/3 + o [Zenmap] Fixed a crash that could occur when entering certain characters in the target entry (those whose UTF-8 encoding contains a byte that counts as whitespace in the Windows locale): diff --git a/scripts/ssl-cert.nse b/scripts/ssl-cert.nse index f489078c1..7672b95c2 100644 --- a/scripts/ssl-cert.nse +++ b/scripts/ssl-cert.nse @@ -71,24 +71,38 @@ require("stdnse") local stringify_name local date_to_string local table_find +local s local LIKELY_SSL_PORTS = { 443, 465, 989, 990, 992, 993, 994, 995, 587, 8443 } +local STARTTLS_PORTS = { 25, 587 } portrule = function(host, port) return port.version.service_tunnel == "ssl" - or table_find(LIKELY_SSL_PORTS, port.number) + or table_find(LIKELY_SSL_PORTS, port.number) or table_find(STARTTLS_PORTS, port.number) end action = function(host, port) - local s = nmap.new_socket() - local status, error = s:connect(host.ip, port.number, "ssl") - if not status then - if nmap.verbosity() > 0 then - return error - else + s = nmap.new_socket() + + if table_find(STARTTLS_PORTS, port.number) then + local status = starttls_negotiate(host,port) + + if not status then return nil end - end + else + local status, error = s:connect(host.ip, port.number, "ssl") + + if not status then + if nmap.verbosity() > 0 then + return error + else + return nil + end + end + + end + local cert = s:get_ssl_certificate() s:close() @@ -166,3 +180,67 @@ function date_to_string(date) return os.date("%Y-%m-%d %H:%M:%S", os.time(date)) end end + +function starttls_negotiate(host, port) + -- Attempt to negotiate TLS over SMTP for services that support it + -- Works for SMTP (25) and SMTP Submission (587) + + -- Open a standard TCP socket + local status, error = s:connect(host.ip, port.number, "tcp") + + if not status then + return nil + else + + -- Loop until the service presents a banner to deal with server + -- load and timing issues. There may be a better way to handle this. + local i = 0 + repeat + status, resultEHLO = s:receive_lines(1) + i = i + 1 + until string.match(resultEHLO, "^220") or i == 5 + + -- Send EHLO because the the server expects it + -- We are not going to check for STARTTLS in the capabilities + -- list, sometimes it is not advertised. + local query = "EHLO example.org\r\n" + status = s:send(query) + status, resultEHLO = s:receive_lines(1) + + if not (string.match(resultEHLO, "^250")) then + stdnse.print_debug("1","%s",resultEHLO) + stdnse.print_debug("1","EHLO with errors or timeout. Enable --script-trace to see what is happening.") + return nil + end + + resultEHLO = "" + + -- Send STARTTLS command ask the service to start encryption + local query = "STARTTLS\r\n" + status = s:send(query) + status, resultEHLO = s:receive_lines(1) + + if not (string.match(resultEHLO, "^220")) then + stdnse.print_debug("1","%s",resultEHLO) + stdnse.print_debug("1","STARTTLS failed or unavailable. Enable --script-trace to see what is happening.") + + -- Send QUIT to clean up server side connection + local query = "QUIT\r\n" + status = s:send(query) + resultEHLO = "" + + return nil + end + + -- Service supports STARTTLS, tell NSE start SSL negotiation + status, error = s:reconnect_ssl() + if not status then + stdnse.print_debug("1","Could not establish SSL session after STARTTLS command.") + s:close() + return nil + end + + end + -- Should have a solid TLS over SMTP session now... + return "Connected" +end