Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JetBrains TeamCity HTTP Login Scanner #19601

Merged
merged 14 commits into from
Nov 15, 2024

Conversation

sjanusz-r7
Copy link
Contributor

@sjanusz-r7 sjanusz-r7 commented Oct 30, 2024

This PR adds in a new login scanner module that targets the JetBrains TeamCity service.

Docker

You can easily set up a TeamCity instance using Docker:
Latest:

docker run -it --rm -p 8111:8111 jetbrains/teamcity-server

10.0:

docker run -it --rm -p 8222:8111 jetbrains/teamcity-server:10.0

9.1.7:

docker run -it --rm -p 8333:8111 jetbrains/teamcity-server:9.1.7

Conveniently, the oldest available image in Docker and the newest images share the same encryption scheme and both work out of the box with no additional configuration or checking of the server version needed.

Verification

  • Boot up a Docker instance of JetBrains TeamCity and perform the initial setup in your browser
  • Start msfconsole
  • use scanner/teamcity/teamcity_login
  • run rport=8111 rhost=127.0.0.1 username=... password=...
  • Verify that you can log in
  • Verify that when using a password file, your user gets locked out but after waiting, you can login as expected

Example

 bundle exec 'ruby ./msfconsole -q'
msf6 auxiliary(scanner/teamcity/teamcity_login) > run

[+] 127.0.0.1:8111 - Login Successful: admin:admin
[!] No active DB -- Credential data will not be saved!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

msf6 auxiliary(scanner/teamcity/teamcity_login) > run pass_file=pass_list.txt password=''

[-] 127.0.0.1:8111 - LOGIN FAILED: admin:admin_password (Incorrect)
[!] No active DB -- Credential data will not be saved!
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:password (Incorrect)
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:my_password111 (Incorrect)
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:f00bar123! (Incorrect)
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:p4$$w0rd01!! (Incorrect)
[*] User 'admin' locked out for 59 seconds. Sleeping, and retrying...
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:admin_admin (Incorrect)
[-] 127.0.0.1:8111 - LOGIN FAILED: admin:adminadmin (Incorrect)
[+] 127.0.0.1:8111 - Login Successful: admin:admin
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

msf6 auxiliary(scanner/teamcity/teamcity_login) > cat pass_list.txt
[*] exec: cat pass_list.txt

admin_password
password
my_password111
f00bar123!
p4$$w0rd01!!
admin_admin
adminadmin
admin

Non-English Support

msf6 auxiliary(scanner/teamcity/teamcity_login) > run username='' password='' user_file=japanese.txt pass_file=japanese.txt

[+] 127.0.0.1:8111 - Login Successful: メタスプライトが大好きです:メタスプライトが大好きです
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

msf6 auxiliary(scanner/teamcity/teamcity_login) > run username='' password='' user_file=french.txt pass_file=french.txt

[+] 127.0.0.1:8111 - Login Successful: çççççç:çççççç
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

class Teamcity < HTTP
DEFAULT_PORT = 8111
LIKELY_PORTS = [8111]
LIKELY_SERVICE_NAMES = ['skynetflow'] # Comes from nmap 7.95 on MacOS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib/rex/proto/teamcity/rsa.rb Outdated Show resolved Hide resolved
lib/metasploit/framework/login_scanner/teamcity.rb Outdated Show resolved Hide resolved
lib/metasploit/framework/login_scanner/teamcity.rb Outdated Show resolved Hide resolved
lib/metasploit/framework/login_scanner/teamcity.rb Outdated Show resolved Hide resolved
lib/msf/core/exploit/remote/http/teamcity.rb Outdated Show resolved Hide resolved
return { status: UNABLE_TO_CONNECT, proof: res } if res.body.match?('ajax') # TODO: Get the exact error message here.
return { status: INVALID_PUBLIC_PART, proof: res } if res.body.match?('publicKeyExpired') # TODO: Invalid public part? Or Incorrect/Unable_to_connect?

{ status: :success, proof: res }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding some positive assertions here to verify we're logged in, versus defaulting to success?

I guess it might be better to have false positives so that users report bugs for us to fix, versus false negatives that cause users to miss valid creds though 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there anything actionable here?

Copy link
Contributor

@adfoster-r7 adfoster-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some non-blocking comments 👍

# Currently, those building blocks are not available, so this is the approach I have implemented.
timeout = res.body.match(/login only in (?<timeout>\d+)s/)&.named_captures&.dig('timeout')&.to_i
if timeout
framework_module.print_status "User '#{username}' locked out for #{timeout} seconds. Sleeping, and retrying..."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're missing the prefix here for when we're scanning multiple hosts

msf6 auxiliary(scanner/teamcity/teamcity_login) > run http://127.0.0.1:8111 user_file=./users.txt pass_file=./pass.txt

[-] 127.0.0.1:8111 - LOGIN FAILED: test_user:mypass (Incorrect)
[-] 127.0.0.1:8111 - LOGIN FAILED: administrator:mypass (Incorrect)
[*] User 'user1' locked out for 59 seconds. Sleeping, and retrying...

i.e. the 127.0.0.1:8111 part

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also be nice to have the user1:#{pass} locked out - so we can see the progress in the wordlist file

return { status: UNABLE_TO_CONNECT, proof: res } if res.body.match?('ajax') # TODO: Get the exact error message here.
return { status: INVALID_PUBLIC_PART, proof: res } if res.body.match?('publicKeyExpired') # TODO: Invalid public part? Or Incorrect/Unable_to_connect?

{ status: :success, proof: res }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there anything actionable here?

@adfoster-r7
Copy link
Contributor

adfoster-r7 commented Nov 15, 2024

Release Notes

Adds a new bruteforce scanner/teamcity/teamcity_login login scanner module that targets the JetBrains TeamCity service.

@adfoster-r7 adfoster-r7 merged commit d039bea into rapid7:master Nov 15, 2024
37 checks passed
@adfoster-r7 adfoster-r7 added the rn-modules release notes for new or majorly enhanced modules label Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-modules release notes for new or majorly enhanced modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants