Skip to content

Commit 555713b

Browse files
committed
Land rapid7#4456 - MS14-068, Kerberos Checksum (plus krb protocol support)
2 parents f9b141c + f213031 commit 555713b

File tree

82 files changed

+10904
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+10904
-0
lines changed

lib/msf/core.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ module Msf
7272
require 'msf/http/typo3'
7373
require 'msf/http/jboss'
7474

75+
# Kerberos Support
76+
require 'msf/kerberos/client'
77+
7578
# Drivers
7679
require 'msf/core/exploit_driver'
7780

lib/msf/kerberos/client.rb

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# -*- coding: binary -*-
2+
require 'rex/proto/kerberos'
3+
4+
module Msf
5+
module Kerberos
6+
module Client
7+
require 'msf/kerberos/client/base'
8+
require 'msf/kerberos/client/as_request'
9+
require 'msf/kerberos/client/as_response'
10+
require 'msf/kerberos/client/tgs_request'
11+
require 'msf/kerberos/client/tgs_response'
12+
require 'msf/kerberos/client/pac'
13+
require 'msf/kerberos/client/cache_credential'
14+
15+
include Msf::Kerberos::Client::Base
16+
include Msf::Kerberos::Client::AsRequest
17+
include Msf::Kerberos::Client::AsResponse
18+
include Msf::Kerberos::Client::TgsRequest
19+
include Msf::Kerberos::Client::TgsResponse
20+
include Msf::Kerberos::Client::Pac
21+
include Msf::Kerberos::Client::CacheCredential
22+
23+
# @!attribute client
24+
# @return [Rex::Proto::Kerberos::Client] The kerberos client
25+
attr_accessor :client
26+
27+
def initialize(info = {})
28+
super
29+
30+
register_options(
31+
[
32+
Opt::RHOST,
33+
Opt::RPORT(88),
34+
OptInt.new('Timeout', [true, 'The TCP timeout to establish connection and read data', 10])
35+
], self.class
36+
)
37+
end
38+
39+
# Returns the target host
40+
#
41+
# @return [String]
42+
def rhost
43+
datastore['RHOST']
44+
end
45+
46+
# Returns the remote port
47+
#
48+
# @return [Fixnum]
49+
def rport
50+
datastore['RPORT']
51+
end
52+
53+
# Returns the TCP timeout
54+
#
55+
# @return [Fixnum]
56+
def timeout
57+
datastore['Timeout']
58+
end
59+
60+
# Returns the kdc peer
61+
#
62+
# @return [String]
63+
def peer
64+
"#{rhost}:#{rport}"
65+
end
66+
67+
# Creates a kerberos connection
68+
#
69+
# @param opts [Hash{Symbol => <String, Fixnum>}]
70+
# @option opts [String] :rhost
71+
# @option opts [<String, Fixnum>] :rport
72+
# @return [Rex::Proto::Kerberos::Client]
73+
def connect(opts={})
74+
kerb_client = Rex::Proto::Kerberos::Client.new(
75+
host: opts[:rhost] || rhost,
76+
port: (opts[:rport] || rport).to_i,
77+
timeout: (opts[:timeout] || timeout).to_i,
78+
context:
79+
{
80+
'Msf' => framework,
81+
'MsfExploit' => self,
82+
},
83+
protocol: 'tcp'
84+
)
85+
86+
disconnect if client
87+
self.client = kerb_client
88+
89+
kerb_client
90+
end
91+
92+
# Disconnects the Kerberos client
93+
#
94+
# @param kerb_client [Rex::Proto::Kerberos::Client] the client to disconnect
95+
def disconnect(kerb_client = client)
96+
kerb_client.close if kerb_client
97+
98+
if kerb_client == client
99+
self.client = nil
100+
end
101+
end
102+
103+
# Performs cleanup as necessary, disconnecting the Kerberos client
104+
# if it's still established.
105+
def cleanup
106+
super
107+
disconnect
108+
end
109+
110+
# Sends a kerberos AS request and reads the response
111+
#
112+
# @param opts [Hash]
113+
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
114+
# @see Msf::Kerberos::Client::AsRequest#build_as_request
115+
# @see Rex::Proto::Kerberos::Model::KdcResponse
116+
def send_request_as(opts = {})
117+
connect(opts)
118+
req = build_as_request(opts)
119+
res = client.send_recv(req)
120+
disconnect
121+
res
122+
end
123+
124+
# Sends a kerberos AS request and reads the response
125+
#
126+
# @param opts [Hash]
127+
# @return [Rex::Proto::Kerberos::Model::KdcResponse]
128+
# @see Msf::Kerberos::Client::TgsRequest#build_tgs_request
129+
# @see Rex::Proto::Kerberos::Model::KdcResponse
130+
def send_request_tgs(opts = {})
131+
connect(opts)
132+
req = build_tgs_request(opts)
133+
res = client.send_recv(req)
134+
disconnect
135+
res
136+
end
137+
end
138+
end
139+
end

lib/msf/kerberos/client/as_request.rb

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# -*- coding: binary -*-
2+
require 'rex/proto/kerberos'
3+
4+
module Msf
5+
module Kerberos
6+
module Client
7+
module AsRequest
8+
9+
# Builds a kerberos AS request
10+
#
11+
# @param opts [Hash{Symbol => <Array<Rex::Proto::Kerberos::Model::PreAuthData>, Rex::Proto::Kerberos::Model::KdcRequestBody>}]
12+
# @option opts [Array<Rex::Proto::Kerberos::Model::PreAuthData>] :pa_data
13+
# @option opts [Rex::Proto::Kerberos::Model::KdcRequestBody] :body
14+
# @return [Rex::Proto::Kerberos::Model::KdcRequest]
15+
# @see [Rex::Proto::Kerberos::Model::KdcRequest]
16+
# @see #build_as_pa_time_stamp
17+
# @see #build_as_request_body
18+
def build_as_request(opts = {})
19+
pa_data = opts[:pa_data] || build_as_pa_time_stamp(opts)
20+
body = opts[:body] || build_as_request_body(opts)
21+
22+
request = Rex::Proto::Kerberos::Model::KdcRequest.new(
23+
pvno: 5,
24+
msg_type: Rex::Proto::Kerberos::Model::AS_REQ,
25+
pa_data: pa_data,
26+
req_body: body
27+
)
28+
29+
request
30+
end
31+
32+
# Builds a kerberos PA-ENC-TIMESTAMP pre authenticated structure
33+
#
34+
# @param opts [Hash{Symbol => <Time, Fixnum, String>}]
35+
# @option opts [Time] :time_stamp
36+
# @option opts [Fixnum] :pausec
37+
# @option opts [Fixnum] :etype
38+
# @option opts [String] :key
39+
# @return [Rex::Proto::Kerberos::Model::PreAuthData]
40+
# @see Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp
41+
# @see Rex::Proto::Kerberos::Model::EncryptedData
42+
# @see Rex::Proto::Kerberos::Model::PreAuthData
43+
def build_as_pa_time_stamp(opts = {})
44+
time_stamp = opts[:time_stamp] || Time.now
45+
pausec = opts[:pausec] || 0
46+
etype = opts[:etype] || Rex::Proto::Kerberos::Crypto::RC4_HMAC
47+
key = opts[:key] || ''
48+
49+
pa_time_stamp = Rex::Proto::Kerberos::Model::PreAuthEncTimeStamp.new(
50+
pa_time_stamp: time_stamp,
51+
pausec: pausec
52+
)
53+
54+
enc_time_stamp = Rex::Proto::Kerberos::Model::EncryptedData.new(
55+
etype: etype,
56+
cipher: pa_time_stamp.encrypt(etype, key)
57+
)
58+
59+
pa_enc_time_stamp = Rex::Proto::Kerberos::Model::PreAuthData.new(
60+
type: Rex::Proto::Kerberos::Model::PA_ENC_TIMESTAMP,
61+
value: enc_time_stamp.encode
62+
)
63+
64+
pa_enc_time_stamp
65+
end
66+
67+
# Builds a kerberos AS request body
68+
#
69+
# @param opts [Hash{Symbol => <Fixnum, Time, String, Rex::Proto::Kerberos::Model::PrincipalName>}]
70+
# @option opts [Fixnum] :options
71+
# @option opts [Time] :from
72+
# @option opts [Time] :till
73+
# @option opts [Time] :rtime
74+
# @option opts [Fixnum] :nonce
75+
# @option opts [Fixnum] :etype
76+
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :cname
77+
# @option opts [String] :realm
78+
# @option opts [Rex::Proto::Kerberos::Model::PrincipalName] :sname
79+
# @return [Rex::Proto::Kerberos::Model::KdcRequestBody]
80+
# @see #build_client_name
81+
# @see #build_server_name
82+
# @see Rex::Proto::Kerberos::Model::KdcRequestBody
83+
def build_as_request_body(opts = {})
84+
options = opts[:options] || 0x50800000 # Forwardable, Proxiable, Renewable
85+
from = opts[:from] || Time.utc('1970-01-01-01 00:00:00')
86+
till = opts[:till] || Time.utc('1970-01-01-01 00:00:00')
87+
rtime = opts[:rtime] || Time.utc('1970-01-01-01 00:00:00')
88+
nonce = opts[:nonce] || Rex::Text.rand_text_numeric(6).to_i
89+
etype = opts[:etype] || [Rex::Proto::Kerberos::Crypto::RC4_HMAC]
90+
cname = opts[:cname] || build_client_name(opts)
91+
realm = opts[:realm] || ''
92+
sname = opts[:sname] || build_server_name(opts)
93+
94+
body = Rex::Proto::Kerberos::Model::KdcRequestBody.new(
95+
options: options,
96+
cname: cname,
97+
realm: realm,
98+
sname: sname,
99+
from: from,
100+
till: till,
101+
rtime: rtime,
102+
nonce: nonce,
103+
etype: etype
104+
)
105+
106+
body
107+
end
108+
end
109+
end
110+
end
111+
end
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# -*- coding: binary -*-
2+
require 'rex/proto/kerberos'
3+
4+
module Msf
5+
module Kerberos
6+
module Client
7+
module AsResponse
8+
9+
# Extracts the session key from a Kerberos AS Response
10+
#
11+
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
12+
# @param key [String]
13+
# @return [Rex::Proto::Kerberos::Model::EncryptionKey]
14+
# @see Rex::Proto::Kerberos::Model::KdcResponse
15+
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
16+
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
17+
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
18+
# @see Rex::Proto::Kerberos::Model::EncryptionKey
19+
def extract_session_key(res, key)
20+
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
21+
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
22+
23+
enc_kdc_res.key
24+
end
25+
26+
# Extracts the logon time from a Kerberos AS Response
27+
#
28+
# @param res [Rex::Proto::Kerberos::Model::KdcResponse]
29+
# @param key [String]
30+
# @return [Fixnum]
31+
# @see Rex::Proto::Kerberos::Model::KdcResponse
32+
# @see Rex::Proto::Kerberos::Model::EncryptedData.decrypt
33+
# @see Rex::Proto::Kerberos::Model::EncKdcResponse
34+
# @see Rex::Proto::Kerberos::Model::EncKdcResponse.decode
35+
def extract_logon_time(res, key)
36+
decrypt_res = res.enc_part.decrypt(key, Rex::Proto::Kerberos::Crypto::ENC_AS_RESPONSE)
37+
enc_kdc_res = Rex::Proto::Kerberos::Model::EncKdcResponse.decode(decrypt_res)
38+
39+
auth_time = enc_kdc_res.auth_time
40+
41+
auth_time.to_i
42+
end
43+
end
44+
end
45+
end
46+
end

lib/msf/kerberos/client/base.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf
4+
module Kerberos
5+
module Client
6+
module Base
7+
8+
# Builds a kerberos Client Name Principal
9+
#
10+
# @param opts [Hash{Symbol => <String, Fixnum>}]
11+
# @option opts [String] :client_name the client's name
12+
# @option opts [Fixnum] :client_type the client's name type
13+
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
14+
# @see Rex::Proto::Kerberos::Model::PrincipalName
15+
def build_client_name(opts = {})
16+
name = opts[:client_name] || ''
17+
name_type = opts[:client_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
18+
19+
Rex::Proto::Kerberos::Model::PrincipalName.new(
20+
name_type: name_type,
21+
name_string: name.split('/')
22+
)
23+
end
24+
25+
# Builds a kerberos Server Name Principal
26+
#
27+
# @param opts [Hash{Symbol => <String, Fixnum>}]
28+
# @option opts [String] :server_name the server's name
29+
# @option opts [Fixnum] :server_type the server's name type
30+
# @return [Rex::Proto::Kerberos::Model::PrincipalName]
31+
# @see Rex::Proto::Kerberos::Model::PrincipalName
32+
def build_server_name(opts = {})
33+
name = opts[:server_name] || ''
34+
name_type = opts[:server_type] || Rex::Proto::Kerberos::Model::NT_PRINCIPAL
35+
36+
Rex::Proto::Kerberos::Model::PrincipalName.new(
37+
name_type: name_type,
38+
name_string: name.split('/')
39+
)
40+
end
41+
end
42+
end
43+
end
44+
end

0 commit comments

Comments
 (0)