async def recv_one(self): finished = False forceRecv = 0 retAnswer = b'' while not finished: # At least give me the MSRPCRespHeader, especially important for # TCP/UDP Transports response_data, err = await self.transport.recv(1) #test response_header = MSRPCRespHeader(response_data) off = response_header.get_header_size() if response_header['type'] == MSRPC_FAULT and response_header['frag_len'] >= off+4: status_code = unpack("<L",response_data[off:off+4])[0] if status_code in rpc_status_codes: return None, DCERPCException(rpc_status_codes[status_code]) elif status_code & 0xffff in rpc_status_codes: return None, DCERPCException(rpc_status_codes[status_code & 0xffff]) else: if status_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[status_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[status_code][1] return None, DCERPCException('%s - %s' % (error_msg_short, error_msg_verbose)) else: return None, DCERPCException('Unknown DCE RPC fault status code: %.8x' % status_code) if response_header['flags'] & PFC_LAST_FRAG: # No need to reassembly DCERPC finished = True else: # Forcing Read Recv, we need more packets! forceRecv = 1 return response_data, None
async def recv_one(self): finished = False forceRecv = 0 retAnswer = b'' while not finished: # At least give me the MSRPCRespHeader, especially important for # TCP/UDP Transports response_data, _ = await rr( self.transport.recv(MSRPCRespHeader._SIZE)) #print('DATA: %s' % repr(response_data)) response_header = MSRPCRespHeader(response_data) # Ok, there might be situation, especially with large packets, that # the transport layer didn't send us the full packet's contents # So we gotta check we received it all while len(response_data) < response_header['frag_len']: data, _ = await rr( self.transport.recv(response_header['frag_len'] - len(response_data))) #print('DATA1: %s' % repr(response_data)) response_data += data off = response_header.get_header_size() if response_header['type'] == MSRPC_FAULT and response_header[ 'frag_len'] >= off + 4: status_code = unpack("<L", response_data[off:off + 4])[0] if status_code in rpc_status_codes: return None, DCERPCException(rpc_status_codes[status_code]) elif status_code & 0xffff in rpc_status_codes: return None, DCERPCException(rpc_status_codes[status_code & 0xffff]) else: if status_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[ status_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[ status_code][1] return None, DCERPCException( '%s - %s' % (error_msg_short, error_msg_verbose)) else: return None, DCERPCException( 'Unknown DCE RPC fault status code: %.8x' % status_code) if response_header['flags'] & PFC_LAST_FRAG: # No need to reassembly DCERPC finished = True else: # Forcing Read Recv, we need more packets! forceRecv = 1 return response_data, None
async def request(self, request, uuid=None, checkError=True): """ Creates a requests then dispateches it to _transport.send for singing/encryption asn sending """ try: if self.transfer_syntax == self.NDR64Syntax: request.changeTransferSyntax(self.NDR64Syntax) isNDR64 = True else: isNDR64 = False _, err = await self.call(request.opnum, request, uuid) if err is not None: raise err answer, err = await self.recv() if err is not None: raise err __import__(request.__module__) module = sys.modules[request.__module__] respClass = getattr(module, request.__class__.__name__ + 'Response') if answer[-4:] != b'\x00\x00\x00\x00' and checkError is True: error_code = unpack('<L', answer[-4:])[0] if error_code in rpc_status_codes: # This is an error we can handle exception = DCERPCException(error_code=error_code) else: sessionErrorClass = getattr(module, 'DCERPCSessionError') try: # Try to unpack the answer, even if it is an error, it works most of the times response = respClass(answer, isNDR64=isNDR64) except: # No luck :( exception = sessionErrorClass(error_code=error_code) else: exception = sessionErrorClass(packet=response, error_code=error_code) return None, exception else: response = respClass(answer, isNDR64=isNDR64) return response, None except Exception as e: return None, e
async def bind(self, iface_uuid, alter=0, bogus_binds=0, transfer_syntax=('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')): """ Performs bind operation. Does authentication and sets up the keys for further communication """ try: bind = MSRPCBind() #item['TransferSyntax']['Version'] = 1 ctx = self.ctx for _ in range(bogus_binds): item = CtxItem() item['ContextID'] = ctx item['TransItems'] = 1 item['ContextID'] = ctx # We generate random UUIDs for bogus binds item['AbstractSyntax'] = generate() + stringver_to_bin('2.0') item['TransferSyntax'] = uuidtup_to_bin(transfer_syntax) bind.addCtxItem(item) self.ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = iface_uuid item['TransferSyntax'] = uuidtup_to_bin(transfer_syntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND packet['pduData'] = bind.getData() packet['call_id'] = self.callid if alter: packet['type'] = MSRPC_ALTERCTX if self.auth_level != RPC_C_AUTHN_LEVEL_NONE: #authentication required if self.auth_type == RPC_C_AUTHN_WINNT: #seal flag MUST be turned on in the handshake flags!!!!!!! #it is "signaled via the is_rpc variable" auth, res, err = await self.gssapi.ntlm.authenticate( None, is_rpc=True) if err is not None: return None, err elif self.auth_type == RPC_C_AUTHN_NETLOGON: return False, Exception( 'RPC_C_AUTHN_NETLOGON Not implemented!') elif self.auth_type == RPC_C_AUTHN_GSS_NEGOTIATE: auth, res, err = await self.gssapi.gssapi.authenticate( None, flags = GSSAPIFlags.GSS_C_CONF_FLAG |\ GSSAPIFlags.GSS_C_INTEG_FLAG | \ GSSAPIFlags.GSS_C_SEQUENCE_FLAG | \ GSSAPIFlags.GSS_C_REPLAY_FLAG | \ GSSAPIFlags.GSS_C_MUTUAL_FLAG | \ GSSAPIFlags.GSS_C_DCE_STYLE, seq_number = 0, is_rpc = True ) if err is not None: return None, err else: return None, Exception('Unsupported auth type!') sec_trailer = SEC_TRAILER() sec_trailer['auth_type'] = self.auth_type sec_trailer['auth_level'] = self.auth_level sec_trailer['auth_ctx_id'] = self.ctx + 79231 pad = (4 - (len(packet.get_packet()) % 4)) % 4 if pad != 0: packet['pduData'] += b'\xFF' * pad sec_trailer['auth_pad_len'] = pad packet['sec_trailer'] = sec_trailer packet['auth_data'] = auth _, _ = await rr(self.transport.send(packet.get_packet())) data, _ = await rr(self.recv_one()) resp = MSRPCHeader(data) if resp['type'] == MSRPC_BINDACK or resp[ 'type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(resp.getData()) elif resp['type'] == MSRPC_BINDNAK or resp['type'] == MSRPC_FAULT: if resp['type'] == MSRPC_FAULT: resp = MSRPCRespHeader(resp.getData()) status_code = unpack('<L', resp['pduData'][:4])[0] else: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if status_code in rpc_status_codes: return False, DCERPCException(error_code=status_code) elif status_code in rpc_provider_reason: return False, DCERPCException( "Bind context rejected: %s" % rpc_provider_reason[status_code]) else: return False, DCERPCException( 'Unknown DCE RPC fault status code: %.8x' % status_code) else: return False, DCERPCException( 'Unknown DCE RPC packet type received: %d' % resp['type']) # check ack results for each context, except for the bogus ones for ctx in range(bogus_binds + 1, bindResp['ctx_num'] + 1): ctxItems = bindResp.getCtxItem(ctx) if ctxItems['Result'] != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get( ctxItems['Result'], 'Unknown DCE RPC context result code: %.4x' % ctxItems['Result']) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get( reason, 'Unknown reason code: %.4x' % reason) if (ctxItems['Result'], reason) == ( 2, 1 ): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise DCERPCException(msg) # Save the transfer syntax for later use self.transfer_syntax = ctxItems['TransferSyntax'] # The received transmit size becomes the client's receive size, and the received receive size becomes the client's transmit size. self.__max_xmit_size = bindResp['max_rfrag'] if self.auth_level != RPC_C_AUTHN_LEVEL_NONE: if self.auth_type == RPC_C_AUTHN_WINNT: response, res, err = await self.gssapi.ntlm.authenticate( bindResp['auth_data'], is_rpc=True) if err is not None: return None, err self.__sessionKey = self.gssapi.ntlm.get_session_key() elif self.auth_type == RPC_C_AUTHN_NETLOGON: response = None elif self.auth_type == RPC_C_AUTHN_GSS_NEGOTIATE: response, res, err = await self.gssapi.gssapi.authenticate( bindResp['auth_data'], is_rpc = True, flags = GSSAPIFlags.GSS_C_CONF_FLAG |\ GSSAPIFlags.GSS_C_INTEG_FLAG | \ GSSAPIFlags.GSS_C_SEQUENCE_FLAG | \ GSSAPIFlags.GSS_C_REPLAY_FLAG | \ GSSAPIFlags.GSS_C_MUTUAL_FLAG | \ GSSAPIFlags.GSS_C_DCE_STYLE ) if err is not None: return None, err self.__sessionKey = self.gssapi.gssapi.get_session_key() self.__sequence = 0 if self.auth_level in (RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY): if self.auth_type == RPC_C_AUTHN_WINNT: if self.gssapi.ntlm.is_extended_security() == True: self.__clientSigningKey = self.gssapi.ntlm.get_signkey( ) self.__serverSigningKey = self.gssapi.ntlm.get_signkey( 'Server') self.__clientSealingKey = self.gssapi.ntlm.get_sealkey( ) self.__serverSealingKey = self.gssapi.ntlm.get_sealkey( 'Server') cipher3 = RC4(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = RC4(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = self.gssapi.ntlm.get_session_key( ) self.__serverSigningKey = self.gssapi.ntlm.get_session_key( ) self.__clientSealingKey = self.gssapi.ntlm.get_session_key( ) self.__serverSealingKey = self.gssapi.ntlm.get_session_key( ) cipher = RC4(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt elif self.auth_type == RPC_C_AUTHN_NETLOGON: raise Exception( 'RPC_C_AUTHN_NETLOGON is not implemented!') sec_trailer = SEC_TRAILER() sec_trailer['auth_type'] = self.auth_type sec_trailer['auth_level'] = self.auth_level sec_trailer['auth_ctx_id'] = self.ctx + 79231 if response is not None: if self.auth_type == RPC_C_AUTHN_GSS_NEGOTIATE: alter_ctx = MSRPCHeader() alter_ctx['type'] = MSRPC_ALTERCTX alter_ctx['pduData'] = bind.getData() alter_ctx['sec_trailer'] = sec_trailer alter_ctx['auth_data'] = response await rr( self.transport.send(alter_ctx.get_packet(), forceWriteAndx=1)) self.__sequence = 0 await rr( self.recv_one() ) #recieving the result of alter_context command self.__sequence = self.gssapi.gssapi.selected_authentication_context.seq_number else: auth3 = MSRPCHeader() auth3['type'] = MSRPC_AUTH3 # pad (4 bytes): Can be set to any arbitrary value when set and MUST be # ignored on receipt. The pad field MUST be immediately followed by a # sec_trailer structure whose layout, location, and alignment are as # specified in section 2.2.2.11 auth3[ 'pduData'] = b' ' * 4 #SkelSec: I have spent 3 hours to find this bug, that I caused by replacing spaces to tabs :( auth3['sec_trailer'] = sec_trailer #SkelSec auth3['auth_data'] = response.getData() auth3['auth_data'] = response # Use the same call_id self.callid = resp['call_id'] auth3['call_id'] = self.callid await rr( self.transport.send(auth3.get_packet(), forceWriteAndx=1)) self.callid += 1 return resp, None # means packet is signed, if verifier is wrong it fails except Exception as e: return False, e
async def recv(self): finished = False retAnswer = b'' while not finished: # At least give me the MSRPCRespHeader, especially important for # TCP/UDP Transports response_data, _ = await rr(self.transport.recv(1)) response_header = MSRPCRespHeader(response_data) off = response_header.get_header_size() if response_header['type'] == MSRPC_FAULT and response_header[ 'frag_len'] >= off + 4: status_code = unpack("<L", response_data[off:off + 4])[0] if status_code in rpc_status_codes: raise DCERPCException(rpc_status_codes[status_code]) elif status_code & 0xffff in rpc_status_codes: raise DCERPCException(rpc_status_codes[status_code & 0xffff]) else: if status_code in hresult_errors.ERROR_MESSAGES: error_msg_short = hresult_errors.ERROR_MESSAGES[ status_code][0] error_msg_verbose = hresult_errors.ERROR_MESSAGES[ status_code][1] raise DCERPCException( '%s - %s' % (error_msg_short, error_msg_verbose)) else: raise DCERPCException( 'Unknown DCE RPC fault status code: %.8x' % status_code) if response_header['flags'] & PFC_LAST_FRAG: # No need to reassembly DCERPC finished = True else: # Forcing Read Recv, we need more packets! forceRecv = 1 answer = response_data[off:] auth_len = response_header['auth_len'] if auth_len: auth_len += 8 auth_data = answer[-auth_len:] sec_trailer = SEC_TRAILER(data=auth_data) answer = answer[:-auth_len] if sec_trailer['auth_level'] == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: if self.auth_type == RPC_C_AUTHN_WINNT: if self.gssapi.ntlm.is_extended_security() == True: # TODO: FIX THIS, it's not calculating the signature well # Since I'm not testing it we don't care... yet answer, signature = self.gssapi.ntlm.SEAL( self.__serverSigningKey, self.__serverSealingKey, answer, answer, self.__sequence, self.__serverSealingHandle) else: answer, signature = self.gssapi.ntlm.SEAL( self.__serverSigningKey, self.__serverSealingKey, answer, answer, self.__sequence, self.__serverSealingHandle) self.__sequence += 1 elif self.auth_type == RPC_C_AUTHN_NETLOGON: raise Exception( 'RPC_C_AUTHN_NETLOGON is not implemented!') #from impacket.dcerpc.v5 import nrpc #answer, cfounder = nrpc.UNSEAL(answer, # auth_data[len(sec_trailer):], # self.__sessionKey, # False) #self.__sequence += 1 elif self.auth_type == RPC_C_AUTHN_GSS_NEGOTIATE: if self.__sequence > 0: answer, cfounder = await self.gssapi.gssapi.decrypt( answer, self.__sequence, direction='init', auth_data=auth_data) elif sec_trailer[ 'auth_level'] == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: if self.auth_type == RPC_C_AUTHN_WINNT: ntlmssp = auth_data[12:] if self.gssapi.ntlm.is_extended_security() == True: #TODO: signature = self.gssapi.ntlm.SIGN( self.__serverSigningKey, answer, self.__sequence, self.__serverSealingHandle) else: signature = self.gssapi.ntlm.SIGN( self.__serverSigningKey, ntlmssp, self.__sequence, self.__serverSealingHandle) # Yes.. NTLM2 doesn't increment sequence when receiving # the packet :P self.__sequence += 1 elif self.auth_type == RPC_C_AUTHN_NETLOGON: raise Exception( 'RPC_C_AUTHN_NETLOGON is not implemented!') #from impacket.dcerpc.v5 import nrpc #ntlmssp = auth_data[12:] #signature = nrpc.SIGN(ntlmssp, # self.__confounder, # self.__sequence, # self.__sessionKey, # False) #self.__sequence += 1 elif self.auth_type == RPC_C_AUTHN_GSS_NEGOTIATE: # Do NOT increment the sequence number when Signing Kerberos #self.__sequence += 1 pass if sec_trailer['auth_pad_len']: answer = answer[:-sec_trailer['auth_pad_len']] retAnswer += answer return retAnswer, None