local bin = require "bin"
local coroutine = require "coroutine"
local io = require "io"
local nmap = require "nmap"
local os = require "os"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
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.
Each cipher is shown with a strength rating: one of strong
,
weak
, or unknown strength
. The output line
beginning with Least strength
shows the strength of the
weakest cipher offered. If you are auditing for weak ciphers, you would
want to look more closely at any port where Least strength
is not strong
. The cipher strength database is in the file
nselib/data/ssl-ciphers
, or you can use a different file
through the script argument
ssl-enum-ciphers.rankedcipherlist
.
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.
This script is intrusive since it must initiate many connections to a server,
and therefore is quite noisy.
]]
---
-- @usage
-- nmap --script ssl-enum-ciphers -p 443
--
-- @args ssl-enum-ciphers.rankedcipherlist A path to a file of cipher names and strength ratings
--
-- @output
-- PORT STATE SERVICE REASON
-- 443/tcp open https syn-ack
-- | ssl-enum-ciphers:
-- | SSLv3
-- | Ciphers (6)
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA - unknown strength
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA - unknown strength
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong
-- | TLS_RSA_WITH_AES_128_CBC_SHA - strong
-- | TLS_RSA_WITH_AES_256_CBC_SHA - unknown strength
-- | Compressors (1)
-- | uncompressed
-- | TLSv1.0
-- | Ciphers (6)
-- | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA - unknown strength
-- | TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong
-- | TLS_DHE_RSA_WITH_AES_256_CBC_SHA - unknown strength
-- | TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong
-- | TLS_RSA_WITH_AES_128_CBC_SHA - strong
-- | TLS_RSA_WITH_AES_256_CBC_SHA - unknown strength
-- | Compressors (1)
-- | uncompressed
-- |_ Least strength = unknown strength
author = "Mak Kolybabi , Gabriel Lawrence"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
-- Most of the values in the tables below are from:
-- http://www.iana.org/assignments/tls-parameters/
PROTOCOLS = {
["SSLv3"] = 0x0300,
["TLSv1.0"] = 0x0301,
["TLSv1.1"] = 0x0302,
["TLSv1.2"] = 0x0303
}
--
-- TLS Record Types
--
TLS_RECORD_HEADER_LENGTH = 5
TLS_CONTENTTYPE_REGISTRY = {
["change_cipher_spec"] = 20,
["alert"] = 21,
["handshake"] = 22,
["application_data"] = 23
}
--
-- TLS Alert Levels
--
TLS_ALERT_LEVELS = {
["warning"] = 1,
["fatal"] = 2,
}
--
-- TLS Alert Record Types
--
TLS_ALERT_REGISTRY = {
["close_notify"] = 0,
["unexpected_message"] = 10,
["bad_record_mac"] = 20,
["decryption_failed"] = 21,
["record_overflow"] = 22,
["decompression_failure"] = 30,
["handshake_failure"] = 40,
["no_certificate"] = 41,
["bad_certificate"] = 42,
["unsupported_certificate"] = 43,
["certificate_revoked"] = 44,
["certificate_expired"] = 45,
["certificate_unknown"] = 46,
["illegal_parameter"] = 47,
["unknown_ca"] = 48,
["access_denied"] = 49,
["decode_error"] = 50,
["decrypt_error"] = 51,
["export_restriction"] = 60,
["protocol_version"] = 70,
["insufficient_security"] = 71,
["internal_error"] = 80,
["user_canceled"] = 90,
["no_renegotiation"] = 100,
["unsupported_extension"] = 110,
["certificate_unobtainable"] = 111,
["unrecognized_name"] = 112,
["bad_certificate_status_response"] = 113,
["bad_certificate_hash_value"] = 114,
["unknown_psk_identity"] = 115
}
--
-- TLS Handshake Record Types
--
TLS_HANDSHAKETYPE_REGISTRY = {
["hello_request"] = 0,
["client_hello"] = 1,
["server_hello"] = 2,
["hello_verify_request"] = 3,
["NewSessionTicket"] = 4,
["certificate"] = 11,
["server_key_exchange"] = 12,
["certificate_request"] = 13,
["server_hello_done"] = 14,
["certificate_verify"] = 15,
["client_key_exchange"] = 16,
["finished"] = 20,
["certificate_url"] = 21,
["certificate_status"] = 22,
["supplemental_data"] = 23
}
--
-- Compression Algorithms
-- http://www.iana.org/assignments/comp-meth-ids
--
COMPRESSORS = {
["NULL"] = 0,
["DEFLATE"] = 1,
["LZS"] = 64
}
--
-- Encryption Algorithms
--
CIPHERS = {
["TLS_NULL_WITH_NULL_NULL"] = 0x0000,
["TLS_RSA_WITH_NULL_MD5"] = 0x0001,
["TLS_RSA_WITH_NULL_SHA"] = 0x0002,
["TLS_RSA_EXPORT_WITH_RC4_40_MD5"] = 0x0003,
["TLS_RSA_WITH_RC4_128_MD5"] = 0x0004,
["TLS_RSA_WITH_RC4_128_SHA"] = 0x0005,
["TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"] = 0x0006,
["TLS_RSA_WITH_IDEA_CBC_SHA"] = 0x0007,
["TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"] = 0x0008,
["TLS_RSA_WITH_DES_CBC_SHA"] = 0x0009,
["TLS_RSA_WITH_3DES_EDE_CBC_SHA"] = 0x000A,
["TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"] = 0x000B,
["TLS_DH_DSS_WITH_DES_CBC_SHA"] = 0x000C,
["TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"] = 0x000D,
["TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"] = 0x000E,
["TLS_DH_RSA_WITH_DES_CBC_SHA"] = 0x000F,
["TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"] = 0x0010,
["TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"] = 0x0011,
["TLS_DHE_DSS_WITH_DES_CBC_SHA"] = 0x0012,
["TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"] = 0x0013,
["TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"] = 0x0014,
["TLS_DHE_RSA_WITH_DES_CBC_SHA"] = 0x0015,
["TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"] = 0x0016,
["TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"] = 0x0017,
["TLS_DH_anon_WITH_RC4_128_MD5"] = 0x0018,
["TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"] = 0x0019,
["TLS_DH_anon_WITH_DES_CBC_SHA"] = 0x001A,
["TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"] = 0x001B,
["SSL_FORTEZZA_KEA_WITH_NULL_SHA"] = 0x001C,
["SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"] = 0x001D,
["TLS_KRB5_WITH_DES_CBC_SHA"] = 0x001E,
["TLS_KRB5_WITH_3DES_EDE_CBC_SHA"] = 0x001F,
["TLS_KRB5_WITH_RC4_128_SHA"] = 0x0020,
["TLS_KRB5_WITH_IDEA_CBC_SHA"] = 0x0021,
["TLS_KRB5_WITH_DES_CBC_MD5"] = 0x0022,
["TLS_KRB5_WITH_3DES_EDE_CBC_MD5"] = 0x0023,
["TLS_KRB5_WITH_RC4_128_MD5"] = 0x0024,
["TLS_KRB5_WITH_IDEA_CBC_MD5"] = 0x0025,
["TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"] = 0x0026,
["TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"] = 0x0027,
["TLS_KRB5_EXPORT_WITH_RC4_40_SHA"] = 0x0028,
["TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"] = 0x0029,
["TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"] = 0x002A,
["TLS_KRB5_EXPORT_WITH_RC4_40_MD5"] = 0x002B,
["TLS_PSK_WITH_NULL_SHA"] = 0x002C,
["TLS_DHE_PSK_WITH_NULL_SHA"] = 0x002D,
["TLS_RSA_PSK_WITH_NULL_SHA"] = 0x002E,
["TLS_RSA_WITH_AES_128_CBC_SHA"] = 0x002F,
["TLS_DH_DSS_WITH_AES_128_CBC_SHA"] = 0x0030,
["TLS_DH_RSA_WITH_AES_128_CBC_SHA"] = 0x0031,
["TLS_DHE_DSS_WITH_AES_128_CBC_SHA"] = 0x0032,
["TLS_DHE_RSA_WITH_AES_128_CBC_SHA"] = 0x0033,
["TLS_DH_anon_WITH_AES_128_CBC_SHA"] = 0x0034,
["TLS_RSA_WITH_AES_256_CBC_SHA"] = 0x0035,
["TLS_DH_DSS_WITH_AES_256_CBC_SHA"] = 0x0036,
["TLS_DH_RSA_WITH_AES_256_CBC_SHA"] = 0x0037,
["TLS_DHE_DSS_WITH_AES_256_CBC_SHA"] = 0x0038,
["TLS_DHE_RSA_WITH_AES_256_CBC_SHA"] = 0x0039,
["TLS_DH_anon_WITH_AES_256_CBC_SHA"] = 0x003A,
["TLS_RSA_WITH_NULL_SHA256"] = 0x003B,
["TLS_RSA_WITH_AES_128_CBC_SHA256"] = 0x003C,
["TLS_RSA_WITH_AES_256_CBC_SHA256"] = 0x003D,
["TLS_DH_DSS_WITH_AES_128_CBC_SHA256"] = 0x003E,
["TLS_DH_RSA_WITH_AES_128_CBC_SHA256"] = 0x003F,
["TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"] = 0x0040,
["TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"] = 0x0041,
["TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"] = 0x0042,
["TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"] = 0x0043,
["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,
["TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"] = 0x006A,
["TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"] = 0x006B,
["TLS_DH_anon_WITH_AES_128_CBC_SHA256"] = 0x006C,
["TLS_DH_anon_WITH_AES_256_CBC_SHA256"] = 0x006D,
["TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"] = 0x0084,
["TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"] = 0x0085,
["TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"] = 0x0086,
["TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"] = 0x0087,
["TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"] = 0x0088,
["TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"] = 0x0089,
["TLS_PSK_WITH_RC4_128_SHA"] = 0x008A,
["TLS_PSK_WITH_3DES_EDE_CBC_SHA"] = 0x008B,
["TLS_PSK_WITH_AES_128_CBC_SHA"] = 0x008C,
["TLS_PSK_WITH_AES_256_CBC_SHA"] = 0x008D,
["TLS_DHE_PSK_WITH_RC4_128_SHA"] = 0x008E,
["TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"] = 0x008F,
["TLS_DHE_PSK_WITH_AES_128_CBC_SHA"] = 0x0090,
["TLS_DHE_PSK_WITH_AES_256_CBC_SHA"] = 0x0091,
["TLS_RSA_PSK_WITH_RC4_128_SHA"] = 0x0092,
["TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"] = 0x0093,
["TLS_RSA_PSK_WITH_AES_128_CBC_SHA"] = 0x0094,
["TLS_RSA_PSK_WITH_AES_256_CBC_SHA"] = 0x0095,
["TLS_RSA_WITH_SEED_CBC_SHA"] = 0x0096,
["TLS_DH_DSS_WITH_SEED_CBC_SHA"] = 0x0097,
["TLS_DH_RSA_WITH_SEED_CBC_SHA"] = 0x0098,
["TLS_DHE_DSS_WITH_SEED_CBC_SHA"] = 0x0099,
["TLS_DHE_RSA_WITH_SEED_CBC_SHA"] = 0x009A,
["TLS_DH_anon_WITH_SEED_CBC_SHA"] = 0x009B,
["TLS_RSA_WITH_AES_128_GCM_SHA256"] = 0x009C,
["TLS_RSA_WITH_AES_256_GCM_SHA384"] = 0x009D,
["TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"] = 0x009E,
["TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"] = 0x009F,
["TLS_DH_RSA_WITH_AES_128_GCM_SHA256"] = 0x00A0,
["TLS_DH_RSA_WITH_AES_256_GCM_SHA384"] = 0x00A1,
["TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"] = 0x00A2,
["TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"] = 0x00A3,
["TLS_DH_DSS_WITH_AES_128_GCM_SHA256"] = 0x00A4,
["TLS_DH_DSS_WITH_AES_256_GCM_SHA384"] = 0x00A5,
["TLS_DH_anon_WITH_AES_128_GCM_SHA256"] = 0x00A6,
["TLS_DH_anon_WITH_AES_256_GCM_SHA384"] = 0x00A7,
["TLS_PSK_WITH_AES_128_GCM_SHA256"] = 0x00A8,
["TLS_PSK_WITH_AES_256_GCM_SHA384"] = 0x00A9,
["TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"] = 0x00AA,
["TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"] = 0x00AB,
["TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"] = 0x00AC,
["TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"] = 0x00AD,
["TLS_PSK_WITH_AES_128_CBC_SHA256"] = 0x00AE,
["TLS_PSK_WITH_AES_256_CBC_SHA384"] = 0x00AF,
["TLS_PSK_WITH_NULL_SHA256"] = 0x00B0,
["TLS_PSK_WITH_NULL_SHA384"] = 0x00B1,
["TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"] = 0x00B2,
["TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"] = 0x00B3,
["TLS_DHE_PSK_WITH_NULL_SHA256"] = 0x00B4,
["TLS_DHE_PSK_WITH_NULL_SHA384"] = 0x00B5,
["TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"] = 0x00B6,
["TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"] = 0x00B7,
["TLS_RSA_PSK_WITH_NULL_SHA256"] = 0x00B8,
["TLS_RSA_PSK_WITH_NULL_SHA384"] = 0x00B9,
["TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BA,
["TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BB,
["TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BC,
["TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BD,
["TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BE,
["TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"] = 0x00BF,
["TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C0,
["TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C1,
["TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C2,
["TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C3,
["TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C4,
["TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"] = 0x00C5,
["TLS_RENEGO_PROTECTION_REQUEST"] = 0x00FF,
["TLS_ECDH_ECDSA_WITH_NULL_SHA"] = 0xC001,
["TLS_ECDH_ECDSA_WITH_RC4_128_SHA"] = 0xC002,
["TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"] = 0xC003,
["TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"] = 0xC004,
["TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"] = 0xC005,
["TLS_ECDHE_ECDSA_WITH_NULL_SHA"] = 0xC006,
["TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"] = 0xC007,
["TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"] = 0xC008,
["TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"] = 0xC009,
["TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"] = 0xC00A,
["TLS_ECDH_RSA_WITH_NULL_SHA"] = 0xC00B,
["TLS_ECDH_RSA_WITH_RC4_128_SHA"] = 0xC00C,
["TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"] = 0xC00D,
["TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"] = 0xC00E,
["TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"] = 0xC00F,
["TLS_ECDHE_RSA_WITH_NULL_SHA"] = 0xC010,
["TLS_ECDHE_RSA_WITH_RC4_128_SHA"] = 0xC011,
["TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"] = 0xC012,
["TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"] = 0xC013,
["TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"] = 0xC014,
["TLS_ECDH_anon_WITH_NULL_SHA"] = 0xC015,
["TLS_ECDH_anon_WITH_RC4_128_SHA"] = 0xC016,
["TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"] = 0xC017,
["TLS_ECDH_anon_WITH_AES_128_CBC_SHA"] = 0xC018,
["TLS_ECDH_anon_WITH_AES_256_CBC_SHA"] = 0xC019,
["TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"] = 0xC01A,
["TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"] = 0xC01B,
["TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"] = 0xC01C,
["TLS_SRP_SHA_WITH_AES_128_CBC_SHA"] = 0xC01D,
["TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"] = 0xC01E,
["TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"] = 0xC01F,
["TLS_SRP_SHA_WITH_AES_256_CBC_SHA"] = 0xC020,
["TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"] = 0xC021,
["TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"] = 0xC022,
["TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"] = 0xC023,
["TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"] = 0xC024,
["TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"] = 0xC025,
["TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"] = 0xC026,
["TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"] = 0xC027,
["TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"] = 0xC028,
["TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"] = 0xC029,
["TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"] = 0xC02A,
["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"] = 0xC02B,
["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"] = 0xC02C,
["TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"] = 0xC02D,
["TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"] = 0xC02E,
["TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"] = 0xC02F,
["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"] = 0xC030,
["TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"] = 0xC031,
["TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"] = 0xC032,
["TLS_ECDHE_PSK_WITH_RC4_128_SHA"] = 0xC033,
["TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"] = 0xC034,
["TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"] = 0xC035,
["TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"] = 0xC036,
["TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"] = 0xC037,
["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,
["SSL_RSA_FIPS_WITH_DES_CBC_SHA"] = 0xFEFE,
["SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"] = 0xFEFF
}
cipherstrength = {
["weak"] = 0,
["unknown strength"] = 1,
["strong"] = 2
}
local rankedciphers={}
local mincipherstrength=2
local rankedciphersfilename=false
local policy=true
local function record_read(buffer, i)
local b, h, j, len
local function find_key(t, value)
local k, v
for k, v in pairs(t) do
if v == value then
return k
end
end
return nil
end
------------
-- Header --
------------
-- Ensure we have enough data for the header.
if #buffer - i < TLS_RECORD_HEADER_LENGTH then
return i, nil
end
-- Parse header.
h = {}
j, h["type"] = bin.unpack("C", buffer, i)
j, h["protocol"] = bin.unpack(">S", buffer, j)
j, h["length"] = bin.unpack(">S", buffer, j)
-- Ensure we have enough data for the body.
len = j + h["length"] - 1
if #buffer < len then
return i, nil
end
-- Convert to human-readable form.
h["type"] = find_key(TLS_CONTENTTYPE_REGISTRY, h["type"])
h["protocol"] = find_key(PROTOCOLS, h["protocol"])
----------
-- Body --
----------
b = {}
h["body"] = b
if h["type"] == "alert" then
-- Parse body.
j, b["level"] = bin.unpack("C", buffer, j)
j, b["description"] = bin.unpack("C", buffer, j)
-- Convert to human-readable form.
b["level"] = find_key(TLS_ALERT_LEVELS, b["level"])
b["description"] = find_key(TLS_ALERT_REGISTRY, b["description"])
elseif h["type"] == "handshake" then
-- Parse body.
j, b["type"] = bin.unpack("C", buffer, j)
local _
j, _ = bin.unpack("A3", buffer, j)
-- Convert to human-readable form.
b["type"] = find_key(TLS_HANDSHAKETYPE_REGISTRY, b["type"])
if b["type"] == "server_hello" then
-- Parse body.
j, b["protocol"] = bin.unpack(">S", buffer, j)
j, b["time"] = bin.unpack(">I", buffer, j)
j, b["random"] = bin.unpack("A28", buffer, j)
j, b["session_id_length"] = bin.unpack("C", buffer, j)
j, b["session_id"] = bin.unpack("A" .. b["session_id_length"], buffer, j)
j, b["cipher"] = bin.unpack(">S", buffer, j)
j, b["compressor"] = bin.unpack("C", buffer, j)
-- Convert to human-readable form.
b["protocol"] = find_key(PROTOCOLS, b["protocol"])
b["cipher"] = find_key(CIPHERS, b["cipher"])
b["compressor"] = find_key(COMPRESSORS, b["compressor"])
end
end
-- Ignore unparsed bytes.
j = len
return j, h
end
local function record_write(type, protocol, b)
local h
h = ""
-- Set the header as a handshake.
h = h .. bin.pack("C", TLS_CONTENTTYPE_REGISTRY[type])
-- Set the protocol.
h = h .. bin.pack(">S", PROTOCOLS[protocol])
-- Set the length of the header body.
h = h .. bin.pack(">S", #b)
return h .. b
end
local function client_hello(t)
local b, cipher, ciphers, compressor, compressors, h, len
----------
-- Body --
----------
b = ""
-- Set the protocol.
b = b .. bin.pack(">S", PROTOCOLS[t["protocol"]])
-- Set the random data.
b = b .. bin.pack(">I", os.time())
-- Set the random data.
b = b .. string.rep("nmap", 7)
-- Set the session ID.
b = b .. bin.pack("C", 0)
-- 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
end
b = b .. bin.pack(">S", #ciphers)
b = b .. ciphers
-- 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
-- Add all known compressors.
for _, compressor in pairs(COMPRESSORS) do
compressors = compressors .. bin.pack("C", compressor)
end
end
b = b .. bin.pack("C", #compressors)
b = b .. compressors
------------
-- Header --
------------
h = ""
-- Set type to ClientHello.
h = h .. bin.pack("C", TLS_HANDSHAKETYPE_REGISTRY["client_hello"])
-- Set the length of the body.
len = bin.pack(">I", #b)
h = h .. bin.pack("CCC", len:byte(2), len:byte(3), len:byte(4))
return record_write("handshake", t["protocol"], h .. b)
end
local function try_params(host, port, t, name, records)
local buffer, err, i, record, req, resp, sock, status
local condvar = nmap.condvar(records)
-- Create socket.
sock = nmap.new_socket()
sock:set_timeout(5000)
status, err = sock:connect(host, port, "tcp")
if not status then
stdnse.print_debug(1, "Can't connect: %s", err)
sock:close()
condvar "signal"
return nil
end
-- Send request.
req = client_hello(t)
status, err = sock:send(req)
if not status then
stdnse.print_debug(1, "Can't send: %s", err)
sock:close()
condvar "signal"
return nil
end
-- Read response.
i = 0
buffer = ""
record = nil
while true do
status, resp = sock:receive()
if not status then
sock:close()
condvar "signal"
return record
end
buffer = buffer .. resp
-- Parse response.
i, record = record_read(buffer, i)
if record ~= nil then
sock:close()
record.name = name
table.insert(records, record)
condvar "signal"
return
end
end
end
local function try_protocol(host, port, protocol)
local ciphers, compressors, results
local function find_ciphers()
local name, protocol_worked, record, results, t,cipherstr
local records, threads = {}, {}
local condvar = nmap.condvar(records)
results = {}
-- Try every cipher.
protocol_worked = false
for name, _ in pairs(CIPHERS) do
-- Create structure.
t = {
["ciphers"] = {name},
["protocol"] = protocol
}
-- Try connecting with cipher.
local co = stdnse.new_thread(try_params, host, port, t, name, records)
threads[co] = true
end
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then threads[thread] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
for _, record in ipairs(records) do
local name = record.name
if record == nil then
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"]
if rankedciphersfilename and rankedciphers[name] then
cipherstr=rankedciphers[name]
else
cipherstr="unknown strength"
end
stdnse.print_debug(2, "Strength of %s rated %d.",cipherstr,cipherstrength[cipherstr])
if mincipherstrength>cipherstrength[cipherstr] then
stdnse.print_debug(2, "Downgrading min cipher strength to %d.",cipherstrength[cipherstr])
mincipherstrength=cipherstrength[cipherstr]
end
name=name.." - "..cipherstr
table.insert(results, name)
end
end
return results
end
local function find_compressors()
local name, protocol_worked, record, results, t
local records, threads = {}, {}
local condvar = nmap.condvar(records)
results = {}
-- Try every compressor.
protocol_worked = false
for name, _ in pairs(COMPRESSORS) do
-- Create structure.
t = {
["compressors"] = {name},
["protocol"] = protocol
}
-- Try connecting with compressor.
local co = stdnse.new_thread(try_params, host, port, t, name, records)
threads[co] = true
end
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then threads[thread] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
for _, record in ipairs(records) do
local name = record.name
if record == nil then
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.")
elseif record["body"]["compressor"] ~= name then
protocol_worked = true
stdnse.print_debug(2, "Compressor %s rejected.", name)
else
protocol_worked = true
stdnse.print_debug(2, "Compressor %s chosen.", name)
-- Add compressor to the list of accepted compressors.
table.insert(results, name)
end
end
return results
end
results = {}
-- Find all valid ciphers.
ciphers = find_ciphers()
if #ciphers == 0 then
return {}
end
-- Find all valid compression methods.
compressors = find_compressors()
-- Format the cipher table.
table.sort(ciphers)
ciphers["name"] = "Ciphers (" .. #ciphers .. ")"
table.insert(results, ciphers)
-- Format the compressor table.
table.sort(compressors)
compressors["name"] = "Compressors (" .. #compressors .. ")"
table.insert(results, compressors)
return results
end
-- Shamelessly stolen from nselib/unpwdb.lua and changed a bit. (Gabriel Lawrence)
local filltable = function(filename,table)
if #table ~= 0 then
return true
end
local file = io.open(filename, "r")
if not file then
return false
end
while true do
local l = file:read()
if not l then
break
end
-- Comments takes up a whole line
if not l:match("#!comment:") then
local lsplit=stdnse.strsplit("%s+", l)
if cipherstrength[lsplit[2]] then
table[lsplit[1]] = lsplit[2]
else
stdnse.print_debug(1,"Strength not defined, ignoring: %s:%s",lsplit[1],lsplit[2])
end
end
end
file:close()
return true
end
portrule = shortport.ssl
action = function(host, port)
local name, result, results
rankedciphersfilename=stdnse.get_script_args("ssl-enum-ciphers.rankedcipherlist")
if rankedciphersfilename then
filltable(rankedciphersfilename,rankedciphers)
else
rankedciphersfilename = nmap.fetchfile( "nselib/data/ssl-ciphers" )
stdnse.print_debug(1, "Ranked ciphers filename: %s", rankedciphersfilename)
filltable(rankedciphersfilename,rankedciphers)
end
results = {}
for name, _ in pairs(PROTOCOLS) do
stdnse.print_debug(1, "Trying protocol %s.", name)
result = try_protocol(host.ip, port.number, name)
if #result > 0 then
result["name"] = name
table.insert(results, result)
end
end
-- Sort protocol results by name.
table.sort(results, function(a, b) return a["name"] < b["name"] end)
if rankedciphersfilename then
for k, v in pairs(cipherstrength) do
if v == mincipherstrength then
table.insert(results, "Least strength = " .. k)
end
end
end
return stdnse.format_output(true, results)
end