Add LDAP and RDP TLS peer cert tracing via CertificateTrace (Phase 2)#21607
Conversation
|
Thanks for your pull request! As part of our landing process, we manually verify that all modules work as expected. We've added the |
Live lab validation against Windows Server 2022 AD CS DC (192.168.64.3)LDAPS port 636 -- metadata modeLDAPS port 636 -- full mode (shows AD CS template, SAN, EKU, extensions)RDP port 3389RDP produces a self-signed cert (Windows default) -- Subject equals Issuer, 6-month validity. Correctly identified as self-signed with no chain. Dedup (cross-run, DB-backed)Second run of the same module against the same host:port with NotesThe |
CI fix: LoginScanner::LDAP rspec failuresThe Root cause: Fix: Add |
|
Addressed the peer-cert labeling feedback: a server's TLS peer certificate was printing under the same Added an optional Verified live against the AD DC (192.168.64.3), full mode. RDP ( LDAP over LDAPS ( Specs updated to lock in both the new |
There was a problem hiding this comment.
Pull request overview
This PR extends the existing Msf::Exploit::Remote::CertificateTrace mixin to trace and deduplicate TLS peer certificates for additional protocols (LDAP-over-TLS and RDP), and updates the presenter to label peer cert output distinctly from issued certificates.
Changes:
- Add
CertificateTraceintegration points to LDAP (ldap_open) and RDP (swap_sock_plain_to_ssl) to capture the TLS peer cert right after handshake. - Enhance
CertificateTracePresenterto support a configurable separator label (e.g.,Peer Certvsx.509) and add/extend specs for peer-cert behavior. - Add HTTP hook to call
certificate_peer_cert_tracewhen a peer cert is available (ensuring consistent behavior across protocols).
Impact Analysis:
- Blast radius: high —
lib/msf/core/exploit/remote/certificate_trace.rbandlib/msf/core/trace/certificate_trace_presenter.rbare shared mixins/utilities used by multiple protocol mixins/modules. - Data and contract effects: adds/uses DB notes of type
ssl.peer_cert_seenfor dedup; changes presenter method signatures to accept a keyword arg (label:) while keeping defaults for existing callers. - Rollback and test focus: rollback is straightforward (revert mixin hooks); validate (1) no output when tracing is off, (2) dedup works with/without DB, (3) LDAP
ldap_openbehavior with and without blocks, and (4) DB performance on large workspaces.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/lib/msf/core/exploit/remote/rdp_spec.rb | Adds unit coverage to ensure RDP peer cert tracing prints only when enabled and cert is present. |
| spec/lib/msf/core/exploit/remote/ldap_spec.rb | Adds unit coverage for LDAP peer cert tracing for both keep_open paths. |
| spec/lib/msf/core/exploit/remote/certificate_trace_spec.rb | Adds peer-cert tracing specs (labeling, modes, dedup, DB note behavior, colors). |
| lib/msf/core/trace/certificate_trace_presenter.rb | Adds label: keyword arg support so headers can distinguish Peer Cert vs x.509. |
| lib/msf/core/exploit/remote/rdp.rb | Hooks peer cert tracing after ssl.connect during RDP TLS upgrade. |
| lib/msf/core/exploit/remote/ldap.rb | Hooks peer cert tracing in ldap_open for both open paths; adds helper. |
| lib/msf/core/exploit/remote/http_client.rb | Calls certificate_peer_cert_trace when an HTTP TLS peer cert is available. |
| lib/msf/core/exploit/remote/certificate_trace.rb | Implements certificate_peer_cert_trace with dedup, DB note recording, and labeled output. |
|
Hey Jack, pushed an update to this PR. It was conflicting after #21599 landed, so I brought it up to date and addressed the Copilot review in the same pass. Split into two commits so it is easy to follow: 1. Merge upstream/master ( 2. Address Copilot review (
CI is running on the new commit. Specs for cert trace, LDAP, and RDP are green locally (84 examples, 0 failures). Let me know if you want anything reworked. |
Includes CertificateTrace in Exploit::Remote::LDAP so LDAP over TLS connections surface the server certificate through the same CertificateTrace option and CertificateTraceColors that HTTP uses. No new operator-facing option is introduced. ldap_open is updated for both paths: - keep_open: false (Rex::Proto::LDAP::Client.open) -- wraps the caller block and reads peer_cert from the @open_connection socket before yielding to the caller. - keep_open: true (Rex::Proto::LDAP::Client._open) -- reads peer_cert from ldap.socket after the connection is established and returned. A private _ldap_trace_peer_cert helper centralises the socket guard and the certificate_peer_cert_trace call so both paths share the same nil-safe check. Plain LDAP connections (no TLS, no peer_cert) are silently ignored. Dedup and DB recording are handled by certificate_peer_cert_trace in the CertificateTrace mixin. Spec: 14 examples (7 new for peer cert tracing), 0 failures.
Includes CertificateTrace in Exploit::Remote::RDP so the server TLS certificate is surfaced when an operator sets CertificateTrace to metadata or full. The hook is in swap_sock_plain_to_ssl, immediately after ssl.connect succeeds -- the OpenSSL socket has a valid peer_cert at that point and before it is wrapped into the Rex SslTcp layer. No new operator-facing option is introduced; RDP re-uses the same CertificateTrace enum as HTTP and LDAP. A nil peer_cert (e.g. when the server sends no certificate) is a silent no-op. Spec: 3 new examples covering TLS cert printed, off mode silent, and nil peer_cert silent.
LoginScanner::LDAP includes Exploit::Remote::LDAP so it calls _ldap_trace_peer_cert via ldap_open. LoginScanner does not have datastore, so calling rhost (which calls datastore['RHOST']) raises NoMethodError, caught as UNABLE_TO_CONNECT in the scanner. Guard with certificate_trace_enabled? at the top so we return before evaluating rhost/rport when tracing is unavailable.
- ldap.rb: remove the duplicate CertificateTrace include.
- ldap_open: handle a block-less call. Net::LDAP.open yields to a block
and closes the connection, so forwarding a nil block raised
NoMethodError; open and return the client (as keep_open does) instead.
- certificate_peer_cert_trace: key the DB dedup on the note's own
serialized data ("host:port") instead of walking the host/service
associations, which issued a query per note (N+1).
- Specs: cover the block-less ldap_open path and the data-keyed dedup
(including a different-endpoint case).
c8f11f4 to
bb9c01d
Compare
|
@jheysel-r7 rebased Phase 2 onto master as requested. It is a clean linear history now (the shared peer-cert work from #21599 dropped out since it is already on master), and the Copilot review is addressed. All checks have passed (46 successful, 1 skipped) and specs for cert trace, LDAP, and RDP are green locally (84 examples, 0 failures). Ready for review whenever you get a chance. |
jheysel-r7
left a comment
There was a problem hiding this comment.
Looks great @Pushpenderrathore thanks for this update!
Testing
LDAP
msf auxiliary(gather/ldap_query) > run
[*] Running module against 172.16.199.200
[CertificateTrace] Peer Cert ----------------------------
Subject :
Issuer : /DC=issue/DC=kerberos/CN=kerberos-DC2-CA
Not Before : 2026-05-05 17:17:16 UTC
Not After : 2027-05-05 17:17:16 UTC
SHA-256 : 96576841d03b8b872099e841918486db86709a405b4b3226406ed2e010621bc7
Serial : 1828661108666046931287660402855086338078671307
Version : v3
Public Key : RSA-2048
SAN : DNS:dc2.kerberos.issue, DNS:kerberos.issue, DNS:KERBEROS
EKU : TLS Web Client Authentication, TLS Web Server Authentication, Microsoft Smartcard Login, Signing KDC Response
Key Usage : Digital Signature, Key Encipherment
Extensions :
Certificate Template Information : Template 1.3.6.1.4.1.311.21.8.12682474.6065318.6963902.6406785.3291287.83.1.33 (v110.1)
Application Policies : 1.3.6.1.5.5.7.3.2 (Client Authentication), 1.3.6.1.5.5.7.3.1 (Server Authentication), 1.3.6.1.4.1.311.20.2.2 (Smart Card Logon), 1.3.6.1.5.2.3.5
subjectKeyIdentifier : EC:3B:36:5A:7F:11:DC:1A:2A:F2:28:E6:37:86:20:F8:25:DF:15:A9
authorityKeyIdentifier : 39:A1:AB:7A:6E:8A:70:91:7D:EF:83:78:9D:9F:32:19:5A:AE:A0:ED
crlDistributionPoints : Full Name:
URI:ldap:///CN=kerberos-DC2-CA,CN=dc2,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue?certificateRevocationList?base?objectClass=cRLDistributionPoint
authorityInfoAccess : CA Issuers - URI:ldap:///CN=kerberos-DC2-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue?cACertificate?base?objectClass=certificationAuthority
msf auxiliary(gather/ldap_query) > db_status
[*] Connected to msf. Connection type: postgresql.
msf auxiliary(gather/ldap_query) > run
[*] Running module against 172.16.199.200
[-] Auxiliary aborted due to failure: no-access: Invalid credentials provided!
[*] Auxiliary module execution completed
msf auxiliary(gather/ldap_query) > db_disconnect
Successfully disconnected from the data service: local_db_service.
msf auxiliary(gather/ldap_query) > set certificatetrace metadata
certificatetrace => metadata
msf auxiliary(gather/ldap_query) > run
[*] Running module against 172.16.199.200
[CertificateTrace] Peer Cert ----------------------------
Subject :
Issuer : /DC=issue/DC=kerberos/CN=kerberos-DC2-CA
Not Before : 2026-05-05 17:17:16 UTC
Not After : 2027-05-05 17:17:16 UTC
SHA-256 : 96576841d03b8b872099e841918486db86709a405b4b3226406ed2e010621bc7
RDP
msf auxiliary(scanner/rdp/rdp_scanner) > run
[CertificateTrace] Peer Cert ----------------------------
Subject : /CN=dc2.kerberos.issue
Issuer : /CN=dc2.kerberos.issue
Not Before : 2026-07-15 17:51:29 UTC
Not After : 2027-01-14 17:51:29 UTC
SHA-256 : 84ca072ea07feb5606c0d5fad7fb89f486894fffc69ee7d4a37099a250c66d9e
Serial : 74753792603575789070804919156855713120
Version : v3
Public Key : RSA-2048
Identity : dc2.kerberos.issue (Subject CN)
EKU : TLS Web Server Authentication
Key Usage : Key Encipherment, Data Encipherment
[*] 172.16.199.200:3389 - Detected RDP on 172.16.199.200:3389 (name:DC2) (domain:KERBEROS) (domain_fqdn:kerberos.issue) (server_fqdn:dc2.kerberos.issue) (os_version:10.0.17763) (Requires NLA: Yes)
[*] 172.16.199.200:3389 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/rdp/rdp_scanner) > set certificatetrace metadata
certificatetrace => metadata
msf auxiliary(scanner/rdp/rdp_scanner) > run
[CertificateTrace] Peer Cert ----------------------------
Subject : /CN=dc2.kerberos.issue
Issuer : /CN=dc2.kerberos.issue
Not Before : 2026-07-15 17:51:29 UTC
Not After : 2027-01-14 17:51:29 UTC
SHA-256 : 84ca072ea07feb5606c0d5fad7fb89f486894fffc69ee7d4a37099a250c66d9e
[*] 172.16.199.200:3389 - Detected RDP on 172.16.199.200:3389 (name:DC2) (domain:KERBEROS) (domain_fqdn:kerberos.issue) (server_fqdn:dc2.kerberos.issue) (os_version:10.0.17763) (Requires NLA: Yes)
[*] 172.16.199.200:3389 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/rdp/rdp_scanner) >
|
Additional test pipeline started ⌛ |
|
Slice summary: No test slices found. Note: build results only accessible to maintainers. |
|
Additional test pipeline started ⌛ |
|
Slice summary: No test slices found. Note: build results only accessible to maintainers. |
|
Additional test pipeline started ⌛ |
|
Slice summary: No test slices found. Note: build results only accessible to maintainers. |
|
Additional test pipeline started ⌛ |
|
Slice summary:
Note: build results only accessible to maintainers. |
Release notesThis extends CertificateTrace peer cert tracing to support LDAP over TLS and RDP. No new operator-facing option -- operators use the existing CertificateTrace enum (off/metadata/full). |
Summary
Extends
CertificateTracepeer cert tracing (introduced in #21599) to LDAP over TLS and RDP. No new operator-facing option -- operators use the existingCertificateTraceenum (off/metadata/full).LDAP (
Exploit::Remote::LDAP):CertificateTraceldap_openwraps both connection paths (keep_open: falseviaRex::Proto::LDAP::Client.open;keep_open: trueviaClient._open) to read the server cert from the socket after the TLS handshake_ldap_trace_peer_certhelper guards against non-TLS (plain LDAP) connections silentlyRDP (
Exploit::Remote::RDP):CertificateTraceswap_sock_plain_to_sslcallscertificate_peer_cert_traceimmediately afterssl.connectwhile the rawOpenSSL::SSL::SSLSocketis still in scopeDedup, color, and DB recording are delegated to
#certificate_peer_cert_traceinCertificateTrace(the same logic as HTTP).Test plan
Rebased onto latest master; the conflicts with the merged CSR trace work (#21580) were resolved (peer-cert and CSR methods/specs kept as siblings). 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 failuresbundle exec rspec spec/lib/msf/core/exploit/remote/ldap_spec.rb- 31 examples, 0 failuresbundle exec rspec spec/lib/msf/core/exploit/remote/rdp_spec.rb- 10 examples, 0 failuresLive test - RDP peer cert trace against a real Windows domain controller (
auxiliary/scanner/rdp/rdp_scannervs192.168.64.3:3389,CertificateTrace=full):Live test - LDAP peer cert trace over LDAPS against the same DC (
192.168.64.3:636,CertificateTrace=full), showing the AD CS issued cert with extensions:Notes
HttpClient)