11require 'metasploit/framework/login_scanner/http'
2- require 'rex/proto/teamcity/rsa'
32
43module 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
0 commit comments