update formatting, some variable names, and other Pythonic things for winrm

main
Marshall Hallenbeck 2023-03-09 19:58:36 -05:00
parent e9b353d645
commit 160efc6980
4 changed files with 157 additions and 99 deletions

View File

@ -24,7 +24,7 @@ class CMEModule:
def on_login(self, context, connection): def on_login(self, context, connection):
trigger = TriggerAuth() 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: if dce is not None:
logging.debug("Target is vulnerable to DFSCoerce") logging.debug("Target is vulnerable to DFSCoerce")

View File

@ -25,12 +25,12 @@ class CMEModule:
userName = Principal(connection.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) userName = Principal(connection.username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
tgt_with_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain, tgt_with_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain,
unhexlify(connection.lmhash), unhexlify(connection.nthash), connection.aesKey, unhexlify(connection.lm_hash), unhexlify(connection.nt_hash), connection.aesKey,
connection.host,requestPAC=True) connection.host, requestPAC=True)
context.log.highlight("TGT with PAC size " + str(len(tgt_with_pac))) context.log.highlight("TGT with PAC size " + str(len(tgt_with_pac)))
tgt_no_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain, tgt_no_pac, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, connection.password, connection.domain,
unhexlify(connection.lmhash), unhexlify(connection.nthash), connection.aesKey, unhexlify(connection.lm_hash), unhexlify(connection.nt_hash), connection.aesKey,
connection.host,requestPAC=False) connection.host, requestPAC=False)
context.log.highlight("TGT without PAC size " + str(len(tgt_no_pac))) context.log.highlight("TGT without PAC size " + str(len(tgt_no_pac)))
if len(tgt_no_pac) < len(tgt_with_pac): if len(tgt_no_pac) < len(tgt_with_pac):
context.log.highlight("") context.log.highlight("")

View File

@ -41,7 +41,7 @@ class CMEModule:
logging.debug("First try failed. Creating another dce connection...") logging.debug("First try failed. Creating another dce connection...")
# Sleeping mandatory for second try # Sleeping mandatory for second try
time.sleep(2) 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: if self.ipsc:
logging.debug("ipsc = %s", self.ipsc) logging.debug("ipsc = %s", self.ipsc)

View File

@ -1,34 +1,39 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime
import os
import requests import requests
import logging
import configparser
from impacket.smbconnection import SMBConnection, SessionError from impacket.smbconnection import SMBConnection, SessionError
from cme.connection import * from cme.connection import *
from cme.helpers.logger import highlight from cme.helpers.logger import highlight
from cme.helpers.bloodhound import add_user_bh from cme.helpers.bloodhound import add_user_bh
from cme.protocols.ldap.smbldap import LDAPConnect from cme.protocols.ldap.smbldap import LDAPConnect
from cme.logger import CMEAdapter from cme.logger import CMEAdapter
from io import StringIO
from pypsrp.client import Client 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 # The following disables the InsecureRequests warning and the 'Starting new HTTPS connection' log message
from requests.packages.urllib3.exceptions import InsecureRequestWarning from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning) requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class SuppressFilter(logging.Filter): class SuppressFilter(logging.Filter):
# remove warning https://github.com/diyan/pywinrm/issues/269 # remove warning https://github.com/diyan/pywinrm/issues/269
def filter(self, record): def filter(self, record):
return 'wsman' not in record.getMessage() return 'wsman' not in record.getMessage()
class winrm(connection):
class winrm(connection):
def __init__(self, args, db, host): def __init__(self, args, db, host):
self.domain = None self.domain = None
self.server_os = None self.server_os = None
self.output_filename = 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) connection.__init__(self, args, db, host)
@ -70,10 +75,12 @@ class winrm(connection):
self.call_cmd_args() self.call_cmd_args()
def proto_logger(self): def proto_logger(self):
self.logger = CMEAdapter(extra={'protocol': 'SMB', self.logger = CMEAdapter(extra={
'host': self.host, 'protocol': 'SMB',
'port': 'NONE', 'host': self.host,
'hostname': 'NONE'}) 'port': 'NONE',
'hostname': 'NONE'
})
def enum_host_info(self): def enum_host_info(self):
# smb no open, specify the domain # smb no open, specify the domain
@ -111,16 +118,23 @@ class winrm(connection):
self.output_filename = self.output_filename.replace(":", "-") self.output_filename = self.output_filename.replace(":", "-")
def laps_search(self, username, password, ntlm_hash, domain): def laps_search(self, username, password, ntlm_hash, domain):
ldapco = LDAPConnect(self.domain, "389", self.domain) ldap_conn = 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 '' ) login = ldap_conn.plaintext_login(
if connection == False: domain,
logging.debug('LAPS connection failed with account {}'.format(username)) 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 return False
searchFilter = '(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name='+ self.hostname +'))' search_filter = f"(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name='{self.hostname}))"
attributes = ['ms-MCS-AdmPwd','samAccountname'] attributes = ['ms-MCS-AdmPwd', 'samAccountname']
result = connection.search(searchFilter=searchFilter, result = login.search(
attributes=attributes, searchFilter=search_filter,
sizeLimit=0) attributes=attributes,
sizeLimit=0
)
msMCSAdmPwd = '' msMCSAdmPwd = ''
sAMAccountName = '' sAMAccountName = ''
@ -150,19 +164,15 @@ class winrm(connection):
self.logger.info(self.endpoint) self.logger.info(self.endpoint)
else: else:
self.logger.extra['protocol'] = "SMB" self.logger.extra['protocol'] = "SMB"
self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os, self.logger.info(u"{} (name:{}) (domain:{})".format(self.server_os, self.hostname, self.domain))
self.hostname,
self.domain))
self.logger.extra['protocol'] = "HTTP" self.logger.extra['protocol'] = "HTTP"
self.logger.info(self.endpoint) self.logger.info(self.endpoint)
self.logger.extra['protocol'] = "WINRM" self.logger.extra['protocol'] = "WINRM"
if self.args.laps: if self.args.laps:
return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain) return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain)
return True return True
def create_conn_obj(self): def create_conn_obj(self):
endpoints = [ endpoints = [
'https://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5986), '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) '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.username = username
self.domain = domain self.domain = domain
if self.args.ssl and self.args.ignore_ssl_cert: if self.args.ssl and self.args.ignore_ssl_cert:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(domain, self.username), auth='ntlm',
password=self.password, username=u'{}\\{}'.format(domain, self.username),
ssl=True, password=self.password,
cert_validation=False) ssl=True,
cert_validation=False
)
elif self.args.ssl: elif self.args.ssl:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(domain, self.username), auth='ntlm',
password=self.password, username=u'{}\\{}'.format(domain, self.username),
ssl=True) password=self.password,
ssl=True
)
else: else:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(domain, self.username), auth='ntlm',
password=self.password, username=u'{}\\{}'.format(domain, self.username),
ssl=False) 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 # 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) # we could just authenticate without running a command :) (probably)
self.conn.execute_ps("hostname") self.conn.execute_ps("hostname")
self.admin_privs = True self.admin_privs = True
self.logger.success(u'{}\\{}:{} {}'.format(self.domain, self.logger.success(
self.username, u'{}\\{}:{} {}'.format(
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, self.domain,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) 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: if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config) add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success: if not self.args.continue_on_success:
@ -229,14 +249,22 @@ class winrm(connection):
except Exception as e: except Exception as e:
if "with ntlm" in str(e): if "with ntlm" in str(e):
self.logger.error(u'{}\\{}:{}'.format(self.domain, self.logger.error(
self.username, u'{}\\{}:{}'.format(
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8)) self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8
)
)
else: else:
self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, self.logger.error(
self.username, u'{}\\{}:{} "{}"'.format(
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, self.domain,
e)) self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8,
e
)
)
return False return False
@ -244,51 +272,63 @@ class winrm(connection):
try: try:
from urllib3.connectionpool import log from urllib3.connectionpool import log
log.addFilter(SuppressFilter()) log.addFilter(SuppressFilter())
lmhash = '00000000000000000000000000000000:' lm_hash = '00000000000000000000000000000000:'
nthash = '' nt_hash = ''
if not self.args.laps: if not self.args.laps:
self.username = username 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: if ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':') lm_hash, nt_hash = ntlm_hash.split(':')
else: else:
nthash = ntlm_hash nt_hash = ntlm_hash
ntlm_hash = lmhash + nthash ntlm_hash = lm_hash + nt_hash
if lmhash: self.lmhash = lmhash if lm_hash:
if nthash: self.nthash = nthash self.lm_hash = lm_hash
if nt_hash:
self.nt_hash = nt_hash
else: else:
nthash = self.hash nt_hash = self.hash
self.domain = domain self.domain = domain
if self.args.ssl and self.args.ignore_ssl_cert: if self.args.ssl and self.args.ignore_ssl_cert:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(self.domain, self.username), auth='ntlm',
password=lmhash + nthash, username=u'{}\\{}'.format(self.domain, self.username),
ssl=True, password=lm_hash + nt_hash,
cert_validation=False) ssl=True,
cert_validation=False
)
elif self.args.ssl: elif self.args.ssl:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(self.domain, self.username), auth='ntlm',
password=lmhash + nthash, username=u'{}\\{}'.format(self.domain, self.username),
ssl=True) password=lm_hash + nt_hash,
ssl=True
)
else: else:
self.conn = Client(self.host, self.conn = Client(
auth='ntlm', self.host,
username=u'{}\\{}'.format(self.domain, self.username), auth='ntlm',
password=lmhash + nthash, username=u'{}\\{}'.format(self.domain, self.username),
ssl=False) 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 # 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) # we could just authenticate without running a command :) (probably)
self.conn.execute_ps("hostname") self.conn.execute_ps("hostname")
self.admin_privs = True self.admin_privs = True
self.logger.success(u'{}\\{}:{} {}'.format(self.domain, self.logger.success(
self.username, u'{}\\{}:{} {}'.format(
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, self.domain,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))) 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: if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config) add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success: if not self.args.continue_on_success:
@ -296,15 +336,22 @@ class winrm(connection):
except Exception as e: except Exception as e:
if "with ntlm" in str(e): if "with ntlm" in str(e):
self.logger.error(u'{}\\{}:{}'.format(self.domain, self.logger.error(
self.username, u'{}\\{}:{}'.format(
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8)) self.domain,
self.username,
nt_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8
)
)
else: else:
self.logger.error(u'{}\\{}:{} "{}"'.format(self.domain, self.logger.error(
self.username, u'{}\\{}:{} "{}"'.format(
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8, self.domain,
e)) self.username,
nt_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
e
)
)
return False return False
def execute(self, payload=None, get_output=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") self.conn.execute_cmd("del C:\\windows\\temp\\SAM && del C:\\windows\\temp\\SYSTEM")
localOperations = LocalOperations(self.output_filename + ".system") local_operations = LocalOperations(self.output_filename + ".system")
bootKey = localOperations.getBootKey() boot_key = local_operations.getBootKey()
SAM = SAMHashes(self.output_filename + ".sam", bootKey, isRemote=None, perSecretCallback=lambda secret: self.logger.highlight(secret)) SAM = SAMHashes(
self.output_filename + ".sam",
boot_key,
isRemote=None,
perSecretCallback=lambda secret: self.logger.highlight(secret)
)
SAM.dump() SAM.dump()
SAM.export(self.output_filename + ".sam") 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.fetch("C:\\windows\\temp\\SYSTEM", self.output_filename + ".system")
self.conn.execute_cmd("del C:\\windows\\temp\\SYSTEM && del C:\\windows\\temp\\SECURITY") self.conn.execute_cmd("del C:\\windows\\temp\\SYSTEM && del C:\\windows\\temp\\SECURITY")
localOperations = LocalOperations(self.output_filename + ".system") local_operations = LocalOperations(self.output_filename + ".system")
bootKey = localOperations.getBootKey() boot_key = local_operations.getBootKey()
LSA = LSASecrets(self.output_filename + ".security", bootKey, None, isRemote=None, perSecretCallback=lambda secretType, secret: self.logger.highlight(secret)) LSA = LSASecrets(
self.output_filename + ".security",
boot_key,
None,
isRemote=None,
perSecretCallback=lambda secret_type, secret: self.logger.highlight(secret)
)
LSA.dumpCachedHashes() LSA.dumpCachedHashes()
LSA.dumpSecrets() LSA.dumpSecrets()