Skip to content

Commit b99f044

Browse files
committed
Implement VNC security type 30 (Apple Remote Desktop) authentication
1 parent be4f923 commit b99f044

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed

lib/metasploit/framework/login_scanner/vnc.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ def attempt_login(credential)
4646
service_name: 'vnc'
4747
}
4848

49-
credential.public = nil
50-
5149
begin
5250
# Make our initial socket to the target
5351
disconnect if self.sock
@@ -57,7 +55,11 @@ def attempt_login(credential)
5755
vnc = Rex::Proto::RFB::Client.new(sock, :allow_none => false)
5856

5957
if vnc.handshake
60-
if vnc_auth(vnc,credential.private)
58+
type = vnc.negotiate_authentication
59+
if type != Rex::Proto::RFB::AuthType::ARD
60+
credential.public = nil
61+
end
62+
if vnc_auth(vnc,type,credential.public,credential.private)
6163
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
6264
else
6365
result_options.merge!(
@@ -106,11 +108,13 @@ def set_sane_defaults
106108
# This method attempts the actual VNC authentication. It has built in retries to handle
107109
# delays built into the VNC RFB authentication.
108110
# @param client [Rex::Proto::RFB::Client] The VNC client object to authenticate through
111+
# @param type [Rex::Proto::RFB::AuthType] The VNC authentication type to attempt
112+
# @param username [String] the username to attempt the authentication with
109113
# @param password [String] the password to attempt the authentication with
110-
def vnc_auth(client,password)
114+
def vnc_auth(client,type,username,password)
111115
success = false
112116
5.times do |n|
113-
if client.authenticate(password)
117+
if client.authenticate_with_type(type,username,password)
114118
success = true
115119
break
116120
end

lib/rex/proto/rfb/cipher.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,36 @@ def self.decrypt(cipher, password = "\x17\x52\x6b\x06\x23\x4e\x58\x07")
7575
c.update(cipher)
7676
end
7777

78+
79+
def self.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key)
80+
generator = OpenSSL::BN.new(generator, 2)
81+
prime_modulus = OpenSSL::BN.new(prime_modulus, 2)
82+
peer_public_key = OpenSSL::BN.new(peer_public_key, 2)
83+
84+
user_struct = username + ("\0" * (64 - username.length)) + password + ("\0" * (64 - password.length))
85+
86+
dh_peer = OpenSSL::PKey::DH.new(key_length * 8, generator)
87+
dh_peer.set_key(peer_public_key, nil)
88+
89+
dh = OpenSSL::PKey::DH.new(dh_peer)
90+
dh.set_pqg(prime_modulus, nil, generator)
91+
dh.generate_key!
92+
93+
shared_key = dh.compute_key(dh_peer.pub_key)
94+
95+
md5 = OpenSSL::Digest::MD5.new
96+
key_digest = md5.digest(shared_key)
97+
98+
cipher = OpenSSL::Cipher.new("aes-128-ecb")
99+
cipher.encrypt
100+
cipher.key = key_digest
101+
cipher.padding = 0
102+
ciphertext = cipher.update(user_struct) + cipher.final
103+
104+
response = ciphertext + dh.pub_key.to_s(2)
105+
return response
106+
end
107+
78108
end
79109

80110
end

lib/rex/proto/rfb/client.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,15 @@ def send_client_init
8888
end
8989

9090
def authenticate(password = nil)
91+
authenticate_with_user(nil, password)
92+
end
93+
94+
def authenticate_with_user(username = nil, password = nil)
9195
type = negotiate_authentication
96+
authenticate_with_type(type, username, password)
97+
end
98+
99+
def authenticate_with_type(type, username = nil, password = nil)
92100
return false if not type
93101

94102
# Authenticate.
@@ -99,6 +107,9 @@ def authenticate(password = nil)
99107
when AuthType::VNC
100108
return false if not negotiate_vnc_auth(password)
101109

110+
when AuthType::ARD
111+
return false if not negotiate_ard_auth(username, password)
112+
102113
end
103114

104115
# Handle reading the security result message
@@ -176,6 +187,7 @@ def negotiate_authentication
176187
selected = nil
177188
selected ||= AuthType::None if @opts[:allow_none] and @auth_types.include? AuthType::None
178189
selected ||= AuthType::VNC if @auth_types.include? AuthType::VNC
190+
selected ||= AuthType::ARD if @auth_types.include? AuthType::ARD
179191

180192
if not selected
181193
@error = "No supported authentication method found."
@@ -201,6 +213,21 @@ def negotiate_vnc_auth(password = nil)
201213
true
202214
end
203215

216+
def negotiate_ard_auth(username = nil, password = nil)
217+
generator = @sock.get_once(2)
218+
generator = generator.unpack("n").first
219+
key_length = @sock.get_once(2)
220+
key_length = key_length.unpack("n").first
221+
prime_modulus = @sock.get_once(key_length)
222+
peer_public_key = @sock.get_once(key_length)
223+
224+
response = Cipher.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key)
225+
@sock.put(response)
226+
227+
true
228+
end
229+
230+
204231
attr_reader :error, :majver, :minver, :auth_types
205232
attr_reader :view_only
206233
end

lib/rex/proto/rfb/constants.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class AuthType
3636
GtkVncSasl = 20
3737
MD5Hash = 21
3838
ColinDeanXVP = 22
39+
ARD = 30
3940

4041
def self.to_s(num)
4142
self.constants.each { |c|

0 commit comments

Comments
 (0)