Skip to content

Conversation

@mwalas-r7
Copy link
Contributor

@mwalas-r7 mwalas-r7 commented Sep 3, 2025

This PR fixes missing SNI support for the ssl_version scanner.

Verification

  • Start msfconsole
  • use auxiliary/scanner/ssl/ssl_version
  • set rhosts localhost - target server with weak certificates and ciphers enabled, default port is 443. Use hostname
  • set SSLServerNameIndication localhost
  • run
  • Verify Certificate Information
  • Verify that certificate is saved to loot
  • Verify that information about public key size is visible
  • Verify that each enabled protocol version has a list of all accepted ciphers
  • Verify that detailed scan results are saved to loot

  • set rhosts 127.0.0.1 - target server with weak certificates and ciphers enabled, default port is 443. Use server's IP address
  • set SSLServerNameIndication 127.0.0.1
  • run
  • Verify Certificate Information
  • Verify that certificate is saved to loot
  • Verify that information about public key size is visible
  • Verify that each enabled protocol version has a list of all accepted ciphers
  • Verify that detailed scan results are saved to loot

  • set rhosts 127.0.0.1 - target server with weak certificates and ciphers enabled, default port is 443. Use server's IP address
  • set SSLServerNameIndication localhost
  • run
  • Verify Certificate Information
  • Verify that certificate is saved to loot
  • Verify that information about public key size is visible
  • Verify that each enabled protocol version has a list of all accepted ciphers
  • Verify that detailed scan results are saved to loot

ctx = { 'Msf' => framework, 'MsfExploit' => self }
# Initialize rex-sslscan scanner
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx)
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx, tls_server_name_indication: datastore['RHOSTNAME'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to add this to the datastore options so it appears to the user when they run show options command as well adding validation etc, similar to this module:

OptString.new('SSLServerNameIndication', [ false, 'SSL/TLS Server Name Indication (SNI)', nil]),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adfoster-r7 i have added SSLServerNameIndication as an option OptString.new('SSLServerNameIndication', [ true, 'SSL/TLS Server Name Indication (SNI)', nil])

ctx = { 'Msf' => framework, 'MsfExploit' => self }
# Initialize rex-sslscan scanner
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx)
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx, tls_server_name_indication: datastore['RHOSTNAME'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, my brain autocompleted datastore['RHOSTNAME'] as datastore['SSLServerNameIndication'].

For context, we've got an existing convention of SSLServerNameIndication that you'll want to follow - that's what my original comment was aimed at following 👍

https://github.com/search?q=repo%3Arapid7%2Fmetasploit-framework%20SSLServerNameIndication&type=code

Copy link
Contributor Author

@mwalas-r7 mwalas-r7 Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adfoster-r7 i have added SSLServerNameIndication as an option OptString.new('SSLServerNameIndication', [ true, 'SSL/TLS Server Name Indication (SNI)', nil])

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good. There does appear to be an unrelated exception that's raised though when RHOSTS is set to a hostname and a database is connected that we should fix eventually.

Exception Info
[*] lab.zerosteiner.com:443 - Scanned 1 of 1 hosts (100% complete)
[-] Auxiliary failed: ActiveRecord::StatementInvalid PG::InvalidTextRepresentation: ERROR:  invalid input syntax for type inet: "lab.zerosteiner.com"
CONTEXT:  unnamed portal parameter $2 = '...'

[-] Call stack:
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:894:in `exec_params'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:894:in `block (2 levels) in exec_no_cache'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1004:in `block in with_raw_connection'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activesupport-7.2.2.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:976:in `with_raw_connection'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:893:in `block in exec_no_cache'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activesupport-7.2.2.1/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract_adapter.rb:1119:in `log'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:892:in `exec_no_cache'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:872:in `execute_and_clear'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:66:in `internal_exec_query'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:647:in `select'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/database_statements.rb:73:in `select_all'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/query_cache.rb:251:in `select_all'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/querying.rb:70:in `_query_by_sql'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1431:in `block (2 levels) in exec_main_query'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:415:in `with_connection'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/connection_handling.rb:296:in `with_connection'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1430:in `block in exec_main_query'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1452:in `skip_query_cache_if_necessary'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1414:in `exec_main_query'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1392:in `block in exec_queries'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1452:in `skip_query_cache_if_necessary'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1386:in `exec_queries'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/association_relation.rb:44:in `exec_queries'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:1167:in `load'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:336:in `records'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation.rb:331:in `to_ary'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation/finder_methods.rb:615:in `find_nth_with_limit'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation/finder_methods.rb:600:in `find_nth'
[-]   /home/smcintyre/.rvm/gems/ruby-3.3.8@metasploit-framework/gems/activerecord-7.2.2.1/lib/active_record/relation/finder_methods.rb:177:in `first'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/msf/core/db_manager/exploit_attempt.rb:65:in `report_exploit_failure'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/metasploit/framework/data_service/proxy/exploit_data_proxy.rb:17:in `block in report_exploit_failure'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/metasploit/framework/data_service/proxy/core.rb:164:in `data_service_operation'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/metasploit/framework/data_service/proxy/exploit_data_proxy.rb:15:in `report_exploit_failure'
[-]   /home/smcintyre/Repositories/metasploit-framework.pr/lib/msf/core/module/failure.rb:69:in `report_failure'
msf auxiliary(scanner/ssl/ssl_version) >

If you want to look at the feedback, we can get this landed. Thanks for this fix!


register_options(
[
OptString.new('SSLServerNameIndication', [ true, 'SSL/TLS Server Name Indication (SNI)', nil]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this actually needs to be required. I was able to test another server that didn't have SNI enabled and I can scan it when this option is blank or incorrect.

msf auxiliary(scanner/ssl/ssl_version) > run SSLServerNameIndication='' RHOSTS=192.168.249.7
[*] 192.168.249.7:443     - Starting enhanced SSL/TLS scan of 192.168.249.7:443
[*] 192.168.249.7:443     - Certificate Information:
[*] 192.168.249.7:443     - 	Subject: /CN=zerosteiner.com
[*] 192.168.249.7:443     - 	Issuer: /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http:\/\/certs.godaddy.com\/repository\//CN=Go Daddy Secure Certificate Authority - G2
[*] 192.168.249.7:443     - 	Signature Alg: sha256WithRSAEncryption
[*] 192.168.249.7:443     - 	Public Key Size: 4096 bits
[*] 192.168.249.7:443     - 	Not Valid Before: 2025-07-13 18:52:26 UTC
[*] 192.168.249.7:443     - 	Not Valid After: 2026-08-14 18:52:26 UTC
[*] 192.168.249.7:443     - 	CA Issuer: http://certificates.godaddy.com/repository/gdig2.crt
[*] 192.168.249.7:443     - 	Has common name zerosteiner.com
[+] 192.168.249.7:443     - Certificate saved to loot: /home/smcintyre/.msf4/loot/20250903162942_default_192.168.249.7_ssl.certificate._316753.pem
[+] 192.168.249.7:443     - 192.168.249.7:443 - TLSv1_2 supported with 14 cipher(s)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-CCM (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES256-GCM-SHA384 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES256-SHA (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-GCM-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-GCM-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-GCM-SHA384 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-SHA (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-CCM (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-CHACHA20-POLY1305 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-SHA256 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-SHA (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-SHA (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-SHA256 (128 bits)
[+] 192.168.249.7:443     - Detailed scan results saved to loot: /home/smcintyre/.msf4/loot/20250903162942_default_192.168.249.7_ssl.scan.rex_ssl_916679.json
[*] 192.168.249.7:443     - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/ssl/ssl_version) > run SSLServerNameIndication='wrongwrong' RHOSTS=192.168.249.7
[*] 192.168.249.7:443     - Starting enhanced SSL/TLS scan of 192.168.249.7:443
[*] 192.168.249.7:443     - Certificate Information:
[*] 192.168.249.7:443     - 	Subject: /CN=zerosteiner.com
[*] 192.168.249.7:443     - 	Issuer: /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http:\/\/certs.godaddy.com\/repository\//CN=Go Daddy Secure Certificate Authority - G2
[*] 192.168.249.7:443     - 	Signature Alg: sha256WithRSAEncryption
[*] 192.168.249.7:443     - 	Public Key Size: 4096 bits
[*] 192.168.249.7:443     - 	Not Valid Before: 2025-07-13 18:52:26 UTC
[*] 192.168.249.7:443     - 	Not Valid After: 2026-08-14 18:52:26 UTC
[*] 192.168.249.7:443     - 	CA Issuer: http://certificates.godaddy.com/repository/gdig2.crt
[*] 192.168.249.7:443     - 	Has common name zerosteiner.com
[+] 192.168.249.7:443     - Certificate saved to loot: /home/smcintyre/.msf4/loot/20250903163020_default_192.168.249.7_ssl.certificate._632585.pem
[+] 192.168.249.7:443     - 192.168.249.7:443 - TLSv1_2 supported with 14 cipher(s)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES256-GCM-SHA384 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-GCM-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES256-SHA (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-CCM (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-SHA (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-SHA256 (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-CCM (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-GCM-SHA384 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-CHACHA20-POLY1305 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: ECDHE-RSA-AES128-SHA (128 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-SHA (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES256-SHA256 (256 bits)
[*] 192.168.249.7:443     -   TLSv1_2: AES128-GCM-SHA256 (128 bits)
[+] 192.168.249.7:443     - Detailed scan results saved to loot: /home/smcintyre/.msf4/loot/20250903163020_default_192.168.249.7_ssl.scan.rex_ssl_623174.json
[*] 192.168.249.7:443     - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/ssl/ssl_version) >

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smcintyre-r7 I have adjusted the option and set it to not required

ctx = { 'Msf' => framework, 'MsfExploit' => self }
# Initialize rex-sslscan scanner
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx)
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx, tls_server_name_indication: datastore['SSLServerNameIndication'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once SSLServerNameIndication is made optional, would it be possible to do something like this? This would allow the option to explicitly be set from the datastore, but if the user doesn't set it, the RHOSTNAME from RangeWalker would be used by default in the event that they're targeting a host by it's name making it more convenient to use.

Suggested change
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx, tls_server_name_indication: datastore['SSLServerNameIndication'])
tls_server_name_indication = nil
tls_server_name_indication = datastore['SSLServerNameIndication'] if tls_server_name_indication.nil? and datastore['SSLServerNameIndication'].present?
tls_server_name_indication = datastore['RHOSTNAME'] if tls_server_name_indication.nil? and datastore['RHOSTNAME'].present?
scanner = Rex::SSLScan::Scanner.new(ip, rport, ctx, tls_server_name_indication: tls_server_name_indication)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's roughly the approach taken elsewhere too:

'PeerHostname' => opts['SSLServerNameIndication'] || opts['VHOST'] || opts['RHOSTNAME'],

It's maybe worth noting that Pro doesn't currently use the rhost walker for IP expansion, so RHOSTNAME wouldn't be set automatically by Pro with its current implementation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once SSLServerNameIndication is made optional, would it be possible to do something like this? This would allow the option to explicitly be set from the datastore, but if the user doesn't set it, the RHOSTNAME from RangeWalker would be used by default in the event that they're targeting a host by it's name making it more convenient to use.

@smcintyre-r7 thank you for the proposal, i have added this to the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's roughly the approach taken elsewhere too:

'PeerHostname' => opts['SSLServerNameIndication'] || opts['VHOST'] || opts['RHOSTNAME'],

It's maybe worth noting that Pro doesn't currently use the rhost walker for IP expansion, so RHOSTNAME wouldn't be set automatically by Pro with its current implementation

@adfoster-r7 i have created a task for it

@mwalas-r7 mwalas-r7 force-pushed the fix/sni-support-for-ssl-scanner branch from 8469613 to bf959c1 Compare September 4, 2025 10:22
begin
ctx = { 'Msf' => framework, 'MsfExploit' => self }
tls_server_name_indication = nil
tls_server_name_indication = datastore['SSLServerNameIndication'] if tls_server_name_indication.nil? && datastore['SSLServerNameIndication'].present?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tls_server_name_indication = datastore['SSLServerNameIndication'] if tls_server_name_indication.nil? && datastore['SSLServerNameIndication'].present?
tls_server_name_indication = datastore['SSLServerNameIndication'] if datastore['SSLServerNameIndication'].present?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dwelch-r7 Adjusted the code and removed the unnecessary tls_server_name_indication check

@mwalas-r7 mwalas-r7 force-pushed the fix/sni-support-for-ssl-scanner branch from bf959c1 to 9fb4966 Compare September 4, 2025 14:13
@mwalas-r7 mwalas-r7 requested a review from dwelch-r7 September 4, 2025 14:13
Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New changes look good and the pattern you implement will be pretty convenient for framework users. Thanks for these updates!

@github-project-automation github-project-automation bot moved this from Todo to In Progress in Metasploit Kanban Sep 4, 2025
@smcintyre-r7 smcintyre-r7 merged commit e07d174 into rapid7:master Sep 4, 2025
17 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Metasploit Kanban Sep 4, 2025
@smcintyre-r7
Copy link
Contributor

Release Notes

This fixes SNI functionality in the auxiliary/scanner/ssl/ssl_version module so it can target hosts with multiple names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug module rn-fix release notes fix

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

4 participants