Add Postgres TLS support and peer cert tracing via CertificateTrace (Phase 3)#21608
Open
Pushpenderrathore wants to merge 12 commits into
Open
Add Postgres TLS support and peer cert tracing via CertificateTrace (Phase 3)#21608Pushpenderrathore wants to merge 12 commits into
Pushpenderrathore wants to merge 12 commits into
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).
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.
Includes CertificateTrace in Exploit::Remote::Postgres so the server TLS certificate is surfaced when connecting via SSL (datastore SSL=true). The SSL option was already registered by the included Exploit::Remote::Tcp mixin but was never threaded through to Connection.new. This commit passes it as the sixth positional argument, activating the starttls path in postgres-pr. After a successful login the leaf certificate is read from postgres_conn.conn.peer_cert and forwarded to certificate_peer_cert_trace. A nil peer_cert (plain TCP connection with SSL=false) is a silent no-op. Callers can override with opts[:ssl] to force TLS regardless of the datastore, matching the existing pattern for other opts keys in postgres_login. Spec: 7 new examples covering TLS cert printed, off mode silent, ssl arg threading, plain connection no-op, opts[:ssl] override, and failed login no-op.
|
Thanks for your pull request! As part of our landing process, we manually verify that all modules work as expected. We've added the |
When CertificateTrace=full and the server sends a chain during the TLS handshake, the intermediate and root CA certs are now printed after the leaf cert. Each chain cert uses a "Chain N/M" position header in place of the standard [CertificateTrace] separator so operators can visually distinguish chain depth. certificate_peer_cert_trace gains an optional chain: keyword argument (array of OpenSSL::X509::Certificate). chain[0] is the peer cert already printed; chain[1..] are the chain certs. In metadata mode the chain parameter is silently ignored so verbosity stays minimal. Protocol hooks updated to pass peer_cert_chain: - HTTP (HttpClient): c.conn.peer_cert_chain - LDAP (_ldap_trace_peer_cert): socket.peer_cert_chain via respond_to? guard - RDP (swap_sock_plain_to_ssl): ssl.peer_cert_chain (OpenSSL::SSL::SSLSocket) - Postgres (postgres_login): pg_socket.peer_cert_chain via respond_to? guard Spec: 5 new chain examples in certificate_trace_spec; LDAP and RDP specs updated to stub peer_cert_chain on their socket doubles. 75 examples, 0 failures across all Phase 1-4 specs.
Contributor
Author
Lab test: Postgres TLS cert tracingTested against a local PostgreSQL 14 instance (SSL enabled, self-signed cert) using metadata mode: full mode (extended cert fields + chain): Notes:
|
Plain (non-SSL) connections return a raw Socket which does not have peer_cert, causing a NoMethodError. Match the pattern used in the LDAP and HTTP hooks.
LoginScanner::LDAP includes Exploit::Remote::LDAP so it calls _ldap_trace_peer_cert via ldap_open. LoginScanner does not have datastore, rhost, or rport. Guard with certificate_trace_enabled? early so we return before evaluating rhost/rport arguments when tracing is not available (e.g. in scanner context).
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.
certificate_trace already tested the invalid-cert no-raise case; add the same coverage for certificate_peer_cert_trace.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends
CertificateTracepeer cert tracing to PostgreSQL over TLS, and fixes a long-standing gap where the Postgres mixin accepted anSSLdatastore option (registered via the includedExploit::Remote::Tcp) but never passed it through to the underlying connection.Changes:
Exploit::Remote::Postgresnow includesCertificateTracepostgres_loginextractssslfromopts[:ssl]ordatastore['SSL']and passes it as the 6th argument toConnection.new, activating thestarttlspath inpostgres-prwhen SSL is requestedcertificate_peer_cert_traceis called withpostgres_conn.conn.peer_cert-- guarded byrespond_to?(:peer_cert)so plain TCP connections are a silent no-opopts[:ssl]: true/falseindependent of the datastoreNo new options introduced. The
SSLbool was already registered byExploit::Remote::Tcp; this PR just wires it through.Test plan
bundle exec rspec spec/lib/msf/core/exploit/remote/postgres_spec.rb-- 7 examples, 0 failuresauxiliary/admin/postgres/postgres_sqlagainst a PostgreSQL server with SSL enabled, setSSL trueandCertificateTrace metadata-- confirm the cert prints once (note:auxiliary/scanner/postgres/postgres_loginusesLoginScanner::Postgresinternally and bypassespostgres_login; modules that callpostgres_logindirectly are the correct test surface)SSL false) produce no cert outputNotes