diff --git a/CHANGELOG b/CHANGELOG
index 2a4fc17bb..ac0b3b127 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,10 @@
[NOT YET RELEASED]
+o [NSE] Added the ssl-enum-ciphers script from Mak Kolybabi. This
+ script lists the ciphers and compressors supported by an SSL/TLS
+ server.
+
o [Ncat] Fixed a segmentation fault caused by access to freed memory.
It could be triggered by making multiple connections to a server
that was constantly sending in SSL mode, as
diff --git a/scripts/ssl-enum-ciphers.nse b/scripts/ssl-enum-ciphers.nse
new file mode 100644
index 000000000..bbdaef7f7
--- /dev/null
+++ b/scripts/ssl-enum-ciphers.nse
@@ -0,0 +1,811 @@
+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.
+
+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 ssl-enum-ciphers.sort=name 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).
+]]
+
+---
+-- @usage
+-- nmap --script ssl-enum-algorithms -p 443
+--
+-- @args ssl-enum-ciphers.sort If set to name, sort results
+-- alphabetically instead of in the order in which they were accepted.
+--
+-- @output
+-- PORT STATE SERVICE REASON
+-- 443/tcp open https syn-ack
+-- | sslv3-enum:
+-- | SSLv3
+-- | Ciphers (18)
+-- | 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
+-- | Compressors (1)
+-- | uncompressed
+-- | TLSv1.0
+-- | Ciphers (18)
+-- | 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
+-- | Compressors (1)
+-- |_ uncompressed
+
+author = "Mak Kolybabi "
+
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+
+categories = {"discovery", "intrusive"}
+
+require("bin")
+require("nmap")
+require("shortport")
+require("stdnse")
+
+local SSL_PORTS = {
+ 443,
+ 465,
+ 587,
+ 636,
+ 989,
+ 990,
+ 992,
+ 993,
+ 994,
+ 995,
+ 5061,
+ 6679,
+ 6697,
+ 8443
+}
+
+local SSL_SERVICES = {
+ "ftps",
+ "ftps-data",
+ "https",
+ "https-alt",
+ "imaps",
+ "ircs",
+ "ldapssl",
+ "pop3s",
+ "sip-tls",
+ "smtps",
+ "telnets"
+}
+
+-- All 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
+--
+COMPRESSORS = {
+ ["uncompressed"] = 0,
+ ["ansiX962_compressed_prime"] = 1,
+ ["ansiX962_compressed_char2"] = 2
+}
+
+--
+-- 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,
+ ["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_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_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
+}
+
+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"]
+ 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)
+ 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
+
+ -- 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
+
+ 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 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
+ for _, cipher in pairs(t["ciphers"]) do
+ ciphers = ciphers .. bin.pack(">S", CIPHERS[cipher])
+ end
+ else
+ 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
+ for _, compressor in pairs(t["compressors"]) do
+ compressors = compressors .. bin.pack("C", COMPRESSORS[compressor])
+ end
+ else
+ ciphers = b .. bin.pack("C", #COMPRESSORS)
+ for _, compressor in pairs(COMPRESSORS) do
+ compressors = compressors .. bin.pack("C", compressor)
+ end
+ end
+ b = b .. bin.pack("C", #compressors)
+ b = b .. compressors
+
+ -- Extensions.
+ b = b .. bin.pack(">S", 0)
+
+ ------------
+ -- 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))
+
+ -- 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
+
+ -- 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()
+ 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()
+ return nil
+ end
+
+ -- Read response.
+ i = 0
+ buffer = ""
+ while true do
+ status, chunk = sock:receive()
+ if not status then
+ sock:close()
+ return nil
+ end
+
+ buffer = buffer .. chunk
+
+ -- Parse response.
+ i, record = record_read(buffer, i)
+ if record ~= nil then
+ sock:close()
+ return record
+ end
+ end
+end
+
+local function try_protocol(host, port, protocol)
+ local ciphers, compressors, result
+
+ local function find_ciphers()
+ local k, name, record, results, t, v
+
+ results = {}
+
+ -- Create list of ciphers.
+ t = {
+ ["ciphers"] = {},
+ ["protocol"] = protocol
+ }
+ for name, id in pairs(CIPHERS) do
+ table.insert(t["ciphers"], name)
+ end
+
+ -- Create connections while removing previously accepted ciphers.
+ while true do
+ -- Try connecting with remaining ciphers.
+ 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
+ end
+ end
+ end
+
+ return results
+ end
+
+ local function find_compressors()
+ local compressors, k, name, record, results, t, v
+
+ results = {}
+
+ -- Create list of compressors.
+ t = {
+ ["compressors"] = {},
+ ["protocol"] = protocol
+ }
+ for name, id in pairs(COMPRESSORS) do
+ table.insert(t["compressors"], name)
+ end
+
+ -- Create connections while removing previously accepted compressors.
+ while true do
+ -- Try connecting with remaining ciphers.
+ 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
+ end
+ 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.
+ if nmap.registry.args["ssl-enum-ciphers.sort"] == "name" then
+ table.sort(ciphers)
+ end
+ 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
+ compressors["name"] = "Compressors (" .. #compressors .. ")"
+ table.insert(results, compressors)
+
+ return results
+end
+
+portrule = function(host, port)
+ local is_ssl = shortport.port_or_service(SSL_PORTS, SSL_SERVICES)
+
+ -- This script only handles SSL/TLS over TCP.
+ if port.protocol ~= "tcp" then
+ return false
+ end
+
+ if port.version.service_tunnel == "ssl" then
+ return true
+ end
+
+ if is_ssl(host, port) then
+ return true
+ end
+
+ return false
+end
+
+action = function(host, port)
+ local result, results
+
+ 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)
+
+ return stdnse.format_output(true, results)
+end