mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
TLS 1.3 support for NSE. Fixes #1691
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
#Nmap Changelog ($Id$); -*-text-*-
|
#Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE][GH#1691] TLS 1.3 now supported by most scripts for which it is
|
||||||
|
relevant, such as ssl-enum-ciphers. Some functions like ssl tunnel
|
||||||
|
connections and certificate parsing will require OpenSSL 1.1.1 or later to
|
||||||
|
fully support TLS 1.3. [Daniel Miller]
|
||||||
|
|
||||||
o Setting --host-timeout=0 will disable the host timeout, which is set by -T5
|
o Setting --host-timeout=0 will disable the host timeout, which is set by -T5
|
||||||
to 15 minutes. Earlier versions of Nmap require the user to specify a very
|
to 15 minutes. Earlier versions of Nmap require the user to specify a very
|
||||||
long timeout instead.
|
long timeout instead.
|
||||||
|
|||||||
@@ -942,7 +942,9 @@ end
|
|||||||
|
|
||||||
local function handshake_cert (socket)
|
local function handshake_cert (socket)
|
||||||
-- logic mostly lifted from ssl-enum-ciphers
|
-- logic mostly lifted from ssl-enum-ciphers
|
||||||
local hello = tls.client_hello()
|
-- TODO: implement TLSv1.3 handshake encryption so we can decrypt the
|
||||||
|
-- Certificate message. Until then, we don't attempt TLSv1.3
|
||||||
|
local hello = tls.client_hello({protocol="TLSv1.2"})
|
||||||
local status, err = socket:send(hello)
|
local status, err = socket:send(hello)
|
||||||
if not status then
|
if not status then
|
||||||
return false, "Failed to send to server"
|
return false, "Failed to send to server"
|
||||||
|
|||||||
442
nselib/tls.lua
442
nselib/tls.lua
@@ -28,9 +28,10 @@ PROTOCOLS = {
|
|||||||
["SSLv3"] = 0x0300,
|
["SSLv3"] = 0x0300,
|
||||||
["TLSv1.0"] = 0x0301,
|
["TLSv1.0"] = 0x0301,
|
||||||
["TLSv1.1"] = 0x0302,
|
["TLSv1.1"] = 0x0302,
|
||||||
["TLSv1.2"] = 0x0303
|
["TLSv1.2"] = 0x0303,
|
||||||
|
["TLSv1.3"] = 0x0304,
|
||||||
}
|
}
|
||||||
HIGHEST_PROTOCOL = "TLSv1.2"
|
HIGHEST_PROTOCOL = "TLSv1.3"
|
||||||
local TLS_PROTOCOL_VERSIONS = tableaux.invert(PROTOCOLS)
|
local TLS_PROTOCOL_VERSIONS = tableaux.invert(PROTOCOLS)
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -85,12 +86,15 @@ TLS_ALERT_REGISTRY = {
|
|||||||
["inappropriate_fallback"] = 86,
|
["inappropriate_fallback"] = 86,
|
||||||
["user_canceled"] = 90,
|
["user_canceled"] = 90,
|
||||||
["no_renegotiation"] = 100,
|
["no_renegotiation"] = 100,
|
||||||
|
["missing_extension"] = 109,
|
||||||
["unsupported_extension"] = 110,
|
["unsupported_extension"] = 110,
|
||||||
["certificate_unobtainable"] = 111,
|
["certificate_unobtainable"] = 111,
|
||||||
["unrecognized_name"] = 112,
|
["unrecognized_name"] = 112,
|
||||||
["bad_certificate_status_response"] = 113,
|
["bad_certificate_status_response"] = 113,
|
||||||
["bad_certificate_hash_value"] = 114,
|
["bad_certificate_hash_value"] = 114,
|
||||||
["unknown_psk_identity"] = 115
|
["unknown_psk_identity"] = 115,
|
||||||
|
["certificate_required"] = 116,
|
||||||
|
["no_application_protocol"] = 120,
|
||||||
}
|
}
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -102,6 +106,9 @@ TLS_HANDSHAKETYPE_REGISTRY = {
|
|||||||
["server_hello"] = 2,
|
["server_hello"] = 2,
|
||||||
["hello_verify_request"] = 3,
|
["hello_verify_request"] = 3,
|
||||||
["NewSessionTicket"] = 4,
|
["NewSessionTicket"] = 4,
|
||||||
|
["end_of_early_data"] = 5,
|
||||||
|
["hello_retry_request"] = 6,
|
||||||
|
["encrypted_extensions"] = 8,
|
||||||
["certificate"] = 11,
|
["certificate"] = 11,
|
||||||
["server_key_exchange"] = 12,
|
["server_key_exchange"] = 12,
|
||||||
["certificate_request"] = 13,
|
["certificate_request"] = 13,
|
||||||
@@ -112,7 +119,9 @@ TLS_HANDSHAKETYPE_REGISTRY = {
|
|||||||
["certificate_url"] = 21,
|
["certificate_url"] = 21,
|
||||||
["certificate_status"] = 22,
|
["certificate_status"] = 22,
|
||||||
["supplemental_data"] = 23,
|
["supplemental_data"] = 23,
|
||||||
|
["key_update"] = 24,
|
||||||
["next_protocol"] = 67,
|
["next_protocol"] = 67,
|
||||||
|
["message_hash"] = 254,
|
||||||
}
|
}
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -156,13 +165,24 @@ ELLIPTIC_CURVES = {
|
|||||||
brainpoolP256r1 = 26, --RFC7027
|
brainpoolP256r1 = 26, --RFC7027
|
||||||
brainpoolP384r1 = 27,
|
brainpoolP384r1 = 27,
|
||||||
brainpoolP512r1 = 28,
|
brainpoolP512r1 = 28,
|
||||||
ecdh_x25519 = 29, -- draft rfc4492
|
ecdh_x25519 = 29, -- rfc8422
|
||||||
ecdh_x448 = 30, --draft rfc4492
|
ecdh_x448 = 30, -- rfc8422
|
||||||
ffdhe2048 = 256, --RFC7919
|
brainpoolP256r1tls13 = 31, --RFC8734
|
||||||
ffdhe3072 = 257, --RFC7919
|
brainpoolP384r1tls13 = 32,
|
||||||
ffdhe4096 = 258, --RFC7919
|
brainpoolP512r1tls13 = 33,
|
||||||
ffdhe6144 = 259, --RFC7919
|
GC256A = 34, -- draft-smyshlyaev-tls12-gost-suites
|
||||||
ffdhe8192 = 260, --RFC7919
|
GC256B = 35,
|
||||||
|
GC256C = 36,
|
||||||
|
GC256D = 37,
|
||||||
|
GC512A = 38,
|
||||||
|
GC512B = 39,
|
||||||
|
GC512C = 40,
|
||||||
|
curveSM2 = 41, -- RFC 8998
|
||||||
|
ffdhe2048 = 0x0100, --RFC7919
|
||||||
|
ffdhe3072 = 0x0101, --RFC7919
|
||||||
|
ffdhe4096 = 0x0102, --RFC7919
|
||||||
|
ffdhe6144 = 0x0103, --RFC7919
|
||||||
|
ffdhe8192 = 0x0104, --RFC7919
|
||||||
arbitrary_explicit_prime_curves = 0xFF01,
|
arbitrary_explicit_prime_curves = 0xFF01,
|
||||||
arbitrary_explicit_char2_curves = 0xFF02,
|
arbitrary_explicit_char2_curves = 0xFF02,
|
||||||
}
|
}
|
||||||
@@ -173,6 +193,7 @@ DEFAULT_ELLIPTIC_CURVES = {
|
|||||||
"secp384r1",
|
"secp384r1",
|
||||||
"secp521r1",
|
"secp521r1",
|
||||||
"ecdh_x25519",
|
"ecdh_x25519",
|
||||||
|
"ffdhe2048", -- added for TLSv1.3
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -204,6 +225,35 @@ SignatureAlgorithms = {
|
|||||||
ed448 = 8,
|
ed448 = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
-- TLS v1.3 Signature Algorithms
|
||||||
|
SignatureSchemes = {
|
||||||
|
-- RSASSA-PKCS1-v1_5 algorithms
|
||||||
|
rsa_pkcs1_sha256 = 0x0401,
|
||||||
|
rsa_pkcs1_sha384 = 0x0501,
|
||||||
|
rsa_pkcs1_sha512 = 0x0601,
|
||||||
|
-- ECDSA algorithms
|
||||||
|
ecdsa_secp256r1_sha256 = 0x0403,
|
||||||
|
ecdsa_secp384r1_sha384 = 0x0503,
|
||||||
|
ecdsa_secp521r1_sha512 = 0x0603,
|
||||||
|
-- RSASSA-PSS algorithms with public key OID rsaEncryption
|
||||||
|
rsa_pss_rsae_sha256 = 0x0804,
|
||||||
|
rsa_pss_rsae_sha384 = 0x0805,
|
||||||
|
rsa_pss_rsae_sha512 = 0x0806,
|
||||||
|
-- EdDSA algorithms
|
||||||
|
ed25519 = 0x0807,
|
||||||
|
ed448 = 0x0808,
|
||||||
|
-- RSASSA-PSS algorithms with public key OID RSASSA-PSS
|
||||||
|
rsa_pss_pss_sha256 = 0x0809,
|
||||||
|
rsa_pss_pss_sha384 = 0x080a,
|
||||||
|
rsa_pss_pss_sha512 = 0x080b,
|
||||||
|
-- Legacy algorithms
|
||||||
|
rsa_pkcs1_sha1 = 0x0201,
|
||||||
|
ecdsa_sha1 = 0x0203,
|
||||||
|
-- RFC 8998
|
||||||
|
sm2sig_sm3 = 0x0708,
|
||||||
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Extensions
|
-- Extensions
|
||||||
-- RFC 6066, draft-agl-tls-nextprotoneg-03
|
-- RFC 6066, draft-agl-tls-nextprotoneg-03
|
||||||
@@ -224,6 +274,9 @@ EXTENSIONS = {
|
|||||||
["ec_point_formats"] = 11,
|
["ec_point_formats"] = 11,
|
||||||
["srp"] = 12,
|
["srp"] = 12,
|
||||||
["signature_algorithms"] = 13,
|
["signature_algorithms"] = 13,
|
||||||
|
-- TLSv1.3 changed the format for this extension. It's just more convenient
|
||||||
|
-- to call it something else.
|
||||||
|
["signature_algorithms_13"] = 13,
|
||||||
["use_srtp"] = 14,
|
["use_srtp"] = 14,
|
||||||
["heartbeat"] = 15,
|
["heartbeat"] = 15,
|
||||||
["application_layer_protocol_negotiation"] = 16,
|
["application_layer_protocol_negotiation"] = 16,
|
||||||
@@ -237,6 +290,18 @@ EXTENSIONS = {
|
|||||||
["token_binding"] = 24, -- Temporary, expires 2018-02-04
|
["token_binding"] = 24, -- Temporary, expires 2018-02-04
|
||||||
["cached_info"] = 25, -- rfc7924
|
["cached_info"] = 25, -- rfc7924
|
||||||
["SessionTicket TLS"] = 35,
|
["SessionTicket TLS"] = 35,
|
||||||
|
-- TLSv1.3
|
||||||
|
["pre_shared_key"] = 41,
|
||||||
|
["early_data"] = 42,
|
||||||
|
["supported_versions"] = 43,
|
||||||
|
["cookie"] = 44,
|
||||||
|
["psk_key_exchange_modes"] = 45,
|
||||||
|
["certificate_authorities"] = 47,
|
||||||
|
["oid_filters"] = 48,
|
||||||
|
["post_handshake_auth"] = 49,
|
||||||
|
["signature_algorithms_cert"] = 50,
|
||||||
|
["key_share"] = 51,
|
||||||
|
--
|
||||||
["next_protocol_negotiation"] = 13172,
|
["next_protocol_negotiation"] = 13172,
|
||||||
["renegotiation_info"] = 65281,
|
["renegotiation_info"] = 65281,
|
||||||
}
|
}
|
||||||
@@ -279,6 +344,13 @@ EXTENSION_HELPERS = {
|
|||||||
end
|
end
|
||||||
return pack(">s2", table.concat(list))
|
return pack(">s2", table.concat(list))
|
||||||
end,
|
end,
|
||||||
|
["signature_algorithms_13"] = function (signature_schemes)
|
||||||
|
local list = {}
|
||||||
|
for _, name in ipairs(signature_schemes) do
|
||||||
|
list[#list+1] = pack(">I2", SignatureSchemes[name])
|
||||||
|
end
|
||||||
|
return pack(">s2", table.concat(list))
|
||||||
|
end,
|
||||||
["application_layer_protocol_negotiation"] = function(protocols)
|
["application_layer_protocol_negotiation"] = function(protocols)
|
||||||
local list = {}
|
local list = {}
|
||||||
for _, proto in ipairs(protocols) do
|
for _, proto in ipairs(protocols) do
|
||||||
@@ -287,6 +359,13 @@ EXTENSION_HELPERS = {
|
|||||||
return pack(">s2", table.concat(list))
|
return pack(">s2", table.concat(list))
|
||||||
end,
|
end,
|
||||||
["next_protocol_negotiation"] = tostring,
|
["next_protocol_negotiation"] = tostring,
|
||||||
|
["supported_versions"] = function(versions)
|
||||||
|
local list = {}
|
||||||
|
for _, name in ipairs(versions) do
|
||||||
|
list[#list+1] = pack(">I2", PROTOCOLS[name])
|
||||||
|
end
|
||||||
|
return pack(">s1", table.concat(list))
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -654,6 +733,12 @@ CIPHERS = {
|
|||||||
["TLS_ECDHE_ECDSA_WITH_AES_256_CCM"] = 0xC0AD,
|
["TLS_ECDHE_ECDSA_WITH_AES_256_CCM"] = 0xC0AD,
|
||||||
["TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"] = 0xC0AE,
|
["TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"] = 0xC0AE,
|
||||||
["TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"] = 0xC0AF,
|
["TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"] = 0xC0AF,
|
||||||
|
["TLS_ECCPWD_WITH_AES_128_GCM_SHA256"] = 0xC0B0, -- RFC8492
|
||||||
|
["TLS_ECCPWD_WITH_AES_256_GCM_SHA384"] = 0xC0B1, -- RFC8492
|
||||||
|
["TLS_ECCPWD_WITH_AES_128_CCM_SHA256"] = 0xC0B2, -- RFC8492
|
||||||
|
["TLS_ECCPWD_WITH_AES_256_CCM_SHA384"] = 0xC0B3, -- RFC8492
|
||||||
|
["TLS_AKE_WITH_NULL_SHA256"] = 0xC0B4, -- draft-camwinget-tls-ts13-macciphersuites
|
||||||
|
["TLS_AKE_WITH_NULL_SHA384"] = 0xC0B5, -- draft-camwinget-tls-ts13-macciphersuites
|
||||||
["TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC13, -- RFC7905 superseded
|
["TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC13, -- RFC7905 superseded
|
||||||
["TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC14, -- RFC7905 superseded
|
["TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC14, -- RFC7905 superseded
|
||||||
["TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC15, -- RFC7905 superseded
|
["TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256-draft"] = 0xCC15, -- RFC7905 superseded
|
||||||
@@ -664,21 +749,49 @@ CIPHERS = {
|
|||||||
["TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAC,
|
["TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAC,
|
||||||
["TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAD,
|
["TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAD,
|
||||||
["TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAE,
|
["TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"] = 0xCCAE,
|
||||||
["TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256"] = 0xD001, -- draft-ietf-tls-ecdhe-psk-aead-05
|
["TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256"] = 0xD001, -- RFC 8442
|
||||||
["TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384"] = 0xD002, -- draft-ietf-tls-ecdhe-psk-aead-05
|
["TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384"] = 0xD002, -- RFC 8442
|
||||||
["TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256"] = 0xD003, -- draft-ietf-tls-ecdhe-psk-aead-05
|
["TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256"] = 0xD003, -- RFC 8442
|
||||||
["TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256"] = 0xD005, -- draft-ietf-tls-ecdhe-psk-aead-05
|
["TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256"] = 0xD005, -- RFC 8442
|
||||||
["SSL_RSA_FIPS_WITH_DES_CBC_SHA"] = 0xFEFE,
|
["SSL_RSA_FIPS_WITH_DES_CBC_SHA"] = 0xFEFE,
|
||||||
["SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"] = 0xFEFF,
|
["SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"] = 0xFEFF,
|
||||||
|
-- TLSv1.3:
|
||||||
|
-- "Although TLS 1.3 uses the same cipher suite space as previous versions of
|
||||||
|
-- TLS, TLS 1.3 cipher suites are defined differently, only specifying the
|
||||||
|
-- symmetric ciphers, and cannot be used for TLS 1.2. Similarly, TLS 1.2 and
|
||||||
|
-- lower cipher suites cannot be used with TLS 1.3."
|
||||||
|
-- We designate these as AKE (Authenticated Key Exchange) ciphersuites, in
|
||||||
|
-- order to simplify use of the cipher_info function.
|
||||||
|
TLS_AKE_WITH_AES_128_GCM_SHA256 = 0x1301,
|
||||||
|
TLS_AKE_WITH_AES_256_GCM_SHA384 = 0x1302,
|
||||||
|
TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 = 0x1303,
|
||||||
|
TLS_AKE_WITH_AES_128_CCM_SHA256 = 0x1304,
|
||||||
|
TLS_AKE_WITH_AES_128_CCM_8_SHA256 = 0x1305,
|
||||||
|
TLS_AKE_WITH_SM4_GCM_SM3 = 0x00C6, -- RFC 8998
|
||||||
|
TLS_AKE_WITH_SM4_CCM_SM3 = 0x00C7, -- RFC 8998
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_CIPHERS = {
|
-- Default ciphers sent by tls.client_hello for TLSv1.2 or earlier
|
||||||
|
DEFAULT_TLS12_CIPHERS = {
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA", -- mandatory TLSv1.2
|
"TLS_RSA_WITH_AES_128_CBC_SHA", -- mandatory TLSv1.2
|
||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", -- mandatory TLSv1.1
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", -- mandatory TLSv1.1
|
||||||
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", -- mandatory TLSv1.0
|
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", -- mandatory TLSv1.0
|
||||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", -- DHE with strong AES
|
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", -- DHE with strong AES
|
||||||
"TLS_RSA_WITH_RC4_128_MD5", -- Weak and old, but likely supported on old stuff
|
"TLS_RSA_WITH_RC4_128_MD5", -- Weak and old, but likely supported on old stuff
|
||||||
}
|
}
|
||||||
|
-- Same, but for TLSv1.3
|
||||||
|
DEFAULT_TLS13_CIPHERS = {
|
||||||
|
"TLS_AKE_WITH_AES_128_GCM_SHA256", -- mandatory TLSv1.3
|
||||||
|
"TLS_AKE_WITH_AES_256_GCM_SHA384", -- stronger TLSv1.3
|
||||||
|
"TLS_AKE_WITH_CHACHA20_POLY1305_SHA256", -- alternate TLSv1.3
|
||||||
|
}
|
||||||
|
-- Same, but for handshakes compatible with any TLS version
|
||||||
|
local DEFAULT_CIPHERS = {
|
||||||
|
table.unpack(DEFAULT_TLS13_CIPHERS)
|
||||||
|
}
|
||||||
|
for _, c in ipairs(DEFAULT_TLS12_CIPHERS) do
|
||||||
|
table.insert(DEFAULT_CIPHERS, c)
|
||||||
|
end
|
||||||
|
|
||||||
local function find_key(t, value)
|
local function find_key(t, value)
|
||||||
local found, v = tableaux.contains(t, value)
|
local found, v = tableaux.contains(t, value)
|
||||||
@@ -747,6 +860,24 @@ local function unpack_dhparams (blob, pos)
|
|||||||
return pos, {p=p, g=g, y=y}, #p * 8
|
return pos, {p=p, g=g, y=y}, #p * 8
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function named_group_info (group)
|
||||||
|
if group:match("^arbitrary") then
|
||||||
|
return "ec"
|
||||||
|
end
|
||||||
|
local ktype, size = group:match("^(%D+)(%d+)")
|
||||||
|
assert(ktype and size, ("Invalid named group: %s"):format(group))
|
||||||
|
size = tonumber(size)
|
||||||
|
if ktype == "ffdhe" then
|
||||||
|
ktype = "dh"
|
||||||
|
else
|
||||||
|
if group == "ecdh_x25519" or group == "curveSM2" then
|
||||||
|
size = 256
|
||||||
|
end
|
||||||
|
ktype = "ec"
|
||||||
|
end
|
||||||
|
return ktype, size
|
||||||
|
end
|
||||||
|
|
||||||
local function unpack_ecdhparams (blob, pos)
|
local function unpack_ecdhparams (blob, pos)
|
||||||
local eccurvetype
|
local eccurvetype
|
||||||
eccurvetype, pos = unpack("B", blob, pos)
|
eccurvetype, pos = unpack("B", blob, pos)
|
||||||
@@ -783,14 +914,8 @@ local function unpack_ecdhparams (blob, pos)
|
|||||||
ec_curve_type = "namedcurve",
|
ec_curve_type = "namedcurve",
|
||||||
curve = find_key(ELLIPTIC_CURVES, curve)
|
curve = find_key(ELLIPTIC_CURVES, curve)
|
||||||
}
|
}
|
||||||
local size = ret.curve_params.curve:match("(%d+)[rk]%d$")
|
local _
|
||||||
if size then
|
_, strength = named_group_info(ret.curve_params.curve)
|
||||||
strength = tonumber(size)
|
|
||||||
elseif ret.curve_params.curve == "ecdh_x25519" then
|
|
||||||
strength = 256
|
|
||||||
elseif ret.curve_params.curve == "ecdh_x448" then
|
|
||||||
strength = 448
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
ret.public, pos = unpack("s1", blob, pos)
|
ret.public, pos = unpack("s1", blob, pos)
|
||||||
return pos, ret, strength
|
return pos, ret, strength
|
||||||
@@ -1064,7 +1189,51 @@ KEX_ALGORITHMS.KRB5_EXPORT={
|
|||||||
export=true,
|
export=true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- TLSv1.3
|
||||||
|
KEX_ALGORITHMS.AKE = {
|
||||||
|
tls13ok=true,
|
||||||
|
tls13only=true,
|
||||||
|
pfs=true,
|
||||||
|
-- TLSv1.3 swaps the ServerKeyExchange message for the key_share extension.
|
||||||
|
-- We'll just pretend that's what this is:
|
||||||
|
server_key_exchange = function (blob, protocol)
|
||||||
|
local named_group, pos = unpack(">I2", blob)
|
||||||
|
stdnse.debug1("named_group = %d", named_group)
|
||||||
|
named_group = find_key(ELLIPTIC_CURVES, named_group)
|
||||||
|
local gtype, strength = named_group_info(named_group)
|
||||||
|
return {
|
||||||
|
type = gtype,
|
||||||
|
strength = strength,
|
||||||
|
ecdhparams={ -- Not always ECC, but reusing structure simplifies things
|
||||||
|
curve_params={
|
||||||
|
ec_curve_type = "namedcurve",
|
||||||
|
curve = named_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
-- RFC 8492
|
||||||
|
KEX_ALGORITHMS.ECCPWD = {
|
||||||
|
tls13ok=true,
|
||||||
|
tls13only=false,
|
||||||
|
}
|
||||||
|
|
||||||
|
local algorithms = {
|
||||||
|
["3DES"] = {s=112, b=64}, --NIST SP 800-57
|
||||||
|
CHACHA20 = {s=256, b=128},
|
||||||
|
IDEA = {s=128, b=64},
|
||||||
|
SEED = {s=128, b=128},
|
||||||
|
FORTEZZA = {s=80, b=64},
|
||||||
|
DES = {s=56, b=64},
|
||||||
|
RC2 = {s=40, b=64},
|
||||||
|
DES40 = {s=40, b=64},
|
||||||
|
NULL = {s=0},
|
||||||
|
CAMELLIA = {b=128},
|
||||||
|
ARIA = {b=128},
|
||||||
|
AES = {b=128},
|
||||||
|
SM4 = {s=128, b=128},
|
||||||
|
}
|
||||||
--- Get info about a cipher suite
|
--- Get info about a cipher suite
|
||||||
--
|
--
|
||||||
-- Returned table has "kex", "cipher", "mode", "size", and
|
-- Returned table has "kex", "cipher", "mode", "size", and
|
||||||
@@ -1076,7 +1245,6 @@ KEX_ALGORITHMS.KRB5_EXPORT={
|
|||||||
function cipher_info (c)
|
function cipher_info (c)
|
||||||
local info = cipher_info_cache[c]
|
local info = cipher_info_cache[c]
|
||||||
if info then return info end
|
if info then return info end
|
||||||
info = {}
|
|
||||||
local tokens = stringaux.strsplit("_", c)
|
local tokens = stringaux.strsplit("_", c)
|
||||||
local i = 1
|
local i = 1
|
||||||
if tokens[i] ~= "TLS" and tokens[i] ~= "SSL" then
|
if tokens[i] ~= "TLS" and tokens[i] ~= "SSL" then
|
||||||
@@ -1088,7 +1256,14 @@ function cipher_info (c)
|
|||||||
while tokens[i] and tokens[i] ~= "WITH" do
|
while tokens[i] and tokens[i] ~= "WITH" do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
info.kex = table.concat(tokens, "_", 2, i-1)
|
local kex = table.concat(tokens, "_", 2, i-1)
|
||||||
|
info = KEX_ALGORITHMS[kex]
|
||||||
|
if info then
|
||||||
|
info = tableaux.tcopy(info)
|
||||||
|
info.kex = kex
|
||||||
|
else
|
||||||
|
info = {kex = kex}
|
||||||
|
end
|
||||||
|
|
||||||
if tokens[i] and tokens[i] ~= "WITH" then
|
if tokens[i] and tokens[i] ~= "WITH" then
|
||||||
stdnse.debug2("cipher_info: Can't parse (no WITH): %s", c)
|
stdnse.debug2("cipher_info: Can't parse (no WITH): %s", c)
|
||||||
@@ -1104,34 +1279,16 @@ function cipher_info (c)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- key size
|
-- key size
|
||||||
if t == "3DES" then -- NIST SP 800-57
|
local tmp = algorithms[t]
|
||||||
info.size = 112
|
if tmp then
|
||||||
elseif t == "CHACHA20" then
|
info.size = tmp.s
|
||||||
info.size = 256
|
info.block_size = tmp.b
|
||||||
elseif t == "IDEA" then
|
end
|
||||||
info.size = 128
|
if info.size == nil then
|
||||||
elseif t == "SEED" then
|
|
||||||
info.size = 128
|
|
||||||
elseif t == "FORTEZZA" then
|
|
||||||
info.size = 80
|
|
||||||
elseif t == "DES" then
|
|
||||||
info.size = 56
|
|
||||||
elseif t == "RC2" or t == "DES40" then
|
|
||||||
info.size = 40
|
|
||||||
elseif t == "NULL" then
|
|
||||||
info.size = 0
|
|
||||||
else
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
info.size = tonumber(tokens[i])
|
info.size = tonumber(tokens[i])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- block size (bits)
|
|
||||||
if t == "3DES" or t == "RC2" or t == "IDEA" or t == "DES" or t == "FORTEZZA" or t == "DES40" then
|
|
||||||
info.block_size = 64
|
|
||||||
elseif t == "AES" or t == "CAMELLIA" or t == "ARIA" or t == "SEED" then
|
|
||||||
info.block_size = 128
|
|
||||||
end
|
|
||||||
|
|
||||||
-- stream ciphers don't have a mode
|
-- stream ciphers don't have a mode
|
||||||
if info.cipher == "RC4" then
|
if info.cipher == "RC4" then
|
||||||
info.mode = "stream"
|
info.mode = "stream"
|
||||||
@@ -1158,15 +1315,28 @@ function cipher_info (c)
|
|||||||
-- hash
|
-- hash
|
||||||
if info.mode == "CCM" then
|
if info.mode == "CCM" then
|
||||||
info.hash = "SHA256"
|
info.hash = "SHA256"
|
||||||
else
|
end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
t = (tokens[i]):match("(.*)%-draft$")
|
if i <= #tokens then
|
||||||
if t then
|
if tokens[i] == "8" and info.mode == "CCM" then
|
||||||
info.draft = true
|
info.mode = "CCM_8"
|
||||||
else
|
i = i + 1
|
||||||
t = tokens[i]
|
elseif info.export and (tokens[i]):match("^%d+$") then
|
||||||
|
info.size = tonumber(tokens[i])
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
if i <= #tokens then
|
||||||
|
local t, w = (tokens[i]):match("(.+)%-([a-z]+)")
|
||||||
|
if t then
|
||||||
|
if w == "draft" then
|
||||||
|
info.draft = true
|
||||||
|
end
|
||||||
|
-- else "or"
|
||||||
|
else
|
||||||
|
t = tokens[i]
|
||||||
|
end
|
||||||
|
info.hash = t
|
||||||
end
|
end
|
||||||
info.hash = t
|
|
||||||
end
|
end
|
||||||
|
|
||||||
cipher_info_cache[c] = info
|
cipher_info_cache[c] = info
|
||||||
@@ -1203,6 +1373,22 @@ handshake_parse = {
|
|||||||
b["cipher"] = find_key(CIPHERS, b["cipher"])
|
b["cipher"] = find_key(CIPHERS, b["cipher"])
|
||||||
b["compressor"] = find_key(COMPRESSORS, b["compressor"])
|
b["compressor"] = find_key(COMPRESSORS, b["compressor"])
|
||||||
|
|
||||||
|
-- RFC 8446: HelloRetryRequest message uses the same structure as the
|
||||||
|
-- ServerHello, but with Random set to the special value of the SHA-256
|
||||||
|
-- of "HelloRetryRequest"
|
||||||
|
if b.protocol == "TLSv1.2" -- TLSv1.3 legacy version
|
||||||
|
and b.random == "\xCF\x21\xAD\x74\xE5\x9A\x61\x11\xBE\x1D\x8C\x02\x1E\x65\xB8\x91\xC2\xA2\x11\x16\x7A\xBB\x8C\x5E\x07\x9E\x09\xE2\xC8\xA8\x33\x9C"
|
||||||
|
then
|
||||||
|
b.helloretry = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- RFC 8446: "the legacy_version field MUST be set to 0x0303,
|
||||||
|
-- which is the version number for TLS 1.2"
|
||||||
|
if (b.protocol == "TLSv1.2" and b.extensions
|
||||||
|
and b.extensions.supported_versions == "\x03\x04") then
|
||||||
|
b.protocol = "TLSv1.3"
|
||||||
|
end
|
||||||
|
|
||||||
return b, j
|
return b, j
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@@ -1395,6 +1581,27 @@ function record_read(buffer, i, fragment)
|
|||||||
return j, h, true
|
return j, h, true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Get the record version field appropriate for the protocol version
|
||||||
|
--
|
||||||
|
-- TLSv1.3 introduced a change in the interpretation of the record version
|
||||||
|
-- field. Previously, this was an indication of the TLS protocol, but now it is
|
||||||
|
-- frozen at TLSv1.2.
|
||||||
|
-- @param proto_version The numeric value of the protocol, e.g. 0x0303 for TLSv1.2
|
||||||
|
-- @return The numeric value that should be used in the record layer version field
|
||||||
|
local function legacy_version (proto_version)
|
||||||
|
-- TLSv1.2 was the last version where protocol version was negotiated via the
|
||||||
|
-- record layer version. Later versions use the supported_versions extension
|
||||||
|
return proto_version <= 0x0303 and proto_version or 0x0303
|
||||||
|
end
|
||||||
|
|
||||||
|
function record_version_ok(received_version, proto_version)
|
||||||
|
if proto_version == "TLSv1.3" then
|
||||||
|
return received_version == "TLSv1.2"
|
||||||
|
end
|
||||||
|
return proto_version == received_version
|
||||||
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Build a SSL/TLS record
|
-- Build a SSL/TLS record
|
||||||
-- @param type The type of record ("handshake", "change_cipher_spec", etc.)
|
-- @param type The type of record ("handshake", "change_cipher_spec", etc.)
|
||||||
@@ -1406,7 +1613,7 @@ function record_write(type, protocol, b)
|
|||||||
-- Set the header as a handshake.
|
-- Set the header as a handshake.
|
||||||
pack("B", TLS_CONTENTTYPE_REGISTRY[type]),
|
pack("B", TLS_CONTENTTYPE_REGISTRY[type]),
|
||||||
-- Set the protocol.
|
-- Set the protocol.
|
||||||
pack(">I2", PROTOCOLS[protocol]),
|
pack(">I2", legacy_version(PROTOCOLS[protocol])),
|
||||||
-- Set the length of the header body.
|
-- Set the length of the header body.
|
||||||
pack(">s2", b)
|
pack(">s2", b)
|
||||||
})
|
})
|
||||||
@@ -1435,6 +1642,26 @@ do
|
|||||||
}
|
}
|
||||||
DEFAULT_SIGALGS = EXTENSION_HELPERS["signature_algorithms"](sigalgs)
|
DEFAULT_SIGALGS = EXTENSION_HELPERS["signature_algorithms"](sigalgs)
|
||||||
end
|
end
|
||||||
|
-- Equivalent for TLSv1.3 is SignatureScheme
|
||||||
|
-- We'll offer all the sha256 and sha512 variants, plus a few extra
|
||||||
|
local DEFAULT_SIGSCHEMES
|
||||||
|
do
|
||||||
|
local sigalgs = {
|
||||||
|
"rsa_pkcs1_sha256",
|
||||||
|
"rsa_pkcs1_sha512",
|
||||||
|
"ecdsa_secp256r1_sha256",
|
||||||
|
"ecdsa_secp521r1_sha512",
|
||||||
|
"rsa_pss_rsae_sha256",
|
||||||
|
"rsa_pss_rsae_sha512",
|
||||||
|
"ed25519",
|
||||||
|
"ed448",
|
||||||
|
"rsa_pss_pss_sha256",
|
||||||
|
"rsa_pss_pss_sha512",
|
||||||
|
"rsa_pkcs1_sha1",
|
||||||
|
"ecdsa_sha1",
|
||||||
|
}
|
||||||
|
DEFAULT_SIGSCHEMES = EXTENSION_HELPERS["signature_algorithms_13"](sigalgs)
|
||||||
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Build a client_hello message
|
-- Build a client_hello message
|
||||||
@@ -1459,10 +1686,11 @@ function client_hello(t)
|
|||||||
-- Set the protocol.
|
-- Set the protocol.
|
||||||
local protocol = t["protocol"] or HIGHEST_PROTOCOL
|
local protocol = t["protocol"] or HIGHEST_PROTOCOL
|
||||||
table.insert(b, pack(">I2 I4",
|
table.insert(b, pack(">I2 I4",
|
||||||
PROTOCOLS[protocol],
|
legacy_version(PROTOCOLS[protocol]),
|
||||||
-- Set the random data.
|
-- Set the random data.
|
||||||
os.time()
|
os.time()
|
||||||
))
|
))
|
||||||
|
local record_proto = t.record_protocol
|
||||||
|
|
||||||
-- Set the random data.
|
-- Set the random data.
|
||||||
table.insert(b, rand.random_string(28))
|
table.insert(b, rand.random_string(28))
|
||||||
@@ -1471,11 +1699,24 @@ function client_hello(t)
|
|||||||
local sid = t["session_id"] or ""
|
local sid = t["session_id"] or ""
|
||||||
table.insert(b, pack(">s1", sid))
|
table.insert(b, pack(">s1", sid))
|
||||||
|
|
||||||
|
local eccpwd = false
|
||||||
|
local shangmi = false
|
||||||
-- Cipher suites.
|
-- Cipher suites.
|
||||||
ciphers = {}
|
ciphers = {}
|
||||||
-- Add specified ciphers.
|
-- Add specified ciphers.
|
||||||
for _, cipher in pairs(t["ciphers"] or DEFAULT_CIPHERS) do
|
for _, cipher in pairs(t.ciphers -- user-specified list
|
||||||
|
or (record_proto == "TLSv1.3" and DEFAULT_TLS13_CIPHERS) -- TLSv1.3 only
|
||||||
|
or (PROTOCOLS[protocol] < PROTOCOLS["TLSv1.3"] and DEFAULT_TLS12_CIPHERS) -- non-TLSv1.3
|
||||||
|
or DEFAULT_CIPHERS) -- combined/compatible handshake
|
||||||
|
do
|
||||||
if type(cipher) == "string" then
|
if type(cipher) == "string" then
|
||||||
|
if cipher:match("^TLS_ECCPWD_") then
|
||||||
|
-- RFC 8492 has specific requirements
|
||||||
|
eccpwd = true
|
||||||
|
elseif protocol == "TLSv1.3" and cipher:match("_SM3$") then
|
||||||
|
-- RFC 8998 has specific requirements
|
||||||
|
shangmi = true
|
||||||
|
end
|
||||||
cipher = CIPHERS[cipher] or SCSVS[cipher]
|
cipher = CIPHERS[cipher] or SCSVS[cipher]
|
||||||
end
|
end
|
||||||
if type(cipher) == "number" and cipher >= 0 and cipher <= 0xffff then
|
if type(cipher) == "number" and cipher >= 0 and cipher <= 0xffff then
|
||||||
@@ -1501,27 +1742,75 @@ function client_hello(t)
|
|||||||
table.insert(b, pack("s1", table.concat(compressors)))
|
table.insert(b, pack("s1", table.concat(compressors)))
|
||||||
|
|
||||||
-- TLS extensions
|
-- TLS extensions
|
||||||
if PROTOCOLS[protocol] and protocol ~= "SSLv3" then
|
local proto_ver = PROTOCOLS[protocol]
|
||||||
|
if proto_ver and protocol ~= "SSLv3" then
|
||||||
local extensions = {}
|
local extensions = {}
|
||||||
if t["extensions"] ~= nil then
|
-- TLSv1.3 requires supported_versions and key_share extensions
|
||||||
-- Do we need to add the signature_algorithms extension?
|
-- OpenSSL also appears to want supported_groups in some cases?
|
||||||
local need_sigalg = (protocol == "TLSv1.2")
|
local need_supported_versions = (proto_ver >= PROTOCOLS["TLSv1.3"])
|
||||||
-- Add specified extensions.
|
local need_key_share = need_supported_versions
|
||||||
|
local need_elliptic_curves = need_supported_versions
|
||||||
|
-- Do we need to add the signature_algorithms extension?
|
||||||
|
local need_sigalg = (proto_ver >= PROTOCOLS["TLSv1.2"])
|
||||||
|
-- Add specified extensions.
|
||||||
|
if t.extensions then
|
||||||
for extension, data in pairs(t["extensions"]) do
|
for extension, data in pairs(t["extensions"]) do
|
||||||
if type(extension) == "number" then
|
if type(extension) == "number" then
|
||||||
table.insert(extensions, pack(">I2", extension))
|
table.insert(extensions, pack(">I2", extension))
|
||||||
else
|
else
|
||||||
if extension == "signature_algorithms" then
|
if extension == "signature_algorithms" or extension == "signature_algorithms_13" then
|
||||||
need_sigalg = false
|
need_sigalg = false
|
||||||
|
if shangmi then
|
||||||
|
local sm2sig_sm3 = pack(">I2", SignatureSchemes.sm2sig_sm3)
|
||||||
|
if not data:match("^..(..)*" .. sm2sig_sm3) then
|
||||||
|
data = pack(">s2", data:sub(3) .. sm2sig_sm3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif extension == "supported_versions" then
|
||||||
|
need_supported_versions = false
|
||||||
|
elseif extension == "key_share" then
|
||||||
|
need_key_share = false
|
||||||
|
elseif extension == "elliptic_curves" then
|
||||||
|
need_elliptic_curves = false
|
||||||
|
if shangmi then
|
||||||
|
-- For now, RFC 8998 is the only one that enforces particular curves
|
||||||
|
local curveSM2 = pack(">I2", ELLIPTIC_CURVES.curveSM2)
|
||||||
|
if not data:match("^..(..)*" .. curveSM2) then
|
||||||
|
data = pack(">s2", data:sub(3) .. curveSM2)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
table.insert(extensions, pack(">I2", EXTENSIONS[extension]))
|
table.insert(extensions, pack(">I2", EXTENSIONS[extension]))
|
||||||
end
|
end
|
||||||
table.insert(extensions, pack(">s2", data))
|
table.insert(extensions, pack(">s2", data))
|
||||||
end
|
end
|
||||||
if need_sigalg then
|
end
|
||||||
table.insert(extensions, pack(">I2", EXTENSIONS["signature_algorithms"]))
|
if need_supported_versions then
|
||||||
table.insert(extensions, pack(">s2", DEFAULT_SIGALGS))
|
table.insert(extensions, pack(">I2", EXTENSIONS["supported_versions"]))
|
||||||
|
-- We'd prefer TLS 1.2 or 1.1, since we've tested our scripts on those.
|
||||||
|
table.insert(extensions, pack(">s2", EXTENSION_HELPERS["supported_versions"]({"TLSv1.2", "TLSv1.1", "TLSv1.3", "SSLv3"})))
|
||||||
|
end
|
||||||
|
if need_sigalg then
|
||||||
|
table.insert(extensions, pack(">I2", EXTENSIONS["signature_algorithms"]))
|
||||||
|
local data = proto_ver >= PROTOCOLS["TLSv1.3"] and DEFAULT_SIGSCHEMES or DEFAULT_SIGALGS
|
||||||
|
if shangmi then
|
||||||
|
data = pack(">s2", data:sub(3) .. pack(">I2", SignatureSchemes.sm2sig_sm3))
|
||||||
end
|
end
|
||||||
|
table.insert(extensions, pack(">s2", data))
|
||||||
|
end
|
||||||
|
if need_key_share then
|
||||||
|
-- RFC 8446: Clients MAY send an empty client_shares vector in order to request
|
||||||
|
-- group selection from the server, at the cost of an additional round trip
|
||||||
|
table.insert(extensions, pack(">I2", EXTENSIONS["key_share"]))
|
||||||
|
table.insert(extensions, pack(">s2", "\0\0"))
|
||||||
|
end
|
||||||
|
if need_elliptic_curves then
|
||||||
|
local curves = {table.unpack(DEFAULT_ELLIPTIC_CURVES)}
|
||||||
|
if shangmi then
|
||||||
|
curves[#curves+1] = "curveSM2"
|
||||||
|
end
|
||||||
|
table.insert(extensions, pack(">I2", EXTENSIONS["elliptic_curves"]))
|
||||||
|
table.insert(extensions, pack(">s2", EXTENSION_HELPERS["elliptic_curves"](curves)))
|
||||||
end
|
end
|
||||||
-- Extensions are optional
|
-- Extensions are optional
|
||||||
if #extensions ~= 0 then
|
if #extensions ~= 0 then
|
||||||
@@ -1548,9 +1837,14 @@ function client_hello(t)
|
|||||||
-- be downgraded by a MITM to SSLv3. So we use TLSv1.0 unless the caller
|
-- be downgraded by a MITM to SSLv3. So we use TLSv1.0 unless the caller
|
||||||
-- explicitly tries to set SSLv3.0 somewhere (t.record_protocol or
|
-- explicitly tries to set SSLv3.0 somewhere (t.record_protocol or
|
||||||
-- t.protocol)
|
-- t.protocol)
|
||||||
local record_proto = t.record_protocol
|
|
||||||
if not record_proto then
|
if not record_proto then
|
||||||
record_proto = (t.protocol == "SSLv3") and "SSLv3" or "TLSv1.0"
|
record_proto = (t.protocol == "SSLv3") and "SSLv3" or "TLSv1.0"
|
||||||
|
elseif record_proto == "TLSv1.3" then
|
||||||
|
-- RFC 8446: "MUST be set to 0x0303 for all records generated by a TLS 1.3
|
||||||
|
-- implementation other than an initial ClientHello (i.e., one not generated
|
||||||
|
-- after a HelloRetryRequest), where it MAY also be 0x0301 for compatibility
|
||||||
|
-- purposes.
|
||||||
|
record_proto = "TLSv1.2"
|
||||||
end
|
end
|
||||||
return record_write("handshake", record_proto, table.concat(h))
|
return record_write("handshake", record_proto, table.concat(h))
|
||||||
end
|
end
|
||||||
@@ -1624,4 +1918,12 @@ function servername(host)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local unittest = require "unittest"
|
||||||
|
if not unittest.testing() then
|
||||||
|
return _ENV
|
||||||
|
end
|
||||||
|
test_suite = unittest.TestSuite:new()
|
||||||
|
for name, code in pairs(CIPHERS) do
|
||||||
|
test_suite:add_test(unittest.not_nil(cipher_info(name).kex), name .. ".kex")
|
||||||
|
end
|
||||||
return _ENV;
|
return _ENV;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ local client_hello = function(host, port)
|
|||||||
status, err = sock:connect(host, port)
|
status, err = sock:connect(host, port)
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
stdnse.debug("Can't send: %s", err)
|
stdnse.debug("Can't connect: %s", err)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -86,23 +86,24 @@ local client_hello = function(host, port)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Send Client Hello to the target server
|
repeat -- only once
|
||||||
status, err = sock:send(cli_h)
|
-- Send Client Hello to the target server
|
||||||
if not status then
|
status, err = sock:send(cli_h)
|
||||||
stdnse.debug("Couldn't send: %s", err)
|
if not status then
|
||||||
sock:close()
|
stdnse.debug("Couldn't send: %s", err)
|
||||||
return false
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read response
|
-- Read response
|
||||||
status, response, err = tls.record_buffer(sock)
|
status, response, err = tls.record_buffer(sock)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.debug("Couldn't receive: %s", err)
|
stdnse.debug("Couldn't receive: %s", err)
|
||||||
sock:close()
|
break
|
||||||
return false
|
end
|
||||||
end
|
until true
|
||||||
|
|
||||||
return true, response
|
sock:close()
|
||||||
|
return status, response
|
||||||
end
|
end
|
||||||
|
|
||||||
-- extract time from ServerHello response
|
-- extract time from ServerHello response
|
||||||
|
|||||||
@@ -883,6 +883,11 @@ parameters.]],
|
|||||||
}
|
}
|
||||||
|
|
||||||
for protocol in pairs(tls.PROTOCOLS) do
|
for protocol in pairs(tls.PROTOCOLS) do
|
||||||
|
if protocol == "TLSv1.3" then
|
||||||
|
-- TLSv1.3 does not allow anonymous key exchange and only allows specific
|
||||||
|
-- DHE groups named in RFC 7919
|
||||||
|
goto NEXT_PROTOCOL
|
||||||
|
end
|
||||||
-- Try anonymous DH ciphersuites
|
-- Try anonymous DH ciphersuites
|
||||||
cipher, dhparams = get_dhe_params(host, port, protocol, dh_anons)
|
cipher, dhparams = get_dhe_params(host, port, protocol, dh_anons)
|
||||||
-- Explicit test for false needed because nil just means no ciphers supported.
|
-- Explicit test for false needed because nil just means no ciphers supported.
|
||||||
|
|||||||
@@ -400,7 +400,10 @@ local function try_params(host, port, t)
|
|||||||
for j = 1, #record.body do -- no ipairs because we append below
|
for j = 1, #record.body do -- no ipairs because we append below
|
||||||
local b = record.body[j]
|
local b = record.body[j]
|
||||||
done = ((record.type == "alert" and b.level == "fatal") or
|
done = ((record.type == "alert" and b.level == "fatal") or
|
||||||
(record.type == "handshake" and b.type == "server_hello_done"))
|
(record.type == "handshake" and (b.type == "server_hello_done" or
|
||||||
|
-- TLSv1.3 does not have server_hello_done
|
||||||
|
(t.protocol == "TLSv1.3" and b.type == "server_hello")))
|
||||||
|
)
|
||||||
table.insert(records[record.type].body, b)
|
table.insert(records[record.type].body, b)
|
||||||
end
|
end
|
||||||
if done then
|
if done then
|
||||||
@@ -544,7 +547,7 @@ local function score_cipher (kex_strength, cipher_info)
|
|||||||
if not kex_strength or not cipher_info.size then
|
if not kex_strength or not cipher_info.size then
|
||||||
return "unknown"
|
return "unknown"
|
||||||
end
|
end
|
||||||
if kex_strength == 0 then
|
if kex_strength <= 0 then
|
||||||
return 0
|
return 0
|
||||||
elseif kex_strength < 512 then
|
elseif kex_strength < 512 then
|
||||||
kex_score = 0.2
|
kex_score = 0.2
|
||||||
@@ -558,7 +561,7 @@ local function score_cipher (kex_strength, cipher_info)
|
|||||||
kex_score = 1.0
|
kex_score = 1.0
|
||||||
end
|
end
|
||||||
|
|
||||||
if cipher_info.size == 0 then
|
if cipher_info.size <= 0 then
|
||||||
return 0
|
return 0
|
||||||
elseif cipher_info.size < 128 then
|
elseif cipher_info.size < 128 then
|
||||||
cipher_score = 0.2
|
cipher_score = 0.2
|
||||||
@@ -589,14 +592,27 @@ local function letter_grade (score)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local tls13proto = tls.PROTOCOLS["TLSv1.3"]
|
||||||
|
local tls13supported = tls.EXTENSION_HELPERS.supported_versions({"TLSv1.3"})
|
||||||
|
local function get_hello_table(host, protocol)
|
||||||
|
local t = {
|
||||||
|
protocol = protocol,
|
||||||
|
record_protocol = protocol, -- improve chances of immediate rejection
|
||||||
|
extensions = base_extensions(host),
|
||||||
|
}
|
||||||
|
|
||||||
|
-- supported_versions extension required for TLSv1.3
|
||||||
|
if (tls.PROTOCOLS[protocol] >= tls13proto) then
|
||||||
|
t.extensions.supported_versions = tls13supported
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
-- Find which ciphers out of group are supported by the server.
|
-- Find which ciphers out of group are supported by the server.
|
||||||
local function find_ciphers_group(host, port, protocol, group, scores)
|
local function find_ciphers_group(host, port, protocol, group, scores)
|
||||||
local results = {}
|
local results = {}
|
||||||
local t = {
|
local t = get_hello_table(host, protocol)
|
||||||
["protocol"] = protocol,
|
|
||||||
["record_protocol"] = protocol, -- improve chances of immediate rejection
|
|
||||||
["extensions"] = base_extensions(host),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- This is a hacky sort of tristate variable. There are three conditions:
|
-- This is a hacky sort of tristate variable. There are three conditions:
|
||||||
-- 1. false = either ciphers or protocol is bad. Keep trying with new ciphers
|
-- 1. false = either ciphers or protocol is bad. Keep trying with new ciphers
|
||||||
@@ -616,14 +632,15 @@ local function find_ciphers_group(host, port, protocol, group, scores)
|
|||||||
local alert = records.alert
|
local alert = records.alert
|
||||||
if alert then
|
if alert then
|
||||||
ctx_log(2, protocol, "Got alert: %s", alert.body[1].description)
|
ctx_log(2, protocol, "Got alert: %s", alert.body[1].description)
|
||||||
if alert["protocol"] ~= protocol then
|
if not tls.record_version_ok(alert["protocol"], protocol) then
|
||||||
ctx_log(1, protocol, "Protocol mismatch (received %s)", alert.protocol)
|
ctx_log(1, protocol, "Protocol mismatch (received %s)", alert.protocol)
|
||||||
-- Sometimes this is not an actual rejection of the protocol. Check specifically:
|
-- Sometimes this is not an actual rejection of the protocol. Check specifically:
|
||||||
if get_body(alert, "description", "protocol_version") then
|
if get_body(alert, "description", "protocol_version") then
|
||||||
protocol_worked = nil
|
protocol_worked = nil
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
elseif get_body(alert, "description", "handshake_failure") then
|
elseif get_body(alert, "description", "handshake_failure")
|
||||||
|
or get_body(alert, "description", "insufficient_security") then
|
||||||
protocol_worked = true
|
protocol_worked = true
|
||||||
ctx_log(2, protocol, "%d ciphers rejected.", #group)
|
ctx_log(2, protocol, "%d ciphers rejected.", #group)
|
||||||
break
|
break
|
||||||
@@ -680,98 +697,97 @@ local function find_ciphers_group(host, port, protocol, group, scores)
|
|||||||
elseif info.cipher == "RC4" then
|
elseif info.cipher == "RC4" then
|
||||||
scores.warnings["Broken cipher RC4 is deprecated by RFC 7465"] = true
|
scores.warnings["Broken cipher RC4 is deprecated by RFC 7465"] = true
|
||||||
end
|
end
|
||||||
|
if protocol == "TLSv1.3" and not info.tls13ok then
|
||||||
|
scores.warnings["Non-TLSv1.3 ciphersuite chosen for TLSv1.3"] = true
|
||||||
|
end
|
||||||
local kex = tls.KEX_ALGORITHMS[info.kex]
|
local kex = tls.KEX_ALGORITHMS[info.kex]
|
||||||
scores.any_pfs_ciphers = kex.pfs or scores.any_pfs_ciphers
|
scores.any_pfs_ciphers = kex.pfs or scores.any_pfs_ciphers
|
||||||
local extra, kex_strength
|
local extra, kex_strength
|
||||||
if kex.anon then
|
if kex.export then
|
||||||
kex_strength = 0
|
scores.warnings["Export key exchange"] = true
|
||||||
elseif kex.export then
|
|
||||||
if info.kex:find("1024$") then
|
if info.kex:find("1024$") then
|
||||||
kex_strength = 1024
|
kex_strength = 1024
|
||||||
else
|
else
|
||||||
kex_strength = 512
|
kex_strength = 512
|
||||||
end
|
end
|
||||||
else
|
end
|
||||||
if have_ssl and kex.pubkey then
|
if kex.anon then
|
||||||
local certs = get_body(handshake, "type", "certificate")
|
scores.warnings["Anonymous key exchange, score capped at F"] = true
|
||||||
-- Assume RFC compliance:
|
kex_strength = 0
|
||||||
-- "The sender's certificate MUST come first in the list."
|
elseif have_ssl and kex.pubkey then
|
||||||
-- This may not always be the case, so
|
local certs = get_body(handshake, "type", "certificate")
|
||||||
-- TODO: reorder certificates and validate entire chain
|
-- Assume RFC compliance:
|
||||||
-- TODO: certificate validation (date, self-signed, etc)
|
-- "The sender's certificate MUST come first in the list."
|
||||||
local c, err
|
-- This may not always be the case, so
|
||||||
if certs == nil then
|
-- TODO: reorder certificates and validate entire chain
|
||||||
err = "no certificate message"
|
-- TODO: certificate validation (date, self-signed, etc)
|
||||||
else
|
local c, err
|
||||||
c, err = sslcert.parse_ssl_certificate(certs.certificates[1])
|
if certs == nil then
|
||||||
|
err = "no certificate message"
|
||||||
|
else
|
||||||
|
c, err = sslcert.parse_ssl_certificate(certs.certificates[1])
|
||||||
|
end
|
||||||
|
if not c then
|
||||||
|
ctx_log(1, protocol, "Failed to parse certificate: %s", err)
|
||||||
|
elseif c.pubkey.type == kex.pubkey then
|
||||||
|
local sigalg = c.sig_algorithm:match("([mM][dD][245])") or c.sig_algorithm:match("([sS][hH][aA]1)")
|
||||||
|
if sigalg then
|
||||||
|
kex_strength = 0
|
||||||
|
scores.warnings[("Insecure certificate signature (%s), score capped at F"):format(string.upper(sigalg))] = true
|
||||||
end
|
end
|
||||||
if not c then
|
local rsa_bits = tls.rsa_equiv(kex.pubkey, c.pubkey.bits)
|
||||||
stdnse.debug1("Failed to parse certificate: %s", err)
|
kex_strength = math.min(kex_strength or rsa_bits, rsa_bits)
|
||||||
elseif c.pubkey.type == kex.pubkey then
|
if c.pubkey.exponent then
|
||||||
local sigalg = c.sig_algorithm:match("([mM][dD][245])")
|
if openssl.bignum_bn2dec(c.pubkey.exponent) == "1" then
|
||||||
if sigalg then
|
|
||||||
-- MD2 and MD5 are broken
|
|
||||||
kex_strength = 0
|
kex_strength = 0
|
||||||
scores.warnings["Insecure certificate signature: " .. string.upper(sigalg)] = true
|
scores.warnings["Certificate RSA exponent is 1, score capped at F"] = true
|
||||||
else
|
|
||||||
sigalg = c.sig_algorithm:match("([sS][hH][aA]1)")
|
|
||||||
if sigalg then
|
|
||||||
-- TODO: Update this when SHA-1 is fully deprecated in 2017
|
|
||||||
if type(c.notBefore) == "table" and c.notBefore.year >= 2016 then
|
|
||||||
kex_strength = 0
|
|
||||||
scores.warnings["Deprecated SHA1 signature in certificate issued after January 1, 2016"] = true
|
|
||||||
end
|
|
||||||
scores.warnings["Weak certificate signature: SHA1"] = true
|
|
||||||
end
|
|
||||||
kex_strength = tls.rsa_equiv(kex.pubkey, c.pubkey.bits)
|
|
||||||
if c.pubkey.exponent then
|
|
||||||
if openssl.bignum_bn2dec(c.pubkey.exponent) == "1" then
|
|
||||||
kex_strength = 0
|
|
||||||
scores.warnings["Certificate RSA exponent is 1, score capped at F"] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if c.pubkey.ecdhparams then
|
|
||||||
if c.pubkey.ecdhparams.curve_params.ec_curve_type == "namedcurve" then
|
|
||||||
extra = c.pubkey.ecdhparams.curve_params.curve
|
|
||||||
else
|
|
||||||
extra = string.format("%s %d", c.pubkey.ecdhparams.curve_params.ec_curve_type, c.pubkey.bits)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
extra = string.format("%s %d", kex.pubkey, c.pubkey.bits)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if c.pubkey.ecdhparams then
|
||||||
|
if c.pubkey.ecdhparams.curve_params.ec_curve_type == "namedcurve" then
|
||||||
|
extra = c.pubkey.ecdhparams.curve_params.curve
|
||||||
|
else
|
||||||
|
extra = string.format("%s %d", c.pubkey.ecdhparams.curve_params.ec_curve_type, c.pubkey.bits)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
extra = string.format("%s %d", kex.pubkey, c.pubkey.bits)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
local ske = get_body(handshake, "type", "server_key_exchange")
|
end
|
||||||
if kex.server_key_exchange and ske then
|
local ske
|
||||||
local kex_info = kex.server_key_exchange(ske.data, protocol)
|
if protocol == "TLSv1.3" then
|
||||||
if kex_info.strength then
|
ske = server_hello.extensions.key_share
|
||||||
local rsa_bits = tls.rsa_equiv(kex.type, kex_info.strength)
|
elseif kex.server_key_exchange then
|
||||||
local low_strength_warning = false
|
ske = get_body(handshake, "type", "server_key_exchange")
|
||||||
if kex_strength and kex_strength > rsa_bits then
|
if ske then
|
||||||
kex_strength = rsa_bits
|
ske = ske.data
|
||||||
low_strength_warning = true
|
end
|
||||||
end
|
end
|
||||||
kex_strength = kex_strength or rsa_bits
|
if ske then
|
||||||
if kex_info.ecdhparams then
|
local kex_info = kex.server_key_exchange(ske, protocol)
|
||||||
if kex_info.ecdhparams.curve_params.ec_curve_type == "namedcurve" then
|
if kex_info.strength then
|
||||||
extra = kex_info.ecdhparams.curve_params.curve
|
local kex_type = kex_info.type or kex.type
|
||||||
else
|
if kex_info.ecdhparams then
|
||||||
extra = string.format("%s %d", kex_info.ecdhparams.curve_params.ec_curve_type, kex_info.strength)
|
if kex_info.ecdhparams.curve_params.ec_curve_type == "namedcurve" then
|
||||||
end
|
extra = kex_info.ecdhparams.curve_params.curve
|
||||||
else
|
else
|
||||||
extra = string.format("%s %d", kex.type, kex_info.strength)
|
extra = string.format("%s %d", kex_info.ecdhparams.curve_params.ec_curve_type, kex_info.strength)
|
||||||
end
|
|
||||||
if low_strength_warning then
|
|
||||||
scores.warnings[(
|
|
||||||
"Key exchange (%s) of lower strength than certificate key"
|
|
||||||
):format(extra)] = true
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
extra = string.format("%s %d", kex_type, kex_info.strength)
|
||||||
end
|
end
|
||||||
if kex_info.rsa and kex_info.rsa.exponent == 1 then
|
local rsa_bits = tls.rsa_equiv(kex_type, kex_info.strength)
|
||||||
kex_strength = 0
|
if kex_strength and kex_strength > rsa_bits then
|
||||||
scores.warnings["Certificate RSA exponent is 1, score capped at F"] = true
|
kex_strength = rsa_bits
|
||||||
|
scores.warnings[(
|
||||||
|
"Key exchange (%s) of lower strength than certificate key"
|
||||||
|
):format(extra)] = true
|
||||||
end
|
end
|
||||||
|
kex_strength = math.min(kex_strength or rsa_bits, rsa_bits)
|
||||||
|
end
|
||||||
|
if kex_info.rsa and kex_info.rsa.exponent == 1 then
|
||||||
|
kex_strength = 0
|
||||||
|
scores.warnings["Certificate RSA exponent is 1, score capped at F"] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
scores[name] = {
|
scores[name] = {
|
||||||
@@ -791,11 +807,8 @@ end
|
|||||||
local function get_chunk_size(host, protocol)
|
local function get_chunk_size(host, protocol)
|
||||||
-- Try to make sure we don't send too big of a handshake
|
-- Try to make sure we don't send too big of a handshake
|
||||||
-- https://github.com/ssllabs/research/wiki/Long-Handshake-Intolerance
|
-- https://github.com/ssllabs/research/wiki/Long-Handshake-Intolerance
|
||||||
local len_t = {
|
local len_t = get_hello_table(host, protocol)
|
||||||
protocol = protocol,
|
len_t.ciphers = {}
|
||||||
ciphers = {},
|
|
||||||
extensions = base_extensions(host),
|
|
||||||
}
|
|
||||||
local cipher_len_remaining = 255 - #tls.client_hello(len_t)
|
local cipher_len_remaining = 255 - #tls.client_hello(len_t)
|
||||||
-- if we're over 255 anyway, just go for it.
|
-- if we're over 255 anyway, just go for it.
|
||||||
-- Each cipher adds 2 bytes
|
-- Each cipher adds 2 bytes
|
||||||
@@ -809,7 +822,17 @@ end
|
|||||||
-- each chunk.
|
-- each chunk.
|
||||||
local function find_ciphers(host, port, protocol)
|
local function find_ciphers(host, port, protocol)
|
||||||
|
|
||||||
local ciphers = in_chunks(sorted_keys(tls.CIPHERS), get_chunk_size(host, protocol))
|
local candidates = {}
|
||||||
|
-- TLSv1.3 ciphers are different, though some are shared (ECCPWD)
|
||||||
|
local tls13 = protocol == "TLSv1.3"
|
||||||
|
for _, c in ipairs(sorted_keys(tls.CIPHERS)) do
|
||||||
|
local info = tls.cipher_info(c)
|
||||||
|
if (not tls13 and not info.tls13only)
|
||||||
|
or (tls13 and info.tls13ok) then
|
||||||
|
candidates[#candidates+1] = c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local ciphers = in_chunks(candidates, get_chunk_size(host, protocol))
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
local scores = {warnings={}}
|
local scores = {warnings={}}
|
||||||
@@ -830,11 +853,8 @@ end
|
|||||||
|
|
||||||
local function find_compressors(host, port, protocol, good_ciphers)
|
local function find_compressors(host, port, protocol, good_ciphers)
|
||||||
local compressors = sorted_keys(tls.COMPRESSORS)
|
local compressors = sorted_keys(tls.COMPRESSORS)
|
||||||
local t = {
|
local t = get_hello_table(host, protocol)
|
||||||
["protocol"] = protocol,
|
t.ciphers = good_ciphers
|
||||||
["ciphers"] = good_ciphers,
|
|
||||||
["extensions"] = base_extensions(host),
|
|
||||||
}
|
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
|
|
||||||
@@ -852,7 +872,7 @@ local function find_compressors(host, port, protocol, good_ciphers)
|
|||||||
local alert = records.alert
|
local alert = records.alert
|
||||||
if alert then
|
if alert then
|
||||||
ctx_log(2, protocol, "Got alert: %s", alert.body[1].description)
|
ctx_log(2, protocol, "Got alert: %s", alert.body[1].description)
|
||||||
if alert["protocol"] ~= protocol then
|
if not tls.record_version_ok(alert["protocol"], protocol) then
|
||||||
ctx_log(1, protocol, "Protocol rejected.")
|
ctx_log(1, protocol, "Protocol rejected.")
|
||||||
protocol_worked = nil
|
protocol_worked = nil
|
||||||
break
|
break
|
||||||
@@ -908,11 +928,8 @@ end
|
|||||||
-- Offer two ciphers and return the one chosen by the server. Returns nil and
|
-- Offer two ciphers and return the one chosen by the server. Returns nil and
|
||||||
-- an error message in case of a server error.
|
-- an error message in case of a server error.
|
||||||
local function compare_ciphers(host, port, protocol, cipher_a, cipher_b)
|
local function compare_ciphers(host, port, protocol, cipher_a, cipher_b)
|
||||||
local t = {
|
local t = get_hello_table(host, protocol)
|
||||||
["protocol"] = protocol,
|
t.ciphers = {cipher_a, cipher_b}
|
||||||
["ciphers"] = {cipher_a, cipher_b},
|
|
||||||
["extensions"] = base_extensions(host),
|
|
||||||
}
|
|
||||||
local records = try_params(host, port, t)
|
local records = try_params(host, port, t)
|
||||||
local server_hello = records.handshake and get_body(records.handshake, "type", "server_hello")
|
local server_hello = records.handshake and get_body(records.handshake, "type", "server_hello")
|
||||||
if server_hello then
|
if server_hello then
|
||||||
@@ -1010,14 +1027,18 @@ local function try_protocol(host, port, protocol, upresults)
|
|||||||
end
|
end
|
||||||
-- Find all valid compression methods.
|
-- Find all valid compression methods.
|
||||||
local compressors
|
local compressors
|
||||||
-- Reduce chunk size by 1 to allow extra room for the extra compressors (2 bytes)
|
-- RFC 8446: "For every TLS 1.3 ClientHello, this vector MUST contain exactly
|
||||||
for _, c in ipairs(in_chunks(ciphers, get_chunk_size(host, protocol) - 1)) do
|
-- one byte, set to zero"
|
||||||
compressors = find_compressors(host, port, protocol, c)
|
if (tls.PROTOCOLS[protocol] < tls13proto) then
|
||||||
-- I observed a weird interaction between ECDSA ciphers and DEFLATE compression.
|
-- Reduce chunk size by 1 to allow extra room for the extra compressors (2 bytes)
|
||||||
-- Some servers would reject the handshake if no non-ECDSA ciphers were available.
|
for _, c in ipairs(in_chunks(ciphers, get_chunk_size(host, protocol) - 1)) do
|
||||||
-- Sending 64 ciphers at a time should be sufficient, but we'll try them all if necessary.
|
compressors = find_compressors(host, port, protocol, c)
|
||||||
if compressors and #compressors ~= 0 then
|
-- I observed a weird interaction between ECDSA ciphers and DEFLATE compression.
|
||||||
break
|
-- Some servers would reject the handshake if no non-ECDSA ciphers were available.
|
||||||
|
-- Sending 64 ciphers at a time should be sufficient, but we'll try them all if necessary.
|
||||||
|
if compressors and #compressors ~= 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
|
|||||||
categories = { "vuln", "safe" }
|
categories = { "vuln", "safe" }
|
||||||
dependencies = {"https-redirect"}
|
dependencies = {"https-redirect"}
|
||||||
|
|
||||||
|
-- TLSv1.3 was not implemented by affected versions of OpenSSL.
|
||||||
local arg_protocols = stdnse.get_script_args(SCRIPT_NAME .. ".protocols") or {'TLSv1.0', 'TLSv1.1', 'TLSv1.2'}
|
local arg_protocols = stdnse.get_script_args(SCRIPT_NAME .. ".protocols") or {'TLSv1.0', 'TLSv1.1', 'TLSv1.2'}
|
||||||
|
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ your TLS ciphersuites.
|
|||||||
-- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3566
|
-- | https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3566
|
||||||
-- |_ https://www.openssl.org/~bodo/ssl-poodle.pdf
|
-- |_ https://www.openssl.org/~bodo/ssl-poodle.pdf
|
||||||
--
|
--
|
||||||
|
-- @see ssl-enum-ciphers.nse
|
||||||
|
|
||||||
author = "Daniel Miller"
|
author = "Daniel Miller"
|
||||||
|
|
||||||
|
|||||||
@@ -56,10 +56,13 @@ local client_hello = function(host, port, protos)
|
|||||||
local sock, status, response, err, cli_h
|
local sock, status, response, err, cli_h
|
||||||
|
|
||||||
cli_h = tls.client_hello({
|
cli_h = tls.client_hello({
|
||||||
["extensions"] = {
|
-- TLSv1.3 does not send this extension plaintext.
|
||||||
[ALPN_NAME] = tls.EXTENSION_HELPERS[ALPN_NAME](protos)
|
-- TODO: implement key exchange crypto to retrieve encrypted extensions
|
||||||
},
|
protocol = "TLSv1.2",
|
||||||
})
|
["extensions"] = {
|
||||||
|
[ALPN_NAME] = tls.EXTENSION_HELPERS[ALPN_NAME](protos)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
-- Connect to the target server
|
-- Connect to the target server
|
||||||
local status, err
|
local status, err
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ local client_hello = function(host, port)
|
|||||||
local sock, status, response, err, cli_h
|
local sock, status, response, err, cli_h
|
||||||
|
|
||||||
cli_h = tls.client_hello({
|
cli_h = tls.client_hello({
|
||||||
|
-- TLSv1.3 does not send this extension plaintext.
|
||||||
|
-- TODO: implement key exchange crypto to retrieve encrypted extensions
|
||||||
|
protocol = "TLSv1.2",
|
||||||
["extensions"] = {
|
["extensions"] = {
|
||||||
["next_protocol_negotiation"] = "",
|
["next_protocol_negotiation"] = "",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user