mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 20:51:30 +00:00
Be more strict to avoid false positives in ssl-ccs-injection. Fixes #1322
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
#Nmap Changelog ($Id$); -*-text-*-
|
#Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE][GH#1322] Fix a few false-positive conditions in ssl-ccs-injection. TLS
|
||||||
|
implementations that responded with fatal alerts other than "unexpected
|
||||||
|
message" had been falsely marked as vulnerable. [Daniel Miller]
|
||||||
|
|
||||||
o Emergency fix to Nmap's birthday announcement so Nmap wishes itself
|
o Emergency fix to Nmap's birthday announcement so Nmap wishes itself
|
||||||
a "Happy 21st Birthday" rather than "Happy 21th" in verbose mode
|
a "Happy 21st Birthday" rather than "Happy 21th" in verbose mode
|
||||||
(-v) on September 1, 2018. [Daniel Miller]
|
(-v) on September 1, 2018. [Daniel Miller]
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ local shortport = require('shortport')
|
|||||||
local sslcert = require('sslcert')
|
local sslcert = require('sslcert')
|
||||||
local stdnse = require('stdnse')
|
local stdnse = require('stdnse')
|
||||||
local vulns = require('vulns')
|
local vulns = require('vulns')
|
||||||
local have_tls, tls = pcall(require,'tls')
|
local tls = require 'tls'
|
||||||
|
|
||||||
assert(have_tls,
|
|
||||||
"This script requires tls.lua from https://nmap.org/nsedoc/lib/tls.html")
|
|
||||||
|
|
||||||
description = [[
|
description = [[
|
||||||
Detects whether a server is vulnerable to the SSL/TLS "CCS Injection"
|
Detects whether a server is vulnerable to the SSL/TLS "CCS Injection"
|
||||||
@@ -82,6 +79,33 @@ local Error = {
|
|||||||
TIMEOUT = 4
|
TIMEOUT = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Reads an SSL/TLS record and returns true if it's any fatal
|
||||||
|
-- alert and false otherwise.
|
||||||
|
local function fatal_alert(s)
|
||||||
|
local status, buffer = tls.record_buffer(s)
|
||||||
|
if not status then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local position, record = tls.record_read(buffer, 1)
|
||||||
|
if record == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if record.type ~= "alert" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, body in ipairs(record.body) do
|
||||||
|
if body.level == "fatal" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Reads an SSL/TLS record and returns true if it's a fatal,
|
-- Reads an SSL/TLS record and returns true if it's a fatal,
|
||||||
-- 'unexpected_message' alert and false otherwise.
|
-- 'unexpected_message' alert and false otherwise.
|
||||||
@@ -114,6 +138,9 @@ end
|
|||||||
local function test_ccs_injection(host, port, version)
|
local function test_ccs_injection(host, port, version)
|
||||||
local hello = tls.client_hello({
|
local hello = tls.client_hello({
|
||||||
["protocol"] = version,
|
["protocol"] = version,
|
||||||
|
-- Only negotiate SSLv3 on its own;
|
||||||
|
-- TLS implementations may refuse to answer if SSLv3 is mentioned.
|
||||||
|
["record_protocol"] = (version == "SSLv3") and "SSLv3" or "TLSv1.0",
|
||||||
-- Claim to support every cipher
|
-- Claim to support every cipher
|
||||||
-- Doesn't work with IIS, but IIS isn't vulnerable
|
-- Doesn't work with IIS, but IIS isn't vulnerable
|
||||||
["ciphers"] = stdnse.keys(tls.CIPHERS),
|
["ciphers"] = stdnse.keys(tls.CIPHERS),
|
||||||
@@ -176,6 +203,14 @@ local function test_ccs_injection(host, port, version)
|
|||||||
stdnse.debug1("Protocol version mismatch (%s)", version)
|
stdnse.debug1("Protocol version mismatch (%s)", version)
|
||||||
s:close()
|
s:close()
|
||||||
return false, Error.PROTOCOL_MISMATCH
|
return false, Error.PROTOCOL_MISMATCH
|
||||||
|
elseif record.type == "alert" then
|
||||||
|
for _, body in ipairs(record.body) do
|
||||||
|
if body.level == "fatal" then
|
||||||
|
stdnse.debug1("Fatal alert: %s", body.description)
|
||||||
|
-- Could be something else, but this lets us retry
|
||||||
|
return false, Error.PROTOCOL_MISMATCH
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if record.type == "handshake" then
|
if record.type == "handshake" then
|
||||||
@@ -204,6 +239,17 @@ local function test_ccs_injection(host, port, version)
|
|||||||
return false, Error.SSL_HANDSHAKE
|
return false, Error.SSL_HANDSHAKE
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Optimistically read the first alert message
|
||||||
|
-- Shorter timeout: we expect most servers will bail at this point.
|
||||||
|
s:set_timeout(stdnse.get_timeout(host))
|
||||||
|
-- If we got an alert right away, we can stop right away: it's not vulnerable.
|
||||||
|
if fatal_alert(s) then
|
||||||
|
s:close()
|
||||||
|
return false, Error.NOT_VULNERABLE
|
||||||
|
end
|
||||||
|
-- Restore our slow timeout
|
||||||
|
s:set_timeout(10000)
|
||||||
|
|
||||||
-- Send the second ccs message
|
-- Send the second ccs message
|
||||||
status, err = s:send(ccs)
|
status, err = s:send(ccs)
|
||||||
if not status then
|
if not status then
|
||||||
@@ -257,8 +303,10 @@ a crafted TLS handshake, aka the "CCS Injection" vulnerability.
|
|||||||
|
|
||||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||||
|
|
||||||
-- Iterate over tls.PROTOCOLS
|
-- client hello will support multiple versions of TLS. We only retry to fall
|
||||||
for tls_version, _ in pairs(tls.PROTOCOLS) do
|
-- back to SSLv3, which some implementations won't allow in combination with
|
||||||
|
-- newer versions.
|
||||||
|
for _, tls_version in ipairs({"TLSv1.2", "SSLv3"}) do
|
||||||
local vulnerable, err = test_ccs_injection(host, port, tls_version)
|
local vulnerable, err = test_ccs_injection(host, port, tls_version)
|
||||||
|
|
||||||
-- Return an explicit message in case of a TIMEOUT,
|
-- Return an explicit message in case of a TIMEOUT,
|
||||||
|
|||||||
Reference in New Issue
Block a user