SSL/TLS: Version Detection
🗓️ 29 Jun 2016 00:00:00Reported by Copyright (C) 2016 Greenbone AGType
openvas🔗 plugins.openvas.org👁 200 Views
# SPDX-FileCopyrightText: 2016 Greenbone AG
# Some text descriptions might be excerpted from (a) referenced
# source(s), and are Copyright (C) by the respective right holder(s).
#
# SPDX-License-Identifier: GPL-2.0-only
if(description)
{
script_oid("1.3.6.1.4.1.25623.1.0.105782");
script_version("2026-05-22T06:54:41+0000");
script_tag(name:"last_modification", value:"2026-05-22 06:54:41 +0000 (Fri, 22 May 2026)");
script_tag(name:"creation_date", value:"2016-06-29 10:54:20 +0200 (Wed, 29 Jun 2016)");
script_tag(name:"cvss_base", value:"0.0");
script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:N/I:N/A:N");
script_name("SSL/TLS: Version Detection");
script_category(ACT_GATHER_INFO);
script_family("SSL and TLS");
script_copyright("Copyright (C) 2016 Greenbone AG");
script_dependencies("secpod_open_tcp_ports.nasl", "gb_starttls_pop3.nasl",
"gb_starttls_imap.nasl", "gb_starttls_ftp.nasl", "gb_starttls_smtp.nasl",
"gb_postgresql_tls_support.nasl", "gb_starttls_ldap.nasl",
"gb_starttls_nntp.nasl", "gb_starttls_xmpp.nasl", "gb_starttls_mysql.nasl",
"gb_starttls_irc.nasl", "gb_starttls_rdp.nasl",
"gb_dont_scan_fragile_device.nasl");
script_mandatory_keys("TCP/PORTS");
script_add_preference(name:"Seconds to wait between probes", value:"", type:"entry", id:1);
script_tag(name:"summary", value:"Enumeration and reporting of SSL/TLS protocol versions supported
by a remote service.");
script_tag(name:"vuldetect", value:"Sends multiple SSL/TLS client hello messages to the remote
service and attempts to determine the SSL/TLS protocol versions supported by the service from the
received SSL/TLS server hello message.
Notes:
- The supported SSL/TLS protocol versions included in the report of this VT are reported
independently from the allowed / supported SSL/TLS ciphers.
- Additional debug logging can be enabled by setting the preferences 'Log verbosity' to 'Debug'
and 'Debug level' to '1' within the VT 'Global variable settings'
(OID: 1.3.6.1.4.1.25623.1.0.12288). The relevant logs will end up in the scanner log file.");
script_tag(name:"qod_type", value:"remote_banner");
exit(0);
}
include("mysql.inc");
include("ssl_funcs.inc");
include("byte_func.inc");
include("misc_func.inc");
include("list_array_func.inc");
include("host_details.inc");
include("global_settings.inc");
function get_tls_hello_record( vers, port, use_extended_ec, delay, use_sni, sni_name ) {
local_var vers, port, use_extended_ec, delay, use_sni, sni_name;
local_var soc, hello, data, search, record, alert_record;
if( ! vers || ! port )
return;
if( ! soc = open_ssl_socket( port:port ) )
return;
if( use_sni )
hello = ssl_hello( port:port, version:vers, use_extended_ec:use_extended_ec, extensions:make_list( "sni" ), sni_name:sni_name );
else
hello = ssl_hello( port:port, version:vers, use_extended_ec:use_extended_ec );
if( ! hello ) {
close( soc );
return;
}
if( delay )
sleep( delay );
else
usleep( 50000 );
send( socket:soc, data:hello );
data = ssl_recv( socket:soc );
close( soc );
if( ! data )
return;
if( vers == SSL_v2 )
search = make_array( "content_typ", SSLv2_SERVER_HELLO );
else if( vers == TLS_13 )
search = make_array( "supported_versions", TLS_13 );
else
search = make_array( "handshake_typ", SSLv3_SERVER_HELLO );
if( ! record = search_ssl_record( data:data, search:search ) ) {
# For SSLv2 we're returning directly when not receiving an SERVER_HELLO.
if( vers == SSL_v2 )
return;
# For all other versions we're checking if we have received an SSLv3_ALERT so
# that our VT can check e.g. with an extended set of elliptic curves again.
# This also saves a few requests against Non-TLS services for such cases because
# we're only sending the additional requests if received such an SSLv3_ALERT.
alert_record = search_ssl_record( data:data, search:make_array( "content_typ", SSLv3_ALERT ) );
if( alert_record ) {
if( ! isnull( alert_record["description"] ) &&
alert_record["description"] == SSLv3_ALERT_UNRECOGNIZED_NAME )
return "alert_unrecognized_name";
return "alert_received";
} else
return;
} else {
return record;
}
}
# @brief Detects if a parsed ServerHello response indicates TLS 1.3.
#
# @param record A record parsed via read_ssl_record / search_ssl_record.
#
# @return TRUE if TLS 1.3 is indicated, FALSE otherwise.
#
function tls_record_indicates_tls13( record ) {
local_var record;
local_var cipher_spec;
if( ! is_array( record ) )
return FALSE;
# RFC 8446 selected_version extension
if( ! isnull( record["supported_versions"] ) && record["supported_versions"] == TLS_13 )
return TRUE;
# TLS 1.3 ciphers use codepoints 0x1300..0x13ff
if( ! isnull( record["cipher_spec"] ) ) {
cipher_spec = int( record["cipher_spec"] );
if( cipher_spec >= 0x1300 && cipher_spec <= 0x13ff )
return TRUE;
}
return FALSE;
}
# nb: Don't use tcp_get_all_port() as we only want to exclude
# specific ports from the TLS checks defined in gb_dont_scan_fragile_device.nasl
port = get_kb_item( "TCP/PORTS" );
if( ! port || ! get_port_state( port ) )
exit( 0 );
# nb: Set by gb_dont_scan_fragile_device.nasl. Some devices are even crashing
# if we're touching one or more ports of them with our SSL/TLS checks so those
# ports gets excluded here.
if( get_kb_item( "fragile_port/exclude_tls/" + port ) )
exit( 0 );
d = script_get_preference( "Seconds to wait between probes", id:1 );
if( int( d ) > 0 )
delay = int( d );
sni_name = tls_get_sni_name();
sni_ctx = tls_get_sni_context_key( sni_name:sni_name );
use_sni_default = FALSE;
if( ! isnull( sni_name ) && sni_name )
use_sni_default = TRUE;
sup_tls = ""; # nb: To make openvas-nasl-lint happy...
# nb: Used later for doing the reporting
sup_tls_list = make_list();
foreach vers( make_list( TLS_10, TLS_11, TLS_12, TLS_13, SSL_v2, SSL_v3 ) ) {
extended_ec_used = "no";
# Resolve SNI once per VT run and reuse it across all protocol probes.
use_sni = use_sni_default;
record = get_tls_hello_record( vers:vers, port:port, use_extended_ec:FALSE, delay:delay, use_sni:use_sni, sni_name:sni_name );
# Some virtual hosts reject server_name; retry once without SNI for compatibility.
if( use_sni && ! isnull( record ) && ! is_array( record ) && record == "alert_unrecognized_name" ) {
debug_print( "SSL/TLS Alert received for SSL/TLS version probe with version: '" + version_string[vers] + '" and SNI extension. Trying without SNI.' );
record = get_tls_hello_record( vers:vers, port:port, use_extended_ec:FALSE, delay:delay, use_sni:FALSE );
}
if( ! record || ( ! is_array( record ) && record == "alert_received" ) ) {
# nb: Try with an extended set of "elliptic curves" (see _ssl3_tls_hello and
# add_ssl_extension of ssl_funcs.inc). This isn't added in the first request
# for backwards compatibility reasons (e.g. a system might throw an SSL Alert
# if unsupported elliptic curves are requested / included in the Client Hello.
# We're only requesting this for TLS 1.0 to 1.2. TLS 1.3 does not support
# the "elliptic curves" extension any more and has mandatory support for
# a "supported groups" extension with the same codepoint instead.
if( vers == TLS_10 || vers == TLS_11 || vers == TLS_12 ) {
debug_print( "SSL/TLS Alert received for SSL/TLS version probe with version: '" + version_string[vers] + '" and standard elliptic curves. Trying with an extended set of elliptic curves.' );
record = get_tls_hello_record( vers:vers, port:port, use_extended_ec:TRUE, delay:delay, use_sni:use_sni, sni_name:sni_name );
# Keep the same fallback behavior when the extended-EC probe is used.
if( use_sni && ! isnull( record ) && ! is_array( record ) && record == "alert_unrecognized_name" ) {
record = get_tls_hello_record( vers:vers, port:port, use_extended_ec:TRUE, delay:delay, use_sni:FALSE );
debug_print( "SSL/TLS Alert received for SSL/TLS version probe with version: '" + version_string[vers] + '", extended elliptic curves and SNI extension. Trying without SNI.' );
}
extended_ec_used = "yes";
}
if( ! record || ( ! is_array( record ) && record == "alert_received" ) ) {
debug_print( "SSL/TLS Alert received for SSL/TLS version probe with version: '" + version_string[vers] + '"' );
continue;
}
}
# nb: Ignore SSLv2 without ciphers
if( vers == SSL_v2 && int( record["cipher_spec_len"] ) < 1 ) {
debug_print( "Ignore SSLv2 without ciphers" );
continue;
}
if( isnull( record["version"] ) ) {
debug_print( "No valid SSL/TLS version received in SSL/TLS record, ignoring" );
continue;
}
# Some TLS 1.3 servers in compatibility mode can answer a TLS 1.2 probe with
# TLS 1.3 semantics. Avoid incorrectly marking TLS 1.2 in this case.
if( vers == TLS_12 && tls_record_indicates_tls13( record:record ) ) {
debug_print( "Ignore TLS 1.2 probe with TLS 1.3 semantics" );
continue;
}
if( record["version"] == vers ) {
debug_report_array = make_array();
foreach record_name( keys( record ) ) {
# nb: Should be irrelevant...
if( record_name == "session_id" || record_name == "random" )
continue;
value = record[record_name];
if( record_name == "version" )
value = hexstr( record["version"] ) + " (" + version_string[vers] + ")";
# nb: Currently this is an int (e.g. 771 = 0303) so need to convert
if( record_name == "handshake_version" ) {
hex = dec2hex( num:record["handshake_version"] );
value = hexstr( hex ) + " (" + version_string[hex] + ")";
}
if( record_name == "extension_ec_point_formats" ) {
ec_pf_list = record["extension_ec_point_formats"];
ec_pf_report = "";
foreach ec_pf( ec_pf_list ) {
i++;
if( i == 1 )
ec_pf_report += hexstr( ec_pf );
else
ec_pf_report += ", " + hexstr( ec_pf );
}
value = ec_pf_report;
}
if( record_name == "supported_versions" )
value = hexstr( record["supported_versions"] );
debug_report_array[record_name] = value;
}
debug_report = "Determined '" + version_string[vers] + "' support based on the following valid SSL/TLS Server Hello response / record:" + '\n' + text_format_table( array:debug_report_array, make_list( "Record name", "Record value" ) );
debug_print( debug_report );
set_kb_item( name:"tls_version_get/" + port + "/sni/" + sni_ctx + "/version", value:version_string[vers] );
set_kb_item( name:"tls_version_get/" + port + "/sni/" + sni_ctx + "/hex_version", value:hexstr( record["version"] ) );
set_kb_item( name:"tls_version_get/" + port + "/sni/" + sni_ctx + "/raw_version", value:record["version"] );
set_kb_item( name:"tls_version_get/" + port + "/sni/" + sni_ctx + "/extended_ec_used", value:extended_ec_used );
set_kb_item( name:"tls_version_get/" + port + "/sni/" + sni_ctx + "/" + version_kb_string_mapping[vers] + "/extended_ec_used", value:extended_ec_used );
set_kb_item( name:"tls_version_get/" + port + "/version", value:version_string[vers] );
set_kb_item( name:"tls_version_get/" + port + "/hex_version", value:hexstr( record["version"] ) );
set_kb_item( name:"tls_version_get/" + port + "/raw_version", value:record["version"] );
set_kb_item( name:"tls_version_get/" + port + "/extended_ec_used", value:extended_ec_used );
set_kb_item( name:"tls_version_get/" + port + "/" + version_kb_string_mapping[vers] + "/extended_ec_used", value:extended_ec_used );
sup_tls += version_string[vers] + ";";
sup_tls_list = make_list( sup_tls_list, version_string[vers] );
# nb:
# - We can register a more generic CPE for the protocol itself which can be used for e.g.:
# - CVE scans / the CVE scanner
# - storing the reference from this one to some VTs like e.g. gb_tlsv10_v11_detect.nasl using
# the info collected here to show a cross-reference within the reports
# - We're not using a 'service:""' call below for now as the service is not known yet
# - If changing the syntax of e.g. the "location" below make sure to update VTs like e.g. the
# gb_tlsv10_v11_detect.nasl accordingly
if( vers == TLS_10 )
register_product( cpe:"cpe:/a:ietf:transport_layer_security:1.0", location:port + "/tcp", port:port );
if( vers == TLS_11 )
register_product( cpe:"cpe:/a:ietf:transport_layer_security:1.1", location:port + "/tcp", port:port );
if( vers == TLS_12 )
register_product( cpe:"cpe:/a:ietf:transport_layer_security:1.2", location:port + "/tcp", port:port );
if( vers == TLS_13 )
register_product( cpe:"cpe:/a:ietf:transport_layer_security:1.3", location:port + "/tcp", port:port );
if( vers == SSL_v2 )
register_product( cpe:"cpe:/a:ietf:secure_sockets_layer:2.0", location:port + "/tcp", port:port );
if( vers == SSL_v3 )
register_product( cpe:"cpe:/a:ietf:secure_sockets_layer:3.0", location:port + "/tcp", port:port );
}
}
if( strlen( sup_tls ) ) {
sup_tls = ereg_replace( string:sup_tls, pattern:"(;)$", replace:"" );
set_kb_item( name:"tls/supported/" + port + "/sni/" + sni_ctx, value:sup_tls );
set_kb_item( name:"tls/supported/" + port, value:sup_tls );
set_kb_item( name:"ssl_tls/port", value:port );
# nb: As SSLv3 and SSLv2 are tested after TLS1.0+ (this is expected) we're sorting the list first
# to have SSLv2, SSLv3 and then TLS1.0+ reported afterwards.
sup_tls_list = sort( sup_tls_list );
foreach sup_tls_item( sup_tls_list )
report += '\n' + sup_tls_item;
if( strlen( report ) > 0 ) {
report = 'The remote SSL/TLS service supports the following SSL/TLS protocol version(s):\n' + report;
log_message( port:port, data:report );
}
}
exit( 0 );
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
22 May 2026 00:00Current
5.8Medium risk
Vulners AI Score5.8