Skip to content

Commit 970beb4

Browse files
committed
TeamCity: Consolidate RSA crypto into login scanner
1 parent a6ee189 commit 970beb4

File tree

2 files changed

+86
-84
lines changed

2 files changed

+86
-84
lines changed

lib/metasploit/framework/login_scanner/teamcity.rb

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
require 'metasploit/framework/login_scanner/http'
2-
require 'rex/proto/teamcity/rsa'
32

43
module Metasploit
54
module Framework
@@ -9,6 +8,91 @@ module LoginScanner
98
# It is responsible for taking a single target, and a list of credentials
109
# and attempting them. It then saves the results.
1110
class Teamcity < HTTP
11+
12+
module Crypto
13+
# https://github.com/openssl/openssl/blob/a08a145d4a7e663dd1e973f06a56e983a5e916f7/crypto/rsa/rsa_pk1.c#L125
14+
# https://datatracker.ietf.org/doc/html/rfc3447#section-7.2.1
15+
def self.pkcs1pad2(text, n)
16+
if n < text.length + 11
17+
raise ArgumentError, 'Message too long'
18+
end
19+
20+
r = Array.new(n, 0)
21+
n -= 1
22+
r[n] = text.length
23+
24+
i = text.length - 1
25+
26+
while i >= 0 && n > 0
27+
c = text[i].ord
28+
i -= 1
29+
n -= 1
30+
r[n] = c % 0x100
31+
end
32+
n -= 1
33+
r[n] = 0
34+
35+
while n > 2
36+
n -= 1
37+
r[n] = rand(1..255) # Can't be a null byte.
38+
end
39+
40+
n -= 1
41+
r[n] = 2
42+
n -= 1
43+
r[n] = 0
44+
45+
r.pack("C*").unpack1("H*").to_i(16)
46+
end
47+
48+
# @param [String] modulus
49+
# @param [String] exponent
50+
# @param [String] text
51+
# @return [String]
52+
def self.rsa_encrypt(modulus, exponent, text)
53+
n = modulus.to_i(16)
54+
e = exponent.to_i(16)
55+
56+
padded_as_big_int = pkcs1pad2(text, (n.bit_length + 7) >> 3)
57+
encrypted = padded_as_big_int.to_bn.mod_exp(e, n)
58+
h = encrypted.to_s(16)
59+
60+
h.length.odd? ? h.prepend('0') : h
61+
end
62+
63+
def self.two_byte_chars?(str)
64+
str.bytesize > str.length
65+
end
66+
67+
def self.max_data_size(str)
68+
# Taken from TeamCity's login page JavaScript sources.
69+
two_byte_chars?(str) ? 58 : 116
70+
end
71+
72+
# @param [String] text The text to encrypt.
73+
# @param [String] public_key The hex representation of the public key to use.
74+
# @return [String] A string blob.
75+
def self.encrypt_data(text, public_key)
76+
exponent = '10001'
77+
e = []
78+
g = max_data_size(text)
79+
80+
c = 0
81+
while c < text.length
82+
b = [text.length, c + g].min
83+
84+
a = text[c..b]
85+
86+
encrypt = rsa_encrypt(public_key, exponent, a)
87+
e.push(encrypt)
88+
c += g
89+
end
90+
91+
e.join('')
92+
end
93+
end
94+
95+
1296
DEFAULT_PORT = 8111
1397
LIKELY_PORTS = [8111]
1498
LIKELY_SERVICE_NAMES = ['skynetflow'] # Comes from nmap 7.95 on MacOS
@@ -67,7 +151,7 @@ def create_login_request(username, password, public_key)
67151
_remember: '',
68152
submitLogin: 'Log in',
69153
publicKey: public_key,
70-
encryptedPassword: Rex::Proto::Teamcity::Rsa.encrypt_data(password, public_key)
154+
encryptedPassword: Crypto.encrypt_data(password, public_key)
71155
}
72156
}
73157
end

lib/rex/proto/teamcity/rsa.rb

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)