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