1
0
mirror of https://github.com/nmap/nmap.git synced 2026-02-03 12:06:35 +00:00

Changes to ssl-enum-ciphers from Mak Kolybabi. This goes back to a

slower one-at-a-time enumeration method that has proved to be more
robust. Other changes:
- Fixes the "malformed packet" bug.
- Treats RSTs as rejections, not fatal errors.
- Adds some obsolete ciphers that were requested.
  - SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
  - SSL_RSA_FIPS_WITH_DES_CBC_SHA
- Adds some other cipher definitions.
  - TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA
  - TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA
  - TLS_DHE_DSS_WITH_RC4_128_SHA
  - TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA
  - TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5
  - TLS_RSA_EXPORT1024_WITH_RC4_56_MD5
  - TLS_RSA_EXPORT1024_WITH_RC4_56_SHA
This commit is contained in:
david
2010-03-22 03:26:56 +00:00
parent 8a496e221a
commit c9e759fe0b

View File

@@ -1,31 +1,20 @@
description = [[ description = [[
This script repeatedly initiates SSL/TLS connections, each time removing This script repeatedly initiates SSL/TLS connections, each time trying a new
whichever cipher or compressor was chosen by the server when making the previous cipher or compressor while recording whether a host accepts or rejects it. The
connection. The end result is a list of all the ciphers and compressors that a end result is a list of all the ciphers and compressors that a server accepts.
server accepts.
SSLv3/TLSv1 requires more effort to determine which ciphers and compression SSLv3/TLSv1 requires more effort to determine which ciphers and compression
methods a server supports than SSLv2. A client lists the ciphers and compressors methods a server supports than SSLv2. A client lists the ciphers and compressors
that it is capable of supporting, and the server will respond with a single that it is capable of supporting, and the server will respond with a single
cipher and compressor chosen, or a rejection notice. cipher and compressor chosen, or a rejection notice.
By default, the ciphers and compressors are presented in the order the server This script is intrusive since it must initiate many connections to a server,
prefers them. Passing <code>ssl-enum-ciphers.sort=name</code> as an argument to and therefore is quite noisy.
this script will instead order them alphabetically, which is more useful for
comparing cipher lists between servers.
This script is intrusive since it must initiate many connections so a server,
and therefore is quite noisy. For a server that supports 10 ciphers and 2
compressors, this script will need to initiate 14 connections (10 successful
ciphers, 1 failed cipher, 2 accepted compressors, and 1 failed compressor).
]] ]]
--- ---
-- @usage -- @usage
-- nmap --script ssl-enum-algorithms -p 443 <host> -- nmap --script ssl-enum-ciphers -p 443 <host>
--
-- @args ssl-enum-ciphers.sort If set to <code>name</code>, sort results
-- alphabetically instead of in the order in which they were accepted.
-- --
-- @output -- @output
-- PORT STATE SERVICE REASON -- PORT STATE SERVICE REASON
@@ -33,46 +22,46 @@ ciphers, 1 failed cipher, 2 accepted compressors, and 1 failed compressor).
-- | sslv3-enum: -- | sslv3-enum:
-- | SSLv3 -- | SSLv3
-- | Ciphers (18) -- | Ciphers (18)
-- | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
-- | TLS_RSA_EXPORT_WITH_RC4_40_MD5
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_RSA_WITH_AES_128_CBC_SHA -- | TLS_RSA_WITH_AES_128_CBC_SHA
-- | TLS_RSA_WITH_AES_256_CBC_SHA -- | TLS_RSA_WITH_AES_256_CBC_SHA
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
-- | TLS_RSA_WITH_RC4_128_MD5
-- | TLS_RSA_EXPORT_WITH_RC4_40_MD5
-- | TLS_DHE_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA
-- | TLS_RSA_WITH_RC4_128_SHA
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA
-- | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -- | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA -- | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_WITH_RC4_128_MD5
-- | TLS_RSA_WITH_RC4_128_SHA
-- | Compressors (1) -- | Compressors (1)
-- | uncompressed -- | uncompressed
-- | TLSv1.0 -- | TLSv1.0
-- | Ciphers (18) -- | Ciphers (18)
-- | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
-- | TLS_RSA_EXPORT_WITH_RC4_40_MD5
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_RSA_WITH_AES_128_CBC_SHA -- | TLS_RSA_WITH_AES_128_CBC_SHA
-- | TLS_RSA_WITH_AES_256_CBC_SHA -- | TLS_RSA_WITH_AES_256_CBC_SHA
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
-- | TLS_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
-- | TLS_RSA_WITH_RC4_128_MD5
-- | TLS_RSA_EXPORT_WITH_RC4_40_MD5
-- | TLS_DHE_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
-- | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA
-- | TLS_RSA_WITH_RC4_128_SHA
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA
-- | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA -- | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
-- | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA -- | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
-- | TLS_RSA_WITH_DES_CBC_SHA
-- | TLS_RSA_WITH_RC4_128_MD5
-- | TLS_RSA_WITH_RC4_128_SHA
-- | Compressors (1) -- | Compressors (1)
-- |_ uncompressed -- |_ uncompressed
@@ -118,7 +107,7 @@ local SSL_SERVICES = {
"telnets" "telnets"
} }
-- All of the values in the tables below are from: -- Most of the values in the tables below are from:
-- http://www.iana.org/assignments/tls-parameters/ -- http://www.iana.org/assignments/tls-parameters/
PROTOCOLS = { PROTOCOLS = {
["SSLv3"] = 0x0300, ["SSLv3"] = 0x0300,
@@ -286,6 +275,13 @@ CIPHERS = {
["TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"] = 0x0044, ["TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"] = 0x0044,
["TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"] = 0x0045, ["TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"] = 0x0045,
["TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"] = 0x0046, ["TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"] = 0x0046,
["TLS_RSA_EXPORT1024_WITH_RC4_56_MD5"] = 0x0060,
["TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5"] = 0x0061,
["TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"] = 0x0062,
["TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"] = 0x0063,
["TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"] = 0x0064,
["TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA"] = 0x0065,
["TLS_DHE_DSS_WITH_RC4_128_SHA"] = 0x0066,
["TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"] = 0x0067, ["TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"] = 0x0067,
["TLS_DH_DSS_WITH_AES_256_CBC_SHA256"] = 0x0068, ["TLS_DH_DSS_WITH_AES_256_CBC_SHA256"] = 0x0068,
["TLS_DH_RSA_WITH_AES_256_CBC_SHA256"] = 0x0069, ["TLS_DH_RSA_WITH_AES_256_CBC_SHA256"] = 0x0069,
@@ -406,11 +402,13 @@ CIPHERS = {
["TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"] = 0xC038, ["TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"] = 0xC038,
["TLS_ECDHE_PSK_WITH_NULL_SHA"] = 0xC039, ["TLS_ECDHE_PSK_WITH_NULL_SHA"] = 0xC039,
["TLS_ECDHE_PSK_WITH_NULL_SHA256"] = 0xC03A, ["TLS_ECDHE_PSK_WITH_NULL_SHA256"] = 0xC03A,
["TLS_ECDHE_PSK_WITH_NULL_SHA384"] = 0xC03B ["TLS_ECDHE_PSK_WITH_NULL_SHA384"] = 0xC03B,
["SSL_RSA_FIPS_WITH_DES_CBC_SHA"] = 0xFEFE,
["SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"] = 0xFEFF
} }
local function record_read(buffer, i) local function record_read(buffer, i)
local _, b, h, j, len local b, h, j, len
local function find_key(t, value) local function find_key(t, value)
local k, v local k, v
@@ -440,7 +438,7 @@ local function record_read(buffer, i)
j, h["length"] = bin.unpack(">S", buffer, j) j, h["length"] = bin.unpack(">S", buffer, j)
-- Ensure we have enough data for the body. -- Ensure we have enough data for the body.
len = j + h["length"] len = j + h["length"] - 1
if #buffer < len then if #buffer < len then
return i, nil return i, nil
end end
@@ -488,17 +486,8 @@ local function record_read(buffer, i)
end end
end end
-- Check for overflow beyond length. -- Ignore unparsed bytes.
if j > len then j = len
stdnse.print_debug(2, "SSL: Parsing error, body %d bytes larger than header declares.", j - len)
j = len
end
-- Consume any unparsed bytes in record body.
if j < len then
stdnse.print_debug(2, "SSL: Parsing error, ignoring %d unused bytes in body.", len - j)
j = len
end
return j, h return j, h
end end
@@ -529,6 +518,9 @@ local function client_hello(t)
b = "" b = ""
-- Set the protocol.
b = b .. bin.pack(">S", PROTOCOLS[t["protocol"]])
-- Set the random data. -- Set the random data.
b = b .. bin.pack(">I", os.time()) b = b .. bin.pack(">I", os.time())
@@ -541,10 +533,12 @@ local function client_hello(t)
-- Cipher suites. -- Cipher suites.
ciphers = "" ciphers = ""
if t["ciphers"] ~= nil then if t["ciphers"] ~= nil then
-- Add specified ciphers.
for _, cipher in pairs(t["ciphers"]) do for _, cipher in pairs(t["ciphers"]) do
ciphers = ciphers .. bin.pack(">S", CIPHERS[cipher]) ciphers = ciphers .. bin.pack(">S", CIPHERS[cipher])
end end
else else
-- Add all known ciphers.
for _, cipher in pairs(CIPHERS) do for _, cipher in pairs(CIPHERS) do
ciphers = ciphers .. bin.pack(">S", cipher) ciphers = ciphers .. bin.pack(">S", cipher)
end end
@@ -555,11 +549,12 @@ local function client_hello(t)
-- Compression methods. -- Compression methods.
compressors = "" compressors = ""
if t["compressors"] ~= nil then if t["compressors"] ~= nil then
-- Add specified compressors.
for _, compressor in pairs(t["compressors"]) do for _, compressor in pairs(t["compressors"]) do
compressors = compressors .. bin.pack("C", COMPRESSORS[compressor]) compressors = compressors .. bin.pack("C", COMPRESSORS[compressor])
end end
else else
ciphers = b .. bin.pack("C", #COMPRESSORS) -- Add all known compressors.
for _, compressor in pairs(COMPRESSORS) do for _, compressor in pairs(COMPRESSORS) do
compressors = compressors .. bin.pack("C", compressor) compressors = compressors .. bin.pack("C", compressor)
end end
@@ -567,9 +562,6 @@ local function client_hello(t)
b = b .. bin.pack("C", #compressors) b = b .. bin.pack("C", #compressors)
b = b .. compressors b = b .. compressors
-- Extensions.
b = b .. bin.pack(">S", 0)
------------ ------------
-- Header -- -- Header --
------------ ------------
@@ -583,14 +575,11 @@ local function client_hello(t)
len = bin.pack(">I", #b) len = bin.pack(">I", #b)
h = h .. bin.pack("CCC", len:byte(2), len:byte(3), len:byte(4)) h = h .. bin.pack("CCC", len:byte(2), len:byte(3), len:byte(4))
-- Set the protocol.
h = h .. bin.pack(">S", PROTOCOLS[t["protocol"]])
return record_write("handshake", t["protocol"], h .. b) return record_write("handshake", t["protocol"], h .. b)
end end
local function try_params(host, port, t) local function try_params(host, port, t)
local buffer, err, i, kind, length, record, req, resp, sock, status local buffer, err, i, record, req, resp, sock, status
-- Create socket. -- Create socket.
sock = nmap.new_socket() sock = nmap.new_socket()
@@ -614,14 +603,15 @@ local function try_params(host, port, t)
-- Read response. -- Read response.
i = 0 i = 0
buffer = "" buffer = ""
record = nil
while true do while true do
status, chunk = sock:receive() status, resp = sock:receive()
if not status then if not status then
sock:close() sock:close()
return nil return record
end end
buffer = buffer .. chunk buffer = buffer .. resp
-- Parse response. -- Parse response.
i, record = record_read(buffer, i) i, record = record_read(buffer, i)
@@ -633,56 +623,45 @@ local function try_params(host, port, t)
end end
local function try_protocol(host, port, protocol) local function try_protocol(host, port, protocol)
local ciphers, compressors, result local ciphers, compressors, results
local function find_ciphers() local function find_ciphers()
local k, name, record, results, t, v local name, protocol_worked, record, results, t
results = {} results = {}
-- Create list of ciphers. -- Try every cipher.
t = { protocol_worked = false
["ciphers"] = {}, for name, _ in pairs(CIPHERS) do
["protocol"] = protocol -- Create structure.
} t = {
for name, id in pairs(CIPHERS) do ["ciphers"] = {name},
table.insert(t["ciphers"], name) ["protocol"] = protocol
end }
-- Create connections while removing previously accepted ciphers. -- Try connecting with cipher.
while true do
-- Try connecting with remaining ciphers.
record = try_params(host, port, t) record = try_params(host, port, t)
if record == nil then if record == nil then
break if protocol_worked then
end stdnse.print_debug(2, "Cipher %s rejected.", name)
else
if record["protocol"] ~= protocol then stdnse.print_debug(2, "Cipher %s and/or protocol %s rejected.", name, protocol)
stdnse.print_debug(1, "Protocol not supported, server using %s.", record["protocol"])
break
end
if record["type"] == "alert" and record["body"]["description"] == "handshake_failure" then
stdnse.print_debug(2, "No ciphers chosen.")
break
end
if record["type"] ~= "handshake" or record["body"]["type"] ~= "server_hello" then
stdnse.print_debug(2, "Unexpected record received.")
break
end
-- Add cipher to the list of accepted ciphers.
name = record["body"]["cipher"]
table.insert(results, name)
stdnse.print_debug(2, "Cipher %s chosen.", name)
-- Remove cipher from list for next attempt.
for k, v in pairs(t["ciphers"]) do
if v == name then
table.remove(t["ciphers"], k)
break
end end
elseif record["protocol"] ~= protocol then
stdnse.print_debug(1, "Protocol %s rejected.", protocol)
break
elseif record["type"] == "alert" and record["body"]["description"] == "handshake_failure" then
protocol_worked = true
stdnse.print_debug(2, "Cipher %s rejected.", name)
elseif record["type"] ~= "handshake" or record["body"]["type"] ~= "server_hello" then
stdnse.print_debug(2, "Unexpected record received.")
else
protocol_worked = true
stdnse.print_debug(2, "Cipher %s chosen.", name)
-- Add cipher to the list of accepted ciphers.
name = record["body"]["cipher"]
table.insert(results, name)
end end
end end
@@ -690,53 +669,42 @@ local function try_protocol(host, port, protocol)
end end
local function find_compressors() local function find_compressors()
local compressors, k, name, record, results, t, v local name, protocol_worked, record, results, t
results = {} results = {}
-- Create list of compressors. -- Try every compressor.
t = { protocol_worked = false
["compressors"] = {}, for name, _ in pairs(COMPRESSORS) do
["protocol"] = protocol -- Create structure.
} t = {
for name, id in pairs(COMPRESSORS) do ["compressors"] = {name},
table.insert(t["compressors"], name) ["protocol"] = protocol
end }
-- Create connections while removing previously accepted compressors. -- Try connecting with compressor.
while true do
-- Try connecting with remaining ciphers.
record = try_params(host, port, t) record = try_params(host, port, t)
if record == nil then if record == nil then
break if protocol_worked then
end stdnse.print_debug(2, "Compressor %s rejected.", name)
else
if record["protocol"] ~= protocol then stdnse.print_debug(2, "Compressor %s and/or protocol %s rejected.", name, protocol)
stdnse.print_debug(1, "Protocol not supported, server using %s.", record["protocol"])
break
end
if record["type"] == "alert" and record["body"]["description"] == "handshake_failure" then
stdnse.print_debug(2, "No compressors chosen.")
break
end
if record["type"] ~= "handshake" or record["body"]["type"] ~= "server_hello" then
stdnse.print_debug(2, "Unexpected record received.")
break
end
-- Add compressor to the list of accepted compressors.
name = record["body"]["compressor"]
table.insert(results, name)
stdnse.print_debug(2, "Compressor %s chosen.", name)
-- Remove compressor from list for next attempt.
for k, v in pairs(t["compressors"]) do
if v == name then
table.remove(t["compressors"], k)
break
end end
elseif record["protocol"] ~= protocol then
stdnse.print_debug(1, "Protocol %s rejected.", protocol)
break
elseif record["type"] == "alert" and record["body"]["description"] == "handshake_failure" then
protocol_worked = true
stdnse.print_debug(2, "Compressor %s rejected.", name)
elseif record["type"] ~= "handshake" or record["body"]["type"] ~= "server_hello" then
stdnse.print_debug(2, "Unexpected record received.")
else
protocol_worked = true
stdnse.print_debug(2, "Compressor %s chosen.", name)
-- Add compressor to the list of accepted compressors.
name = record["body"]["compressor"]
table.insert(results, name)
end end
end end
@@ -755,16 +723,12 @@ local function try_protocol(host, port, protocol)
compressors = find_compressors() compressors = find_compressors()
-- Format the cipher table. -- Format the cipher table.
if nmap.registry.args["ssl-enum-ciphers.sort"] == "name" then table.sort(ciphers)
table.sort(ciphers)
end
ciphers["name"] = "Ciphers (" .. #ciphers .. ")" ciphers["name"] = "Ciphers (" .. #ciphers .. ")"
table.insert(results, ciphers) table.insert(results, ciphers)
-- Format the compressor table. -- Format the compressor table.
if nmap.registry.args["ssl-enum-ciphers.sort"] == "name" then table.sort(compressors)
table.sort(compressors)
end
compressors["name"] = "Compressors (" .. #compressors .. ")" compressors["name"] = "Compressors (" .. #compressors .. ")"
table.insert(results, compressors) table.insert(results, compressors)
@@ -791,7 +755,7 @@ portrule = function(host, port)
end end
action = function(host, port) action = function(host, port)
local result, results local name, result, results
results = {} results = {}