Add HTTP TLS peer cert tracing via CertificateTrace (Phase 1)#21599
Conversation
Introduces Msf::Exploit::Remote::SslPeerCertTrace with an SSLPeerCertTrace enum datastore option (off/metadata/full). HttpClient includes the mixin and calls ssl_peer_cert_trace after each send_recv; the helper reads conn.peer_cert, deduplicates per host:port, and formats the server certificate through the existing CertificateTracePresenter.
Removes the standalone SSLPeerCertTrace mixin and SSLPeerCertTrace datastore option. Peer SSL certificate tracing now lives in CertificateTrace as #certificate_peer_cert_trace, reusing the existing CertificateTrace enum and CertificateTraceColors option so all cert output (issued, CSR, peer) is controlled by a single operator-facing option. Deduplication now uses the database when active (ssl.peer_cert_seen note per host:port, update: :unique) so the same server certificate is not reprinted across module runs. Falls back to in-memory dedup when no DB is connected. HttpClient is updated to include CertificateTrace instead of SslPeerCertTrace and calls certificate_peer_cert_trace after send_recv. The ssl_peer_cert_trace mixin file and its spec are removed; the 20 new peer cert examples are added to certificate_trace_spec.rb (46 examples, 0 failures).
certificate_trace already tested the invalid-cert no-raise case; add the same coverage for certificate_peer_cert_trace.
Resolve conflicts from the merged CSR trace work (rapid7#21580): - certificate_trace.rb: keep both certificate_peer_cert_trace and certificate_csr_trace as sibling methods - certificate_trace_spec.rb: keep both describe blocks as siblings Presenter takes master's trace_separator version; peer cert method uses it transparently via the presenter public API.
|
Rebased/merged latest master and resolved the conflicts. The conflicts were both-add collisions with the CSR trace work (#21580) that landed on master after this branch was cut. Both PRs inserted a new method into Resolution:
Spec passes locally: 52 examples, 0 failures. Branch is now mergeable. |
Addresses review feedback that a server's TLS peer certificate printed under the same '[CertificateTrace] x.509' header as an issued/client certificate, making the two indistinguishable. Add an optional label: keyword to CertificateTracePresenter#to_s_metadata and #to_s_full (default 'x.509', backward compatible). certificate_peer_cert_trace now renders peer certs under a 'Peer Cert' header. Issued-cert and CSR output are unchanged.
|
Addressed the peer-cert labeling feedback: a server's TLS peer certificate was printing under the same Added an optional Verified live with Only the header line differs from an issued or client cert ( |
There was a problem hiding this comment.
Pull request overview
This PR extends Metasploit’s existing Msf::Exploit::Remote::CertificateTrace facility to optionally trace an HTTPS server’s TLS peer certificate from HttpClient connections, using the existing CertificateTrace verbosity enum (off/metadata/full) and presenter formatting.
Changes:
- Add
#certificate_peer_cert_trace(cert, host, port)to theCertificateTracemixin with per-host:port dedup (DB-backed when available, in-memory fallback otherwise). - Wire
HttpClient#send_request_rawto invoke peer-cert tracing when an underlying connection exposespeer_cert. - Update
CertificateTracePresenter#to_s_metadata/#to_s_fullto accept alabel:so peer cert output can be distinguished from issued cert output (Peer Certvsx.509), and add unit specs covering the new behavior.
Impact Analysis:
- Blast radius: high —
Msf::Exploit::Remote::HttpClientis widely mixed into HTTP modules, so tracing behavior and option registration can affect many modules. - Data and contract effects: adds a new public mixin method and causes optional console output; introduces DB notes of type
ssl.peer_cert_seenfor dedup when the DB is active. - Rollback and test focus: focus validation on common HTTPS modules using
send_request_raw, ensuring (1) no output whenCertificateTrace=off, (2) single trace per host:port, (3) no DB query/write regressions on repeated requests, and (4) consistent behavior on invalidCertificateTracevalues.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| spec/lib/msf/core/exploit/remote/certificate_trace_spec.rb | Adds specs for peer-cert tracing modes, labeling, dedup, and color wrapping. |
| lib/msf/core/trace/certificate_trace_presenter.rb | Adds label: to metadata/full rendering to distinguish peer cert headers. |
| lib/msf/core/exploit/remote/http_client.rb | Includes CertificateTrace and invokes peer-cert tracing after send_recv when available. |
| lib/msf/core/exploit/remote/certificate_trace.rb | Implements certificate_peer_cert_trace with dedup and presenter dispatch. |
| @_peer_cert_seen ||= {} | ||
| key = "#{host}:#{port}" | ||
| return if @_peer_cert_seen[key] | ||
|
|
||
| if respond_to?(:framework) && framework&.db&.active | ||
| already_noted = framework.db.notes(ntype: 'ssl.peer_cert_seen').any? do |n| | ||
| n.host&.address == host && n.service&.port == port | ||
| end | ||
| if already_noted | ||
| @_peer_cert_seen[key] = true | ||
| return | ||
| end | ||
| framework.db.report_note( | ||
| host: host, port: port, proto: 'tcp', | ||
| ntype: 'ssl.peer_cert_seen', | ||
| data: {}, | ||
| update: :unique | ||
| ) | ||
| end |
Mirror the metadata/full/else dispatch used by certificate_trace and certificate_csr_trace so an unrecognized CertificateTrace value warns via vprint_warning and produces no output instead of silently falling through the metadata branch. Add matching unknown-mode spec coverage.
|
Hey Jack, pushed a small follow-up from the Copilot review. Addressed (Copilot #2): Left as-is (Copilot #1, the notes lookup / N+1): I looked at this one and decided not to touch it in this PR, wanted to flag why. The lookup does load the |
jheysel-r7
left a comment
There was a problem hiding this comment.
Looks great, thanks @Pushpenderrathore
Testing
msf auxiliary(scanner/http/http_version) > run
[CertificateTrace] Peer Cert ----------------------------
Subject : /CN=example.local
Issuer : /CN=example.local
Not Before : 2026-07-15 14:56:00 UTC
Not After : 2027-07-15 14:56:00 UTC
SHA-256 : 3099b8eb5c278d4bda0d6a4567d3b6e2777160deca9efd2fa538bbce66522e2c
Serial : 146406233325429125722816363610311164214444770560
Version : v3
Public Key : RSA-2048
Identity : example.local (Subject CN)
Extensions :
subjectKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
authorityKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
basicConstraints : CA:TRUE
[+] 172.16.199.136:8443
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/http_version) > run
[+] 172.16.199.136:8443
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/http_version) > notes
Notes
=====
Time Host Service Port Protocol Type Data
---- ---- ------- ---- -------- ---- ----
2026-07-15 17:23:21 UTC 172.16.199.136 https 8443 tcp ssl.peer_cert_seen {}
msf auxiliary(scanner/http/http_version) > db_disconnect
Successfully disconnected from the data service: local_db_service.
msf auxiliary(scanner/http/http_version) > run
[CertificateTrace] Peer Cert ----------------------------
Subject : /CN=example.local
Issuer : /CN=example.local
Not Before : 2026-07-15 14:56:00 UTC
Not After : 2027-07-15 14:56:00 UTC
SHA-256 : 3099b8eb5c278d4bda0d6a4567d3b6e2777160deca9efd2fa538bbce66522e2c
Serial : 146406233325429125722816363610311164214444770560
Version : v3
Public Key : RSA-2048
Identity : example.local (Subject CN)
Extensions :
subjectKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
authorityKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
basicConstraints : CA:TRUE
[+] 172.16.199.136:8443
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/http_version) > run
[CertificateTrace] Peer Cert ----------------------------
Subject : /CN=example.local
Issuer : /CN=example.local
Not Before : 2026-07-15 14:56:00 UTC
Not After : 2027-07-15 14:56:00 UTC
SHA-256 : 3099b8eb5c278d4bda0d6a4567d3b6e2777160deca9efd2fa538bbce66522e2c
Serial : 146406233325429125722816363610311164214444770560
Version : v3
Public Key : RSA-2048
Identity : example.local (Subject CN)
Extensions :
subjectKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
authorityKeyIdentifier : 84:45:D1:67:08:B6:57:5A:87:5C:9C:A9:5C:45:7E:F2:5B:E8:F4:BF
basicConstraints : CA:TRUE
[+] 172.16.199.136:8443
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
Release NotesThis extends CertificateTrace functionality to also surface the server's TLS peer certificate when an HTTP module connects over HTTPS. This makes use of the same CertificateTrace enum (off/metadata/full) operators are already familiar with. |
Summary
Extends
CertificateTrace(the existing mixin for issued cert and CSR tracing) to also surface the server's TLS peer certificate when an HTTP module connects over HTTPS. No new operator-facing option is introduced -- operators use the sameCertificateTraceenum (off/metadata/full) they already know.Key design points:
#certificate_peer_cert_trace(cert, host, port)added toMsf::Exploit::Remote::CertificateTraceHttpClientcalls it aftersend_recvwhen a raw peer cert is availablessl.peer_cert_seennote,update: :unique) with in-memory@_peer_cert_seenfallback when no DBpeer_cert) are silently ignoredSslPeerCertTracemixin and itsSSLPeerCertTracedatastore optionTest plan
Rebased onto latest master; the conflicts with the merged CSR trace work (#21580) were resolved (both
certificate_peer_cert_traceandcertificate_csr_tracekept as sibling methods). All checks below re-run after the merge.Peer cert labeling: peer certs now print under a
Peer Certheader (alabel:keyword was added toCertificateTracePresenter#to_s_metadata/#to_s_full, defaultx.509), so a server's TLS certificate is no longer visually indistinguishable from an issued/client certificate. Issued-cert (x.509) and CSR output are unchanged.Unit specs (this branch, post-merge):
bundle exec rspec spec/lib/msf/core/exploit/remote/certificate_trace_spec.rb- 54 examples, 0 failuresLive test - HTTP peer cert trace against a real HTTPS server (
auxiliary/scanner/http/http_versionvsexample.com:443,CertificateTrace=full):ssl.peer_cert_seennote when a database is connected, in-memory fallback otherwise).Phasing
This PR covers HTTP only (Phase 1). Follow-up PR covers LDAP over TLS and RDP (Phase 2), with Postgres and SMB over TLS pending TLS support in those mixins.
Depends on: #21198 (already merged)