1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-29 02:49:01 +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 = [[
This script repeatedly initiates SSL/TLS connections, each time removing
whichever cipher or compressor was chosen by the server when making the previous
connection. The end result is a list of all the ciphers and compressors that a
server accepts.
This script repeatedly initiates SSL/TLS connections, each time trying a new
cipher or compressor while recording whether a host accepts or rejects it. The
end result is a list of all the ciphers and compressors that a server accepts.
SSLv3/TLSv1 requires more effort to determine which ciphers and compression
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
cipher and compressor chosen, or a rejection notice.
By default, the ciphers and compressors are presented in the order the server
prefers them. Passing <code>ssl-enum-ciphers.sort=name</code> as an argument to
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).
This script is intrusive since it must initiate many connections to a server,
and therefore is quite noisy.
]]
---
-- @usage
-- nmap --script ssl-enum-algorithms -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.
-- nmap --script ssl-enum-ciphers -p 443 <host>
--
-- @output
-- PORT STATE SERVICE REASON
@@ -33,46 +22,46 @@ ciphers, 1 failed cipher, 2 accepted compressors, and 1 failed compressor).
-- | sslv3-enum:
-- | SSLv3
-- | 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_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_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)
-- | uncompressed
-- | TLSv1.0
-- | 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_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_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)
-- |_ uncompressed
@@ -118,7 +107,7 @@ local SSL_SERVICES = {
"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/
PROTOCOLS = {
["SSLv3"] = 0x0300,
@@ -286,6 +275,13 @@ CIPHERS = {
["TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"] = 0x0044,
["TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"] = 0x0045,
["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_DH_DSS_WITH_AES_256_CBC_SHA256"] = 0x0068,
["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_NULL_SHA"] = 0xC039,
["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 _, b, h, j, len
local b, h, j, len
local function find_key(t, value)
local k, v
@@ -440,7 +438,7 @@ local function record_read(buffer, i)
j, h["length"] = bin.unpack(">S", buffer, j)
-- Ensure we have enough data for the body.
len = j + h["length"]
len = j + h["length"] - 1
if #buffer < len then
return i, nil
end
@@ -488,17 +486,8 @@ local function record_read(buffer, i)
end
end
-- Check for overflow beyond length.
if j > len then
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
-- Ignore unparsed bytes.
j = len
return j, h
end
@@ -529,6 +518,9 @@ local function client_hello(t)
b = ""
-- Set the protocol.
b = b .. bin.pack(">S", PROTOCOLS[t["protocol"]])
-- Set the random data.
b = b .. bin.pack(">I", os.time())
@@ -541,10 +533,12 @@ local function client_hello(t)
-- Cipher suites.
ciphers = ""
if t["ciphers"] ~= nil then
-- Add specified ciphers.
for _, cipher in pairs(t["ciphers"]) do
ciphers = ciphers .. bin.pack(">S", CIPHERS[cipher])
end
else
-- Add all known ciphers.
for _, cipher in pairs(CIPHERS) do
ciphers = ciphers .. bin.pack(">S", cipher)
end
@@ -555,11 +549,12 @@ local function client_hello(t)
-- Compression methods.
compressors = ""
if t["compressors"] ~= nil then
-- Add specified compressors.
for _, compressor in pairs(t["compressors"]) do
compressors = compressors .. bin.pack("C", COMPRESSORS[compressor])
end
else
ciphers = b .. bin.pack("C", #COMPRESSORS)
-- Add all known compressors.
for _, compressor in pairs(COMPRESSORS) do
compressors = compressors .. bin.pack("C", compressor)
end
@@ -567,9 +562,6 @@ local function client_hello(t)
b = b .. bin.pack("C", #compressors)
b = b .. compressors
-- Extensions.
b = b .. bin.pack(">S", 0)
------------
-- Header --
------------
@@ -583,14 +575,11 @@ local function client_hello(t)
len = bin.pack(">I", #b)
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)
end
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.
sock = nmap.new_socket()
@@ -614,14 +603,15 @@ local function try_params(host, port, t)
-- Read response.
i = 0
buffer = ""
record = nil
while true do
status, chunk = sock:receive()
status, resp = sock:receive()
if not status then
sock:close()
return nil
return record
end
buffer = buffer .. chunk
buffer = buffer .. resp
-- Parse response.
i, record = record_read(buffer, i)
@@ -633,56 +623,45 @@ local function try_params(host, port, t)
end
local function try_protocol(host, port, protocol)
local ciphers, compressors, result
local ciphers, compressors, results
local function find_ciphers()
local k, name, record, results, t, v
local name, protocol_worked, record, results, t
results = {}
-- Create list of ciphers.
t = {
["ciphers"] = {},
["protocol"] = protocol
}
for name, id in pairs(CIPHERS) do
table.insert(t["ciphers"], name)
end
-- Try every cipher.
protocol_worked = false
for name, _ in pairs(CIPHERS) do
-- Create structure.
t = {
["ciphers"] = {name},
["protocol"] = protocol
}
-- Create connections while removing previously accepted ciphers.
while true do
-- Try connecting with remaining ciphers.
-- Try connecting with cipher.
record = try_params(host, port, t)
if record == nil then
break
end
if record["protocol"] ~= protocol then
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
if protocol_worked then
stdnse.print_debug(2, "Cipher %s rejected.", name)
else
stdnse.print_debug(2, "Cipher %s and/or protocol %s rejected.", name, protocol)
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
@@ -690,53 +669,42 @@ local function try_protocol(host, port, protocol)
end
local function find_compressors()
local compressors, k, name, record, results, t, v
local name, protocol_worked, record, results, t
results = {}
-- Create list of compressors.
t = {
["compressors"] = {},
["protocol"] = protocol
}
for name, id in pairs(COMPRESSORS) do
table.insert(t["compressors"], name)
end
-- Try every compressor.
protocol_worked = false
for name, _ in pairs(COMPRESSORS) do
-- Create structure.
t = {
["compressors"] = {name},
["protocol"] = protocol
}
-- Create connections while removing previously accepted compressors.
while true do
-- Try connecting with remaining ciphers.
-- Try connecting with compressor.
record = try_params(host, port, t)
if record == nil then
break
end
if record["protocol"] ~= protocol then
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
if protocol_worked then
stdnse.print_debug(2, "Compressor %s rejected.", name)
else
stdnse.print_debug(2, "Compressor %s and/or protocol %s rejected.", name, protocol)
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
@@ -755,16 +723,12 @@ local function try_protocol(host, port, protocol)
compressors = find_compressors()
-- Format the cipher table.
if nmap.registry.args["ssl-enum-ciphers.sort"] == "name" then
table.sort(ciphers)
end
table.sort(ciphers)
ciphers["name"] = "Ciphers (" .. #ciphers .. ")"
table.insert(results, ciphers)
-- Format the compressor table.
if nmap.registry.args["ssl-enum-ciphers.sort"] == "name" then
table.sort(compressors)
end
table.sort(compressors)
compressors["name"] = "Compressors (" .. #compressors .. ")"
table.insert(results, compressors)
@@ -791,7 +755,7 @@ portrule = function(host, port)
end
action = function(host, port)
local result, results
local name, result, results
results = {}