Class: Msf::Trace::CertificateTracePresenter
- Inherits:
-
Object
- Object
- Msf::Trace::CertificateTracePresenter
- Defined in:
- lib/msf/core/trace/certificate_trace_presenter.rb
Overview
Presenter for X.509 certificates.
Follows the same responsibility split as Rex::Proto::Kerberos::CredentialCache::Krb5CCachePresenter: this class constructs formatted strings only. The caller (module instance) is responsible for invoking its own print methods so output is correctly associated with the running module.
Usage:
presenter = Msf::Trace::CertificateTracePresenter.new(cert)
mod.print_line(presenter.)
mod.print_line(presenter.to_s_full)
Constant Summary collapse
- SEPARATOR =
('[CertificateTrace] ' + ('-' * 38)).freeze
- NAMED_OIDS =
OIDs surfaced as named fields in to_s_full; excluded from the raw extension dump.
%w[subjectAltName extendedKeyUsage keyUsage].freeze
- CERT_TEMPLATE_NAME_OID =
Microsoft AD CS enrollment extensions whose content carries the template name / template version. OpenSSL has no friendly decoder for these, so we decode them ourselves below.
'1.3.6.1.4.1.311.20.2'- CERT_TEMPLATE_INFO_OID =
'1.3.6.1.4.1.311.21.7'- APPLICATION_POLICIES_OID =
Microsoft Application Policies extension. Its content is a CertificatePolicies SEQUENCE OF PolicyInformation, which the framework already decodes for the icpr_cert workflow; we resolve each policy OID to its friendly label rather than dumping the raw bytes (the policy OIDs - e.g. Client Authentication - are central to ESC attack triage).
'1.3.6.1.4.1.311.21.10'- EXTENSION_OID_NAMES =
Friendly labels for extension OIDs that OpenSSL leaves as raw numeric strings (predominantly Microsoft enrollment OIDs on AD CS certificates).
{ CERT_TEMPLATE_NAME_OID => 'Certificate Template Name', CERT_TEMPLATE_INFO_OID => 'Certificate Template Information', APPLICATION_POLICIES_OID => 'Application Policies', '1.3.6.1.4.1.311.25.2' => 'AD DS Security Extension (SID)' }.freeze
- OPENSSL_READABLE_EXTENSIONS =
Standard PKIX extensions OpenSSL renders as clean, human-readable text. For any other extension - notably the Microsoft AD CS enrollment OIDs - OpenSSL emits a lossy byte dump (raw bytes on OpenSSL, non-printables collapsed to ‘.’ on LibreSSL), so we hex-encode the raw extnValue instead of printing mojibake. Matched against the short name OpenSSL reports for recognised OIDs.
%w[ basicConstraints authorityKeyIdentifier subjectKeyIdentifier crlDistributionPoints authorityInfoAccess certificatePolicies issuerAltName nameConstraints nsComment nsCertType ].freeze
- IDENTITY_SOURCES =
Priority order for resolving a single auth identity from the cert. UPN is the primary AD identity; email and CN are fallbacks.
[ [:upn, 'UPN'], [:email, 'Email SAN'], [:cn, 'Subject CN'] ].freeze
Class Method Summary collapse
-
.coerce(cert) ⇒ OpenSSL::X509::Certificate?
Attempt to coerce input into an OpenSSL::X509::Certificate.
Instance Method Summary collapse
-
#initialize(cert) ⇒ CertificateTracePresenter
constructor
A new instance of CertificateTracePresenter.
-
#to_s_full ⇒ String?
Returns a formatted full string: metadata + serial, version, public key algorithm, SAN / EKU / Key Usage as named fields, then remaining extensions.
-
#to_s_metadata ⇒ String?
Returns a formatted metadata string: subject, issuer, validity, SHA-256 fingerprint.
Constructor Details
#initialize(cert) ⇒ CertificateTracePresenter
Returns a new instance of CertificateTracePresenter.
92 93 94 |
# File 'lib/msf/core/trace/certificate_trace_presenter.rb', line 92 def initialize(cert) @cert = self.class.coerce(cert) end |
Class Method Details
.coerce(cert) ⇒ OpenSSL::X509::Certificate?
Attempt to coerce input into an OpenSSL::X509::Certificate. Accepts a live certificate object, an OpenSSL::PKCS12 bundle (extracts the leaf certificate), or raw DER/PEM bytes.
81 82 83 84 85 86 87 88 89 |
# File 'lib/msf/core/trace/certificate_trace_presenter.rb', line 81 def self.coerce(cert) return cert if cert.is_a?(OpenSSL::X509::Certificate) return cert.certificate if cert.is_a?(OpenSSL::PKCS12) return OpenSSL::X509::Certificate.new(cert) if cert.is_a?(String) nil rescue OpenSSL::X509::CertificateError, OpenSSL::PKCS12::PKCS12Error nil end |
Instance Method Details
#to_s_full ⇒ String?
Returns a formatted full string: metadata + serial, version, public key algorithm, SAN / EKU / Key Usage as named fields, then remaining extensions.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/msf/core/trace/certificate_trace_presenter.rb', line 118 def to_s_full base = return nil unless base lines = [base] lines << " Serial : #{@cert.serial}" # OpenSSL exposes the zero-based encoded X.509 version (v3 == 2). lines << " Version : v#{@cert.version + 1}" lines << " Public Key : #{format_public_key(@cert.public_key)}" identities = parse_san_identities identity_key, identity_label = IDENTITY_SOURCES.find { |key, _| identities[key] } identity_value = identity_key ? identities[identity_key] : subject_cn identity_source = identity_key ? identity_label : 'Subject CN' lines << " Identity : #{identity_value} (#{identity_source})" if identity_value san = extension_value('subjectAltName') lines << " SAN : #{san}" if san eku = extension_value('extendedKeyUsage') lines << " EKU : #{eku}" if eku ku = extension_value('keyUsage') lines << " Key Usage : #{ku}" if ku other = @cert.extensions.reject { |e| NAMED_OIDS.include?(e.oid) } if other.any? lines << ' Extensions :' other.each do |e| label = EXTENSION_OID_NAMES[normalize_oid(e.oid)] || e.oid lines << " #{label} : #{format_extension(e)}" end end lines.join("\n") end |
#to_s_metadata ⇒ String?
Returns a formatted metadata string: subject, issuer, validity, SHA-256 fingerprint.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/msf/core/trace/certificate_trace_presenter.rb', line 99 def return nil unless @cert fingerprint = OpenSSL::Digest::SHA256.hexdigest(@cert.to_der) [ SEPARATOR, " Subject : #{@cert.subject}", " Issuer : #{@cert.issuer}", " Not Before : #{@cert.not_before}", " Not After : #{@cert.not_after}", " SHA-256 : #{fingerprint}" ].join("\n") end |