Class: Rex::Proto::RFB::Cipher

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/rfb/cipher.rb

Overview

A bit of information about the DES algorithm was found here: www.vidarholen.net/contents/junk/vnc.html

In addition, VNC uses two individual 8 byte block encryptions rather than using any block mode (like cbc, ecb, etc).

Class Method Summary collapse

Class Method Details

.decrypt(cipher, password = "\x17\x52\x6b\x06\x23\x4e\x58\x07") ⇒ Object

NOTE: The default password is that of winvnc/etc which is used for encrypting the password(s) on disk/in registry.


64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rex/proto/rfb/cipher.rb', line 64

def self.decrypt(cipher, password = "\x17\x52\x6b\x06\x23\x4e\x58\x07")
  key = self.mangle_password(password)

  # pad the cipher text to 9 bytes
  cipher << ("\x00" * (9 - cipher.length)) if cipher.length < 9

  # NOTE: This only does one 8 byte block
  plain = ''
  c = OpenSSL::Cipher.new('des')
  c.decrypt
  c.key = key
  c.update(cipher)
end

.encrypt(plain, password) ⇒ Object


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rex/proto/rfb/cipher.rb', line 42

def self.encrypt(plain, password)
  key = self.mangle_password(password)

  # pad the plain to 16 chars
  plain << ("\x00" * (16 - plain.length)) if plain.length < 16

  # VNC auth does two 8-byte blocks individually instead supporting some block mode
  cipher = ''
  2.times { |x|
    c = OpenSSL::Cipher.new('des')
    c.encrypt
    c.key = key
    cipher << c.update(plain[x*8, 8])
  }

  cipher
end

.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key) ⇒ Object


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rex/proto/rfb/cipher.rb', line 79

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

.mangle_password(password) ⇒ Object


32
33
34
35
36
37
38
39
40
# File 'lib/rex/proto/rfb/cipher.rb', line 32

def self.mangle_password(password)
  key = ''
  key = password.dup if password
  key.slice!(8,key.length) if key.length > 8
  key << "\x00" * (8 - key.length) if key.length < 8

  # We have to mangle the key so the LSB are kept vs the MSB
  [key.unpack('B*').first.scan(/.{8}/).map! { |e| e.reverse }.join].pack('B*')
end