Add support for PreAuthEncTimeStamp decoding/decrypting

bug/bundler_fix
jvazquez-r7 2014-12-10 18:33:46 -06:00
parent 785ff60d8e
commit 5d2ff5982e
4 changed files with 257 additions and 28 deletions

View File

@ -49,6 +49,9 @@ module Rex
PA_ENC_TIMESTAMP = 2
PA_PW_SALT = 3
PA_PAC_REQUEST = 128
# From RFC-4757: The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows
KERB_ETYPE_RC4_HMAC = 23
end
end
end

View File

@ -6,8 +6,97 @@ module Rex
module Model
module Field
# This class is a representation of a PA-ENC-TIMESTAMP, an encrypted timestamp
class PreAuthEncTimeStamp < Rex::Proto::Kerberos::Model::Type::EncryptedData
class PreAuthEncTimeStamp < Element
# @!attribute pa_time_stamp
# @return [Time] client's time
attr_accessor :pa_time_stamp
# @!attribute pausec
# @return [Fixnum] optional microseconds client's time
attr_accessor :pausec
# Decodes a Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp
#
# @param input [String, OpenSSL::ASN1::Sequence] the input to decode from
# @return [self] if decoding succeeds
# @raise [RuntimeError] if decoding doesn't succeed
def decode(input)
case input
when String
decode_string(input)
when OpenSSL::ASN1::Sequence
decode_asn1(input)
else
raise ::RuntimeError, 'Failed to decode EncryptedData Name, invalid input'
end
self
end
def encode
raise ::RuntimeError, 'EncryptedData encoding is not supported'
end
# Decrypts a Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp
#
# @param input [String, OpenSSL::ASN1::Sequence] the input to decrypt from
# @return [Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp] if decryption succeeds
# @raise [RuntimeError] if decryption doesn't succeed
def self.decrypt(input, key)
elem = PreAuthEncTimeStamp.new
elem.decrypt(input, key)
elem
end
# Decrypts a Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp
#
# @param input [String, OpenSSL::ASN1::Sequence] the input to decrypt from
# @return [self] if decryption succeeds
# @raise [RuntimeError] if decryption doesn't succeed
def decrypt(input, key)
ed = Rex::Proto::Kerberos::Model::Type::EncryptedData.decode(input)
decrypted = ed.decrypt(key, 1)
decode(decrypted[8, decrypted.length - 1])
self
end
private
# Decodes a Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp
#
# @param input [String] the input to decode from
def decode_string(input)
asn1 = OpenSSL::ASN1.decode(input)
decode_asn1(asn1)
end
# Decodes a Rex::Proto::Kerberos::Model::Type::PreAuthEncTimeStamp from an
# OpenSSL::ASN1::Sequence
#
# @param input [OpenSSL::ASN1::Sequence] the input to decode from
def decode_asn1(input)
self.pa_time_stamp = decode_pa_time_stamp(input.value[0])
self.pausec = decode_pausec(input.value[1])
end
# Decodes the decode_pa_time_stamp from an OpenSSL::ASN1::ASN1Data
#
# @param input [OpenSSL::ASN1::ASN1Data] the input to decode from
# @return [Boolean]
def decode_pa_time_stamp(input)
input.value[0].value
end
# Decodes the pausec from an OpenSSL::ASN1::ASN1Data
#
# @param input [OpenSSL::ASN1::ASN1Data] the input to decode from
# @return [Fixnum]
def decode_pausec(input)
input.value[0].value.to_i
end
end
end
end

View File

@ -40,8 +40,61 @@ module Rex
raise ::RuntimeError, 'EncryptedData encoding is not supported'
end
# Decrypts the cipher with etype encryption schema
#
# @param key [String] the key to decrypt
# @param key [Fixnum] the message type
# @return [String] if decryption succeeds
# @raise [RuntimeError] if decryption doesn't succeed
def decrypt(key, msg_type)
if cipher.nil? or cipher.empty?
return ''
end
res = ''
case etype
when KERB_ETYPE_RC4_HMAC
res = decrypt_rc4_hmac(key, msg_type)
else
raise ::RuntimeError, 'EncryptedData encoding is not supported'
end
res
end
private
# Decrypts the cipher using RC4-HMAC schema
#
# @param key [String] the key to decrypt
# @param key [Fixnum] the message type
# @return [String] if decryption succeeds
# @raise [RuntimeError] if decryption doesn't succeed
def decrypt_rc4_hmac(key, msg_type)
unless cipher && cipher.length > 16
raise ::RuntimeError, 'RC4-HMAC decryption failed'
end
my_key = OpenSSL::Digest.digest('MD4', Rex::Text.to_unicode(key))
checksum = cipher[0, 16]
data = cipher[16, cipher.length - 1]
k1 = OpenSSL::HMAC.digest('MD5', my_key, [msg_type].pack('V'))
k3 = OpenSSL::HMAC.digest('MD5', k1, checksum)
cipher = OpenSSL::Cipher::Cipher.new("rc4")
cipher.decrypt
cipher.key = k3
decrypted = cipher.update(data) + cipher.final
if OpenSSL::HMAC.digest('MD5', k1, decrypted) != checksum
raise ::RuntimeError, 'RC4-HMAC decryption failed, incorrect checksum verification'
end
decrypted
end
# Decodes a Rex::Proto::Kerberos::Model::Type::EncryptedData from an String
#
# @param input [String] the input to decode from

View File

@ -10,37 +10,78 @@ describe Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp do
end
=begin
#<OpenSSL::ASN1::Sequence:0x007ff9c1892de0
@infinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::ASN1Data:0x007ff9c1893538
#<OpenSSL::ASN1::Sequence:0x007ff9c3830210
@infinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[#<OpenSSL::ASN1::ASN1Data:0x007ff9c38302d8
@infinite_length=false,
@tag=0,
@tag_class=:CONTEXT_SPECIFIC,
@value=
[#<OpenSSL::ASN1::Integer:0x007ff9c18936a0
@infinite_length=false,
@tag=2,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=#<OpenSSL::BN:0x007ff9c1893a10>>]>,
#<OpenSSL::ASN1::ASN1Data:0x007ff9c1892e58
[#<OpenSSL::ASN1::GeneralizedTime:0x007ff9c3830300
@infinite_length=false,
@tag=24,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=2014-12-09 01:09:09 UTC>]>,
#<OpenSSL::ASN1::ASN1Data:0x007ff9c3830238
@infinite_length=false,
@tag=2,
@tag=1,
@tag_class=:CONTEXT_SPECIFIC,
@value=
[#<OpenSSL::ASN1::OctetString:0x007ff9c1893150
@infinite_length=false,
@tag=4,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
"`\xAES\xA5\vV.Fa\xD9\xD6\x89\x98\xFCy\x9DEs}\r\x8Ax\x84M\xD7|\xC6P\b\x8D\xAB\"y\xC3\x8D\xD3\xAF\x9F^\xB7\xB8\x9BW\xC5\xC9\xC5\xEA\x90\x89\xC3cX">]>]>
[#<OpenSSL::ASN1::Integer:0x007ff9c3830260
@infinite_length=false,
@tag=2,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=#<OpenSSL::BN:0x007ff9c3830288>>]>]>
=end
let(:sample) do
let(:time_stamp_raw) do
"\x30\x1a\xa0\x11\x18\x0f\x32\x30" +
"\x31\x34\x31\x32\x30\x39\x30\x31" +
"\x30\x39\x30\x39\x5a\xa1\x05\x02" +
"\x03\x08\xfc\xc8"
end
=begin
#<OpenSSL::ASN1::Sequence:0x007ff70196b158
@infinite_length=false,
@tag=16,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
[
#<OpenSSL::ASN1::ASN1Data:0x007ff70196b2c0
@infinite_length=false,
@tag=0,
@tag_class=:CONTEXT_SPECIFIC,
@value=
[#<OpenSSL::ASN1::Integer:0x007ff70196b2e8
@infinite_length=false,
@tag=2,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=#<OpenSSL::BN:0x007ff70196b338>>
]>,
#<OpenSSL::ASN1::ASN1Data:0x007ff70196b1a8
@infinite_length=false,
@tag=2,
@tag_class=:CONTEXT_SPECIFIC,
@value=
[#<OpenSSL::ASN1::OctetString:0x007ff70196b1f8
@infinite_length=false,
@tag=4,
@tag_class=:UNIVERSAL,
@tagging=nil,
@value=
"`\xAES\xA5\vV.Fa\xD9\xD6\x89\x98\xFCy\x9DEs}\r\x8Ax\x84M\xD7|\xC6P\b\x8D\xAB\"y\xC3\x8D\xD3\xAF\x9F^\xB7\xB8\x9BW\xC5\xC9\xC5\xEA\x90\x89\xC3cX">
]>
]>
=end
let(:sample_encrypted_data) do
"\x30\x3d\xa0\x03\x02\x01\x17\xa2\x36\x04\x34\x60\xae\x53\xa5\x0b" +
"\x56\x2e\x46\x61\xd9\xd6\x89\x98\xfc\x79\x9d\x45\x73\x7d\x0d\x8a" +
"\x78\x84\x4d\xd7\x7c\xc6\x50\x08\x8d\xab\x22\x79\xc3\x8d\xd3\xaf" +
@ -55,12 +96,55 @@ describe Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp do
describe "#decode" do
it "returns the decoded Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp" do
expect(pre_auth_enc_time_stamp.decode(sample)).to eq(pre_auth_enc_time_stamp)
expect(pre_auth_enc_time_stamp.decode(time_stamp_raw)).to eq(pre_auth_enc_time_stamp)
end
it "decodes etype" do
pre_auth_enc_time_stamp.decode(sample)
expect(pre_auth_enc_time_stamp.etype).to eq(23)
it "decodes pa_time_stamp correctly" do
pre_auth_enc_time_stamp.decode(time_stamp_raw)
expect(pre_auth_enc_time_stamp.pa_time_stamp.to_s).to eq('2014-12-09 01:09:09 UTC')
end
it "decodes pausec correctly" do
pre_auth_enc_time_stamp.decode(time_stamp_raw)
expect(pre_auth_enc_time_stamp.pausec).to eq(589000)
end
end
describe ".decrypt" do
context "correct key" do
it "returns the decoded Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp" do
expect(described_class.decrypt(sample_encrypted_data, 'juan')).to be_a(Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp)
end
end
context "incorrect key" do
it "raises RuntimeError when decrypting with the incorrect key" do
expect { described_class.decrypt(sample_encrypted_data, 'error') }.to raise_error(RuntimeError)
end
end
end
describe "#decrypt" do
context "correct key" do
it "returns the decoded Rex::Proto::Kerberos::Model::Field::PreAuthEncTimeStamp" do
expect(pre_auth_enc_time_stamp.decrypt(sample_encrypted_data, 'juan')).to eq(pre_auth_enc_time_stamp)
end
it "decodes pa_time_stamp correctly" do
pre_auth_enc_time_stamp.decrypt(sample_encrypted_data, 'juan')
expect(pre_auth_enc_time_stamp.pa_time_stamp.to_s).to eq('2014-12-09 01:09:09 UTC')
end
it "decodes pausec correctly" do
pre_auth_enc_time_stamp.decrypt(sample_encrypted_data, 'juan')
expect(pre_auth_enc_time_stamp.pausec).to eq(589000)
end
end
context "when incorrect key" do
it "raises RuntimeError when decrypting with the incorrect key" do
expect { pre_auth_enc_time_stamp.decrypt(sample_encrypted_data, 'error') }.to raise_error(RuntimeError)
end
end
end