mirror of
https://github.com/nmap/nmap.git
synced 2025-12-24 00:19:01 +00:00
sslcert: Try tls.lua if Nsock's SSL routines fail to connect.
This commit is contained in:
@@ -863,9 +863,12 @@ local function get_record_iter(sock)
|
||||
local i = 1
|
||||
local fragment
|
||||
return function ()
|
||||
local record
|
||||
i, record = tls.record_read(buffer, i, fragment)
|
||||
local record, more
|
||||
i, record, more = tls.record_read(buffer, i, fragment)
|
||||
if record == nil then
|
||||
if not more then
|
||||
return nil, "no more"
|
||||
end
|
||||
local status, err
|
||||
status, buffer, err = tls.record_buffer(sock, buffer, i)
|
||||
if not status then
|
||||
@@ -881,6 +884,57 @@ local function get_record_iter(sock)
|
||||
end
|
||||
end
|
||||
|
||||
local function handshake_cert (socket)
|
||||
-- logic mostly lifted from ssl-enum-ciphers
|
||||
local hello = tls.client_hello()
|
||||
local status, err = socket:send(hello)
|
||||
if not status then
|
||||
return false, "Failed to send to server"
|
||||
end
|
||||
|
||||
local get_next_record = get_record_iter(socket)
|
||||
local records = {}
|
||||
local done = false
|
||||
while not done do
|
||||
local record
|
||||
record, err = get_next_record()
|
||||
if not record then
|
||||
stdnse.debug1("no record: %s", err)
|
||||
break
|
||||
end
|
||||
-- Collect message bodies into one record per type
|
||||
records[record.type] = records[record.type] or record
|
||||
for j = 1, #record.body do -- no ipairs because we append below
|
||||
local b = record.body[j]
|
||||
done = ((record.type == "alert" and b.level == "fatal") or
|
||||
(record.type == "handshake" and b.type == "server_hello_done"))
|
||||
table.insert(records[record.type].body, b)
|
||||
end
|
||||
end
|
||||
|
||||
local handshake = records.handshake
|
||||
if not handshake then
|
||||
return false, "Server did not handshake"
|
||||
end
|
||||
|
||||
local certs
|
||||
for i, b in ipairs(handshake.body) do
|
||||
if b.type == "certificate" then
|
||||
certs = b
|
||||
break
|
||||
end
|
||||
end
|
||||
if not certs or not next(certs.certificates) then
|
||||
return false, "Server sent no certificate"
|
||||
end
|
||||
|
||||
local cert, err = parse_ssl_certificate(certs.certificates[1])
|
||||
if not cert then
|
||||
return false, ("Unable to parse cert: %s"):format(err)
|
||||
end
|
||||
return true, cert
|
||||
end
|
||||
|
||||
--- Gets a certificate for the given host and port
|
||||
-- The function will attempt to START-TLS for the ports known to require it.
|
||||
-- @param host table as received by the script action function
|
||||
@@ -901,106 +955,70 @@ function getCertificate(host, port)
|
||||
|
||||
local cert
|
||||
|
||||
local wrapper = SPECIALIZED_WRAPPED_TLS_WITHOUT_RECONNECT[port.service] or SPECIALIZED_WRAPPED_TLS_WITHOUT_RECONNECT[port.number]
|
||||
local specialized = SPECIALIZED_PREPARE_TLS[port.service] or SPECIALIZED_PREPARE_TLS[port.number]
|
||||
|
||||
local status = false
|
||||
|
||||
-- If we don't already know the service is TLS wrapped check to see if we
|
||||
-- have to use a wrapper and do a manual handshake
|
||||
local wrapper
|
||||
if not ( port.version.service_tunnel == 'ssl' ) then
|
||||
wrapper = SPECIALIZED_WRAPPED_TLS_WITHOUT_RECONNECT[port.service] or SPECIALIZED_WRAPPED_TLS_WITHOUT_RECONNECT[port.number]
|
||||
if wrapper and port.version.service_tunnel ~= 'ssl' then
|
||||
local socket
|
||||
status, socket = wrapper(host, port)
|
||||
if not status then
|
||||
stdnse.debug1("Wrapper function error: %s", socket)
|
||||
else
|
||||
status, cert = handshake_cert(socket)
|
||||
socket:close()
|
||||
end
|
||||
end
|
||||
|
||||
if wrapper then
|
||||
local status, socket = wrapper(host, port)
|
||||
-- If that didn't work, see if we need a specialized connection method
|
||||
if not status and specialized and port.version.service_tunnel ~= 'ssl' then
|
||||
local socket
|
||||
status, socket = specialized(host, port)
|
||||
if not status then
|
||||
mutex "done"
|
||||
return false, socket
|
||||
end
|
||||
|
||||
-- logic mostly lifted from ssl-enum-ciphers
|
||||
local hello = tls.client_hello()
|
||||
local status, err = socket:send(hello)
|
||||
if not status then
|
||||
mutex "done"
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
|
||||
local get_next_record = get_record_iter(socket)
|
||||
local records = {}
|
||||
while true do
|
||||
local record
|
||||
record, err = get_next_record()
|
||||
if not record then
|
||||
stdnse.debug1("no record: %s", err)
|
||||
socket:close()
|
||||
break
|
||||
end
|
||||
-- Collect message bodies into one record per type
|
||||
records[record.type] = records[record.type] or record
|
||||
local done = false
|
||||
for j = 1, #record.body do -- no ipairs because we append below
|
||||
local b = record.body[j]
|
||||
done = ((record.type == "alert" and b.level == "fatal") or
|
||||
(record.type == "handshake" and b.type == "server_hello_done"))
|
||||
table.insert(records[record.type].body, b)
|
||||
end
|
||||
if done then
|
||||
socket:close()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local handshake = records.handshake
|
||||
if not handshake then
|
||||
mutex "done"
|
||||
return false, "Server did not handshake"
|
||||
end
|
||||
|
||||
local certs
|
||||
for i, b in ipairs(handshake.body) do
|
||||
if b.type == "certificate" then
|
||||
certs = b
|
||||
break
|
||||
end
|
||||
end
|
||||
if not certs or not next(certs.certificates) then
|
||||
mutex "done"
|
||||
return false, "Server sent no certificate"
|
||||
end
|
||||
|
||||
cert, err = parse_ssl_certificate(certs.certificates[1])
|
||||
if not cert then
|
||||
mutex "done"
|
||||
return false, ("Unable to get cert: %s"):format(err)
|
||||
end
|
||||
else
|
||||
-- If we don't already know the service is TLS wrapped check to see if
|
||||
-- there a specialized function for this port
|
||||
local specialized
|
||||
if not ( port.version.service_tunnel == 'ssl' ) then
|
||||
specialized = SPECIALIZED_PREPARE_TLS[port.service] or SPECIALIZED_PREPARE_TLS[port.number]
|
||||
end
|
||||
local status
|
||||
local socket = nmap.new_socket()
|
||||
if specialized then
|
||||
status, socket = specialized(host, port)
|
||||
if not status then
|
||||
mutex "done"
|
||||
stdnse.debug1("Specialized function error: %s", socket)
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
stdnse.debug1("Specialized function error: %s", socket)
|
||||
else
|
||||
status = socket:connect(host, port, "ssl")
|
||||
if ( not(status) ) then
|
||||
mutex "done"
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
cert = socket:get_ssl_certificate()
|
||||
status = not not cert
|
||||
socket:close()
|
||||
end
|
||||
cert = socket:get_ssl_certificate()
|
||||
if ( cert == nil ) then
|
||||
mutex "done"
|
||||
return false, "Unable to get cert"
|
||||
end
|
||||
|
||||
-- Now try to connect with Nsock's SSL connection
|
||||
if not status then
|
||||
local socket = nmap.new_socket()
|
||||
local errmsg
|
||||
status, errmsg = socket:connect(host, port, "ssl")
|
||||
if not status then
|
||||
stdnse.debug1("SSL connect error: %s", errmsg)
|
||||
else
|
||||
cert = socket:get_ssl_certificate()
|
||||
status = not not cert
|
||||
socket:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Finally, try to connect and manually handshake (maybe more tolerant of TLS
|
||||
-- insecurity than OpenSSL)
|
||||
if not status then
|
||||
local socket = nmap.new_socket()
|
||||
local errmsg
|
||||
status, errmsg = socket:connect(host, port)
|
||||
if not status then
|
||||
stdnse.debug1("Connect error: %s", errmsg)
|
||||
else
|
||||
status, cert = handshake_cert(socket)
|
||||
socket:close()
|
||||
end
|
||||
end
|
||||
|
||||
if not status then
|
||||
mutex "done"
|
||||
return false, "No certificate found"
|
||||
end
|
||||
|
||||
host.registry["ssl-cert"] = host.registry["ssl-cert"] or {}
|
||||
host.registry["ssl-cert"][port.number] = host.registry["ssl-cert"][port.number] or {}
|
||||
host.registry["ssl-cert"][port.number] = cert
|
||||
|
||||
Reference in New Issue
Block a user