Class: Rex::Proto::Gss::KerberosToken
- Inherits:
-
Object
- Object
- Rex::Proto::Gss::KerberosToken
- Extended by:
- Asn1
- Defined in:
- lib/rex/proto/gss/kerberos_token.rb
Overview
Represents the RFC 1964 framing around an opaque Kerberos protocol message and provides the RFC 4178 SPNEGO operations needed to carry it.
The Kerberos message payload is deliberately not decoded. In particular, AP-REQ bytes can be extracted and rebuilt without changing the encrypted ticket or authenticator.
Defined Under Namespace
Classes: ParseError
Constant Summary collapse
- TOK_ID_KRB_AP_REQ =
RFC 1964 section 1.1 token identifiers.
"\x01\x00".b
- TOK_ID_KRB_AP_REP =
"\x02\x00".b
- TOK_ID_KRB_ERROR =
"\x03\x00".b
- KERBEROS_MECHANISM_OIDS =
[ Rex::Proto::Gss::OID_KERBEROS_5.value, Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5.value ].freeze
Instance Attribute Summary collapse
-
#mechanism_oid ⇒ String
readonly
The GSS mechanism OID.
-
#payload ⇒ String
readonly
The opaque Kerberos message payload.
-
#token_id ⇒ String
readonly
The two-byte RFC 1964 token identifier.
Class Method Summary collapse
-
.binary_string(value) ⇒ String?
Coerces protocol binary fields into binary strings.
-
.build_gss_ap_req(ap_req_der, mechanism_oid: Rex::Proto::Gss::OID_KERBEROS_5) ⇒ String
Builds an RFC 1964 GSS-Kerberos token containing an opaque AP-REQ.
-
.build_spnego_ap_req(ap_req_der) ⇒ String
Builds an RFC 4178 SPNEGO NegTokenInit containing a GSS-Kerberos AP-REQ.
-
.build_spnego_init(mech_token, mech_types: [Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5]) ⇒ String
Builds an RFC 4178 SPNEGO NegTokenInit around an existing mechanism token.
-
.extract_ap_req(token) ⇒ String
Extracts an opaque AP-REQ from a bare GSS-Kerberos token or an RFC 4178 SPNEGO NegTokenInit.
-
.kerberos_ap_req?(token) ⇒ Boolean
Tests whether a GSS or SPNEGO token contains a Kerberos AP-REQ.
-
.parse(token) ⇒ KerberosToken
Parses a bare GSS-Kerberos token and validates its mechanism OID.
-
.parse_spnego_init(token) ⇒ Hash
Parses an RFC 4178 SPNEGO NegTokenInit.
-
.parse_spnego_response(token) ⇒ Hash
Parses an RFC 4178 SPNEGO NegTokenResp.
-
.try_extract_ap_req(token) ⇒ String?
Attempts to extract an AP-REQ without raising for an unsupported or malformed token.
Instance Method Summary collapse
-
#ap_req? ⇒ Boolean
Whether this token contains an AP-REQ.
-
#initialize(mechanism_oid:, token_id:, payload:) ⇒ KerberosToken
constructor
A new instance of KerberosToken.
-
#token_id_hex ⇒ String
Hexadecimal token identifier.
-
#token_type ⇒ String
Readable Kerberos token type.
Methods included from Asn1
unwrap_pseudo_asn1, wrap_pseudo_asn1
Constructor Details
#initialize(mechanism_oid:, token_id:, payload:) ⇒ KerberosToken
Returns a new instance of KerberosToken.
47 48 49 50 51 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 47 def initialize(mechanism_oid:, token_id:, payload:) @mechanism_oid = mechanism_oid @token_id = token_id @payload = payload end |
Instance Attribute Details
#mechanism_oid ⇒ String (readonly)
Returns the GSS mechanism OID.
36 37 38 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 36 def mechanism_oid @mechanism_oid end |
#payload ⇒ String (readonly)
Returns the opaque Kerberos message payload.
42 43 44 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 42 def payload @payload end |
#token_id ⇒ String (readonly)
Returns the two-byte RFC 1964 token identifier.
39 40 41 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 39 def token_id @token_id end |
Class Method Details
.binary_string(value) ⇒ String?
Coerces protocol binary fields into binary strings.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 242 def self.binary_string(value) return nil if value.nil? result = if value.is_a?(String) value elsif value.respond_to?(:to_binary_s) value.to_binary_s elsif value.respond_to?(:bytesize) && value.respond_to?(:to_s) value.to_s end unless result.is_a?(String) raise ParseError, "value of type #{value.class} cannot be converted to a binary string" end result.b end |
.build_gss_ap_req(ap_req_der, mechanism_oid: Rex::Proto::Gss::OID_KERBEROS_5) ⇒ String
Builds an RFC 1964 GSS-Kerberos token containing an opaque AP-REQ.
191 192 193 194 195 196 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 191 def self.build_gss_ap_req(ap_req_der, mechanism_oid: Rex::Proto::Gss::OID_KERBEROS_5) mechanism = asn1_object_id(mechanism_oid) validate_kerberos_mechanism!(mechanism.value) ap_req_der = required_binary_string(ap_req_der, 'AP-REQ') wrap_pseudo_asn1(mechanism, TOK_ID_KRB_AP_REQ + ap_req_der) end |
.build_spnego_ap_req(ap_req_der) ⇒ String
Builds an RFC 4178 SPNEGO NegTokenInit containing a GSS-Kerberos AP-REQ. The AP-REQ payload is never decoded or modified.
233 234 235 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 233 def self.build_spnego_ap_req(ap_req_der) build_spnego_init(build_gss_ap_req(ap_req_der)) end |
.build_spnego_init(mech_token, mech_types: [Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5]) ⇒ String
Builds an RFC 4178 SPNEGO NegTokenInit around an existing mechanism token.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 205 def self.build_spnego_init(mech_token, mech_types: [Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5]) encoded_mech_types = mech_types.map { |mechanism| asn1_object_id(mechanism) } if encoded_mech_types.empty? raise ParseError, 'SPNEGO NegTokenInit requires at least one mechanism type' end mech_token = required_binary_string(mech_token, 'SPNEGO mechanism token') OpenSSL::ASN1::ASN1Data.new([ Rex::Proto::Gss::OID_SPNEGO, OpenSSL::ASN1::ASN1Data.new([ OpenSSL::ASN1::Sequence.new([ OpenSSL::ASN1::ASN1Data.new([ OpenSSL::ASN1::Sequence.new(encoded_mech_types) ], 0, :CONTEXT_SPECIFIC), OpenSSL::ASN1::ASN1Data.new([ OpenSSL::ASN1::OctetString.new(mech_token) ], 2, :CONTEXT_SPECIFIC) ]) ], 0, :CONTEXT_SPECIFIC) ], 0, :APPLICATION).to_der end |
.extract_ap_req(token) ⇒ String
Extracts an opaque AP-REQ from a bare GSS-Kerberos token or an RFC 4178 SPNEGO NegTokenInit.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 132 def self.extract_ap_req(token) blob = binary_string(token) mechanism, encapsulated_token = unwrap_pseudo_asn1(blob) kerberos_token = if mechanism.value == Rex::Proto::Gss::OID_SPNEGO.value spnego = parse_spnego_init(blob) mech_token = spnego[:mech_token] raise ParseError, 'SPNEGO NegTokenInit does not contain a mechanism token' if mech_token.nil? raise ParseError, 'SPNEGO mechanism token must not be empty' if mech_token.empty? begin parse(mech_token) rescue ParseError => e raise ParseError, "SPNEGO mechanism token is not a valid Kerberos token: #{e.}" end else parse_unwrapped(mechanism, encapsulated_token) end unless kerberos_token.ap_req? raise ParseError, "GSS-Kerberos token is not an AP-REQ (token ID #{kerberos_token.token_id_hex})" end if kerberos_token.payload.empty? raise ParseError, 'GSS-Kerberos AP-REQ payload is empty' end kerberos_token.payload rescue ParseError raise rescue OpenSSL::ASN1::ASN1Error, TypeError => e raise ParseError, "unable to extract Kerberos AP-REQ: #{e.}" end |
.kerberos_ap_req?(token) ⇒ Boolean
Tests whether a GSS or SPNEGO token contains a Kerberos AP-REQ.
181 182 183 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 181 def self.kerberos_ap_req?(token) !try_extract_ap_req(token).nil? end |
.parse(token) ⇒ KerberosToken
Parses a bare GSS-Kerberos token and validates its mechanism OID.
59 60 61 62 63 64 65 66 67 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 59 def self.parse(token) blob = binary_string(token) mechanism, encapsulated_token = unwrap_pseudo_asn1(blob) parse_unwrapped(mechanism, encapsulated_token) rescue ParseError raise rescue OpenSSL::ASN1::ASN1Error, TypeError => e raise ParseError, "unable to parse GSS-Kerberos token: #{e.}" end |
.parse_spnego_init(token) ⇒ Hash
Parses an RFC 4178 SPNEGO NegTokenInit.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 75 def self.parse_spnego_init(token) spnego = Rex::Proto::Gss::SpnegoNegTokenInit.parse(binary_string(token)) mechanism_oid = spnego[:gssapi][:oid].value unless mechanism_oid == Rex::Proto::Gss::OID_SPNEGO.value raise ParseError, "unsupported GSS mechanism OID #{mechanism_oid.inspect}" end mech_types = [] index = 0 while (mech_type = spnego.mech_type_list[index]) mech_types << mech_type.value index += 1 end if mech_types.empty? raise ParseError, 'SPNEGO NegTokenInit requires at least one mechanism type' end neg_token_init = spnego[:gssapi][:neg_token_init] { mechanism_oid: mechanism_oid, mech_types: mech_types, preferred_mech: mech_types.first, req_flags: neg_token_init[:context_flags]&.value, mech_token: spnego.mech_token, mech_list_mic: neg_token_init[:mech_list_mic]&.value }.compact rescue ParseError raise rescue RASN1::Error, TypeError => e raise ParseError, "unable to parse SPNEGO NegTokenInit: #{e.}" end |
.parse_spnego_response(token) ⇒ Hash
Parses an RFC 4178 SPNEGO NegTokenResp.
112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 112 def self.parse_spnego_response(token) spnego = Rex::Proto::Gss::SpnegoNegTokenTarg.parse(binary_string(token)) { neg_state: spnego_neg_state_name(spnego.neg_result), supported_mech: spnego.supported_mech, response_token: spnego.response_token, mech_list_mic: spnego.mech_list_mic }.compact rescue ParseError raise rescue RASN1::Error, TypeError => e raise ParseError, "unable to parse SPNEGO NegTokenResp: #{e.}" end |
.try_extract_ap_req(token) ⇒ String?
Attempts to extract an AP-REQ without raising for an unsupported or malformed token. This is useful when dispatching between GSS mechanisms, such as Kerberos and NTLM.
171 172 173 174 175 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 171 def self.try_extract_ap_req(token) extract_ap_req(token) rescue ParseError nil end |
Instance Method Details
#ap_req? ⇒ Boolean
Returns whether this token contains an AP-REQ.
280 281 282 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 280 def ap_req? token_id == TOK_ID_KRB_AP_REQ end |
#token_id_hex ⇒ String
Returns hexadecimal token identifier.
261 262 263 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 261 def token_id_hex token_id.unpack1('H*') end |
#token_type ⇒ String
Returns readable Kerberos token type.
266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/rex/proto/gss/kerberos_token.rb', line 266 def token_type case token_id when TOK_ID_KRB_AP_REQ 'AP-REQ' when TOK_ID_KRB_AP_REP 'AP-REP' when TOK_ID_KRB_ERROR 'KRB-ERROR' else "UNKNOWN (#{token_id_hex})" end end |