Skip to content

add batch and legacy version support #4

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 105 additions & 49 deletions mremoteng_decrypt.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,105 @@
#!/usr/bin/env python3

import hashlib
import base64
from Cryptodome.Cipher import AES
import argparse
import sys

def main():
parser = argparse.ArgumentParser(description="Decrypt mRemoteNG passwords.")
group = parser.add_mutually_exclusive_group()
group.add_argument("-f", "--file", help="name of file containing mRemoteNG password")
group.add_argument("-s", "--string", help="base64 string of mRemoteNG password")
parser.add_argument("-p", "--password", help="Custom password", default="mR3m")

if len(sys.argv) < 2:
parser.print_help(sys.stderr)
sys.exit(1)

args = parser.parse_args()
encrypted_data = ""
if args.file != None:
with open(args.file) as f:
encrypted_data = f.read()
encrypted_data = encrypted_data.strip()
encrypted_data = base64.b64decode(encrypted_data)

elif args.string != None:
encrypted_data = args.string
encrypted_data = base64.b64decode(encrypted_data)

else:
print("Please use either the file (-f, --file) or string (-s, --string) flag")
sys.exit(1)

salt = encrypted_data[:16]
associated_data = encrypted_data[:16]
nonce = encrypted_data[16:32]
ciphertext = encrypted_data[32:-16]
tag = encrypted_data[-16:]
key = hashlib.pbkdf2_hmac("sha1", args.password.encode(), salt, 1000, dklen=32)

cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
cipher.update(associated_data)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
print("Password: {}".format(plaintext.decode("utf-8")))

if __name__ == "__main__":
main()
#!/usr/bin/env python3

import hashlib
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
import argparse
import sys
import xml.etree.ElementTree as ET


def decrypt_legacy(encrypted_data, password):
try:
encrypted_data = encrypted_data.strip()
encrypted_data = base64.b64decode(encrypted_data)
initial_vector = encrypted_data[:16]
ciphertext = encrypted_data[16:]
key = hashlib.md5(password.encode()).digest()

cipher = AES.new(key, AES.MODE_CBC, initial_vector)
plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
return plaintext
except Exception as e:
print("Failed to decrypt the password with the following error: {}".format(e))
return b''

def decrypt(encrypted_data, password):
try:
encrypted_data = encrypted_data.strip()
encrypted_data = base64.b64decode(encrypted_data)
salt = encrypted_data[:16]
associated_data = encrypted_data[:16]
nonce = encrypted_data[16:32]
ciphertext = encrypted_data[32:-16]
tag = encrypted_data[-16:]
key = hashlib.pbkdf2_hmac(
"sha1", password.encode(), salt, 1000, dklen=32)

cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
cipher.update(associated_data)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
return plaintext
except Exception as e:
print("Failed to decrypt the password with the following error: {}".format(e))
return b''


def main():
parser = argparse.ArgumentParser(
description="Decrypt mRemoteNG passwords.")
if len(sys.argv) < 2:
parser.print_help(sys.stderr)
sys.exit(1)

group = parser.add_mutually_exclusive_group()
group.add_argument(
"-f", "--file", help="Name of file containing mRemoteNG password")
# Thanks idea from @KingKorSin
group.add_argument(
"-rf", "--realFile", help="Name of the Real mRemoteNG connections file containing the passwords")
group.add_argument(
"-s", "--string", help="base64 string of mRemoteNG password")
parser.add_argument("-p", "--password",
help="Custom password", default="mR3m")
parser.add_argument("-L", "--legacy", help="version <= 1.74", type=bool, default=False)
args = parser.parse_args()

decrypt_func = decrypt
if args.legacy:
decrypt_func = decrypt_legacy

if args.realFile != None:
tree = ET.parse(args.realFile)
root = tree.getroot()
for node in root.iter('Node'):
if node.attrib['Password']:
decPass = decrypt_func(node.attrib['Password'], args.password)
if node.attrib['Username']:
print("Username: {}".format(node.attrib['Username']))
if node.attrib['Hostname']:
print("Hostname: {}".format(node.attrib['Hostname']))
print("Password: {} \n".format(decPass.decode("utf-8")))
sys.exit(1)

elif args.file != None:
with open(args.file) as f:
encrypted_data = f.read()
decPass = decrypt(encrypted_data, args.password)

elif args.string != None:
encrypted_data = args.string
decPass = decrypt(encrypted_data, args.password)

else:
print("Please use either the file (-f, --file) or string (-s, --string) flag")
sys.exit(1)

try:
print("Password: {}".format(decPass.decode("utf-8")))
except Exception as e:
print("Failed to find the password property with the following error: {}".format(e))


if __name__ == "__main__":
main()