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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# 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))

  # OpenSSL 3.0+
  if OpenSSL::PKey.respond_to?(:generate_key)
    dh = OpenSSL::PKey::DH.new(
      OpenSSL::ASN1::Sequence(
        [
          OpenSSL::ASN1::Integer(prime_modulus),
          OpenSSL::ASN1::Integer(generator),
        ]
      ).to_der
    )
    dh = OpenSSL::PKey.generate_key(dh)

    shared_key = dh.compute_key(peer_public_key)
  else
    dh_peer = OpenSSL::PKey::DH.new(key_length * 8, generator)
    dh_peer.set_key(peer_public_key, nil)
    dh_peer_pub_key = dh_peer.pub_key

    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)
  end

  md5 = OpenSSL::Digest.new('MD5')
  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