Module: Metasploit::Framework::LoginScanner::Teamcity::Crypto

Included in:
Metasploit::Framework::LoginScanner::Teamcity
Defined in:
lib/metasploit/framework/login_scanner/teamcity.rb

Instance Method Summary collapse

Instance Method Details

#encrypt_data(text, public_key) ⇒ String

Returns A string blob.

Parameters:

  • text (String)

    The text to encrypt.

  • public_key (String)

    The hex representation of the public key to use.

Returns:

  • (String)

    A string blob.

Raises:

  • (ArgumentError)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/metasploit/framework/login_scanner/teamcity.rb', line 97

def encrypt_data(text, public_key)
  raise ArgumentError, "Cannot encrypt the provided data: '#{text.inspect}'" unless text.is_a?(String)
  raise ArgumentError, "Cannot encrypt data with the public key: '#{public_key.inspect}'" unless public_key.is_a?(String)

  exponent = '10001'
  e = []
  utf_text = text.dup.force_encoding(::Encoding::UTF_8)
  g = max_data_size(utf_text)

  c = 0
  while c < utf_text.length
    b = [utf_text.length, c + g].min

    a = utf_text[c..b]

    encrypt = rsa_encrypt(public_key, exponent, a)
    e.push(encrypt)
    c += g
  end

  e.join('')
end

#max_data_size(str) ⇒ Object

Raises:

  • (ArgumentError)


87
88
89
90
91
92
# File 'lib/metasploit/framework/login_scanner/teamcity.rb', line 87

def max_data_size(str)
  raise ArgumentError, 'Unable to get maximum data size for non-string value' unless str.is_a?(String)

  # Taken from TeamCity's login page JavaScript sources.
  two_byte_chars?(str) ? 58 : 116
end

#pkcs1pad2(text, n) ⇒ Object

Raises:

  • (ArgumentError)


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/metasploit/framework/login_scanner/teamcity.rb', line 15

def pkcs1pad2(text, n)
  raise ArgumentError, "Cannot pad the text: '#{text.inspect}'" unless text.is_a?(String)
  raise ArgumentError, "Invalid message length: '#{n.inspect}'" unless n.is_a?(Integer)

  bytes_per_char = two_byte_chars?(text) ? 2 : 1
  if n < ((bytes_per_char * text.length) + 11)
    raise ArgumentError, 'Message too long'
  end

  ba = Array.new(n, 0)
  n -= 1
  ba[n] = text.length

  i = text.length - 1

  while i >= 0 && n > 0
    char_code = text[i].ord
    i -= 1

    num_bytes = bytes_per_char

    while num_bytes > 0
      next_byte = char_code % 0x100
      char_code >>= 8

      n -= 1
      ba[n] = next_byte

      num_bytes -= 1
    end
  end
  n -= 1
  ba[n] = 0

  while n > 2
    n -= 1
    ba[n] = rand(1..255) # Can't be a null byte.
  end

  n -= 1
  ba[n] = 2
  n -= 1
  ba[n] = 0

  ba.pack("C*").unpack1("H*").to_i(16)
end

#rsa_encrypt(modulus, exponent, text) ⇒ String

Parameters:

  • modulus (String)
  • exponent (String)
  • text (String)

Returns:

  • (String)


66
67
68
69
70
71
72
73
74
75
# File 'lib/metasploit/framework/login_scanner/teamcity.rb', line 66

def rsa_encrypt(modulus, exponent, text)
  n = modulus.to_i(16)
  e = exponent.to_i(16)

  padded_as_big_int = pkcs1pad2(text, (n.bit_length + 7) >> 3)
  encrypted = padded_as_big_int.to_bn.mod_exp(e, n)
  h = encrypted.to_s(16)

  h.length.odd? ? h.prepend('0') : h
end

#two_byte_chars?(str) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (ArgumentError)


77
78
79
80
81
82
83
84
85
# File 'lib/metasploit/framework/login_scanner/teamcity.rb', line 77

def two_byte_chars?(str)
  raise ArgumentError, 'Unable to check char size for non-string value' unless str.is_a?(String)

  str.each_codepoint do |codepoint|
    return true if codepoint >> 8 > 0
  end

  false
end