1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-02 04:49:02 +00:00

o [NSE] Added a sslcert library that gets and caches SSL certificates in the

registry. Modified the scripts ssl-cert and ssl-google-cert-catalog to take
  advantage of this change. [Patrik]
This commit is contained in:
patrik
2012-02-23 19:26:37 +00:00
parent dbda99204b
commit 765c5854b3
4 changed files with 265 additions and 233 deletions

View File

@@ -64,217 +64,11 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = { "default", "safe", "discovery" }
require("nmap")
require("nsedebug")
require("sslcert")
require("shortport")
require("stdnse")
require("xmpp")
local stringify_name
local date_to_string
local table_find
local s
function ftp_starttls(host, port)
-- Attempt to negotiate TLS over FTP for services that support it
-- Works for FTP (21)
-- Open a standard TCP socket
local status, error = s:connect(host, port, "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, result = s:receive_lines(1)
i = i + 1
until string.match(result, "^220") or i == 5
-- Send AUTH TLS command, ask the service to start encryption
local query = "AUTH TLS\r\n"
status = s:send(query)
status, result = s:receive_lines(1)
if not (string.match(result, "^234")) then
stdnse.print_debug("1","%s",result)
stdnse.print_debug("1","AUTH TLS 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)
result = ""
return nil
end
-- Service supports AUTH TLS, tell NSE start SSL negotiation
status, error = s:reconnect_ssl()
if not status then
stdnse.print_debug("1","Could not establish SSL session after AUTH TLS command.")
s:close()
return nil
end
end
-- Should have a solid TLS over FTP session now...
return "Connected"
end
function smtp_starttls(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, port, "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
function xmpp_starttls(host, port)
local ls = xmpp.XMPP:new(host, port, { starttls = true } )
ls.socket = s
ls.socket:set_timeout(ls.options.timeout * 1000)
local status, err = ls.socket:connect(host, port)
if not status then
return nil
end
status, err = ls:connect()
if status then
return "Connected"
end
end
-- A table mapping port numbers to specialized SSL negotiation functions.
local SPECIALIZED_FUNCS = {
[21] = ftp_starttls,
[25] = smtp_starttls,
[587] = smtp_starttls,
[5222] = xmpp_starttls,
[5269] = xmpp_starttls
}
portrule = function(host, port)
return shortport.ssl(host, port) or SPECIALIZED_FUNCS[port.number]
end
action = function(host, port)
local specialized
s = nmap.new_socket()
-- Is there a specialized function for this port?
specialized = SPECIALIZED_FUNCS[port.number]
if specialized then
local status = specialized(host, port)
if not status then
return nil
end
else
local status, error = s:connect(host, port, "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()
local lines = {}
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
if nmap.verbosity() > 0 then
lines[#lines + 1] = "Issuer: " .. stringify_name(cert.issuer)
end
if nmap.verbosity() > 0 then
lines[#lines + 1] = "Public Key type: " .. cert.pubkey.type
lines[#lines + 1] = "Public Key bits: " .. cert.pubkey.bits
end
lines[#lines + 1] = "Not valid before: " ..
date_to_string(cert.validity.notBefore)
lines[#lines + 1] = "Not valid after: " ..
date_to_string(cert.validity.notAfter)
if nmap.verbosity() > 0 then
lines[#lines + 1] = "MD5: " .. stdnse.tohex(cert:digest("md5"), { separator = " ", group = 4 })
lines[#lines + 1] = "SHA-1: " .. stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
end
if nmap.verbosity() > 1 then
lines[#lines + 1] = cert.pem
end
add_cert(host, port.number, cert)
return stdnse.strjoin("\n", lines)
return shortport.ssl(host, port) or sslcert.isPortSupported(port)
end
-- Find the index of a value in an array.
@@ -288,6 +82,17 @@ function table_find(t, value)
return nil
end
function date_to_string(date)
if not date then
return "MISSING"
end
if type(date) == "string" then
return string.format("Can't parse; string is \"%s\"", date)
else
return os.date("%Y-%m-%d %H:%M:%S", os.time(date))
end
end
-- These are the subject/issuer name fields that will be shown, in this order,
-- without a high verbosity.
local NON_VERBOSE_FIELDS = { "commonName", "organizationName",
@@ -319,25 +124,46 @@ function stringify_name(name)
return stdnse.strjoin("/", fields)
end
function date_to_string(date)
if not date then
return "MISSING"
local function parseCertificate(cert)
local lines = {}
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
if nmap.verbosity() > 0 then
lines[#lines + 1] = "Issuer: " .. stringify_name(cert.issuer)
end
if type(date) == "string" then
return string.format("Can't parse; string is \"%s\"", date)
else
return os.date("%Y-%m-%d %H:%M:%S", os.time(date))
if nmap.verbosity() > 0 then
lines[#lines + 1] = "Public Key type: " .. cert.pubkey.type
lines[#lines + 1] = "Public Key bits: " .. cert.pubkey.bits
end
lines[#lines + 1] = "Not valid before: " ..
date_to_string(cert.validity.notBefore)
lines[#lines + 1] = "Not valid after: " ..
date_to_string(cert.validity.notAfter)
if nmap.verbosity() > 0 then
lines[#lines + 1] = "MD5: " .. stdnse.tohex(cert:digest("md5"), { separator = " ", group = 4 })
lines[#lines + 1] = "SHA-1: " .. stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
end
if nmap.verbosity() > 1 then
lines[#lines + 1] = cert.pem
end
return lines
end
function add_cert(host, port, cert)
if not nmap.registry[host.ip] then
nmap.registry[host.ip] = {}
end
if not nmap.registry[host.ip][port] then
nmap.registry[host.ip][port] = {}
end
action = function(host, port)
local status, cert = sslcert.getCertificate(host, port)
if ( not(status) ) then
return
end
local lines = parseCertificate(cert)
nmap.registry[host.ip][port]["ssl-cert"] = cert
return stdnse.strjoin("\n", lines)
end