Skip to content

Hash Types

Types of Hashes

How they work

Test Hashes:

import hashlib, sys, os, hmac
from Crypto.Cipher import DES

def expand_DES_key(key):
	# Expand the key from a 7-byte password key into a 8-byte DES key
	if not isinstance(key, bytes):
		key = bytes(key, encoding='utf8')
	key  = bytearray(key[:7]).ljust(7, b'\x00')
	s = bytearray()
	s.append(((key[0] >> 1) & 0x7f) << 1)
	s.append(((key[0] & 0x01) << 6 | ((key[1] >> 2) & 0x3f)) << 1)
	s.append(((key[1] & 0x03) << 5 | ((key[2] >> 3) & 0x1f)) << 1)
	s.append(((key[2] & 0x07) << 4 | ((key[3] >> 4) & 0x0f)) << 1)
	s.append(((key[3] & 0x0f) << 3 | ((key[4] >> 5) & 0x07)) << 1)
	s.append(((key[4] & 0x1f) << 2 | ((key[5] >> 6) & 0x03)) << 1)
	s.append(((key[5] & 0x3f) << 1 | ((key[6] >> 7) & 0x01)) << 1)
	s.append((key[6] & 0x7f) << 1)
	return bytes(s)


def DES_block(key, msg):
	key = expand_DES_key(key)
	cipher = DES.new(key,DES.MODE_ECB)
	return cipher.encrypt(msg)    

def NT(password):
	return hashlib.new("md4", password.encode('utf_16le')).digest()

def NTLMv2(input_hash, user_name, domain_name):
	return hmac.new(input_hash, user_name + domain_name, "md5").digest()

def LM(password):
	password = password.upper()
	lmhash  = DES_block(password[:7], b"KGS!@#$%")
	lmhash += DES_block(password[7:14], b"KGS!@#$%")
	return lmhash


if __name__ == '__main__':

	#server_challenge = os.urandom(8)
	server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef"
	client_challenge = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"

	domain_name = "Domain"
	user_name = "User"
	password = "Password"
	server_name = b"Server"
	#server_name = b'\x02\x00\x0c\x00\x44\x00\x6f\x00\x6d\x00\x61\x00\x69\x00\x6e\x00\x01\x00\x0c\x00\x53\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00\x00\x00\x00\x00'
		
	nt_hash = NT(password)
	lm_hash = LM(password)

	### Local Hashes
	print("Password: {}".format(password))
	print("Server Nonce: {}".format(server_challenge.hex()))
	print("NT Hash: {}".format(nt_hash.hex()))
	print("LM Hash: {}\n".format(lm_hash.hex()))

	### Version 1 Net Hashes
	nt_response = DES_block(nt_hash[:7], server_challenge) + DES_block(nt_hash[7:14], server_challenge) + DES_block(nt_hash[14:], server_challenge)
	lm_response = DES_block(lm_hash[:7], server_challenge) + DES_block(lm_hash[7:14], server_challenge) + DES_block(lm_hash[14:], server_challenge)

	print("Net-NTv1 Response: {}".format(nt_response.hex()))
	print("Net-LMv1 Response: {}".format(lm_response.hex()))

	shared_key = hashlib.new("md4", nt_hash).digest()

	print("Net-NTLMv1 Shared Key: {}\n".format(shared_key.hex()))

	### Version 2 Net Hashes

	nt_response_key_v2 = NTLMv2(nt_hash, user_name.upper().encode('utf_16le'), domain_name.encode('utf_16le'))
	lm_response_key_v2 = NTLMv2(lm_hash, user_name.upper().encode('utf_16le'), domain_name.encode('utf_16le'))

	# Response Server Version + \x00\x00\x00\x00\x00\x00 + Time Stamp + Client Challenge + \x00\x00\x00\x00 + server_name + \x00\x00\x00\x00
	temp = b'\x01\x01' + b'\x00' * 6 + b'\x00' * 8 + client_challenge +  b'\x00' * 4 + server_name + b'\x00' * 4 
	temp_response = NTLMv2(nt_response_key_v2, server_challenge, temp)
	
	nt_response_v2 = temp_response + temp
	lm_response_v2 = NTLMv2(nt_response_key_v2, server_challenge, client_challenge) + client_challenge
	shared_key_v2  = NTLMv2(nt_response_key_v2, temp_response, b"") + b""

	print("Net-NTv2 Response Key: {}".format(nt_response_key_v2.hex()))
	print("Net-LMv2 Response Key: {}".format(lm_response_key_v2.hex()))

	print("Net-NTv2 Response: {}".format(nt_response_v2.hex()))
	print("Net-LMv2 Response: {}".format(lm_response_v2.hex()))

	print("Net-NTLMv2 Shared Key: {}".format(shared_key_v2.hex()))

	#Verify asserts from https://github.com/SecureAuthCorp/impacket/blob/429f97a894d35473d478cbacff5919739ae409b4/tests/SMB_RPC/test_ntlm.py
	assert(lm_hash == bytearray(b'\xe5,\xacgA\x9a\x9a"J;\x10\x8f?\xa6\xcbm'))
	assert(nt_hash == bytearray(b'\xa4\xf4\x9c\x40\x65\x10\xbd\xca\xb6\x82\x4e\xe7\xc3\x0f\xd8\x52'))
	
	assert(nt_response == bytearray(b'\x67\xC4\x30\x11\xF3\x02\x98\xA2\xAD\x35\xEC\xE6\x4F\x16\x33\x1C\x44\xBD\xBE\xD9\x27\x84\x1F\x94'))
	assert(lm_response == bytearray(b'\x98\xDE\xF7\xB8\x7F\x88\xAA\x5D\xAF\xE2\xDF\x77\x96\x88\xA1\x72\xde\xf1\x1c\x7d\x5c\xcd\xef\x13'))
	assert(shared_key == bytearray(b'\xD8\x72\x62\xB0\xCD\xE4\xB1\xCB\x74\x99\xBE\xCC\xCD\xF1\x07\x84'))

	assert(nt_response_key_v2==bytearray(b'\x0c\x86\x8a\x40\x3b\xfd\x7a\x93\xa3\x00\x1e\xf2\x2e\xf0\x2e\x3f'))
	assert(lm_response_key_v2==bytearray(b'\xc2\xd6\x9a\xca\xe7\x12\x59\xa9\xc7\xcd\x95\x81\x63\xa9\x8b\x1c'))	
	assert(nt_response_v2[:16]==bytearray(b'\x35\x6c\x72\x25\x90\x7b\x92\x6d\x1b\xac\xf4\x0a\x41\x77\xe0\x58'))
	assert(lm_response_v2==bytearray(b'\x86\xc3\x50\x97\xac\x9c\xec\x10\x25\x54\x76\x4a\x57\xcc\xcc\x19\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'))
	assert(shared_key_v2==bytearray(b'\x8e\x63\xba\x6e\x5f\x82\x76\x30\xca\xaf\x3e\xc7\xa8\x9b\xd8\xa8'))
	

LM Hashes

  • case-insensitive
  • limited character set of only 142 characters
  • splits the password into two 7-character chunks
    • padding as necessary.
  • LM was turned off by default starting in Windows Vista/Server 2008
  • supports Pass the Hash

How the password is hashed:
1. Convert all lower case to upper case
2. Pad password to 14 characters with NULL characters
3. Split the password to two 7 character chunks
4. Create two DES keys from each 7 character chunk
5. DES encrypt the string "KGS!@#$%" with these two chunks
6. Concatenate the two DES encrypted strings. This is the LM hash.

Example of a LM Hash:
299BD128C1101FD6

Cracking Hashes

john --format=lm hash.txt
hashcat -m 3000 -a 3 hash.txt

NTHash (A.K.A. NTLM)

This is the way passwords are stored on modern Windows systems, and can be obtained by dumping the SAM database, or using Mimikatz. They are also stored on domain controllers in the NTDS file. These are the hashes you can use to pass-the-hash.

  • case-sensitive
  • supports almost the entire Unicode character set of 65,536 characters
  • NT hash calculates the hash based on the entire password the user entered
  • supports Pass the Hash

How the password is hashed:

def NT(password):
	return hashlib.new("md4", password.encode('utf_16le')).digest()

Example of a NTHash:
B4B9B02E6F09A9BD760F388B67351E2B

Cracking Hashes

john --format=nt hash.txt
hashcat -m 1000 -a 3 hash.txt

NTLMv1 (A.K.A. Net-NTLMv1)

  • Can use either the NT or LM hash
  • does not support Pass the Hash
  • Can be relayed to other servers if SMB Signing is disabled
    • Can also be relayed back to the same server if does not have patch (MS08-068)
      • Can relay cross-protocol if has patch

The NTLM protocol uses the NTHash in a challenge/response between a server and a client. The v1 of the protocol uses both the NT and LM hash, depending on configuration and what is available.

How the password is hashed:

#C = 8-byte server challenge, random
C = os.urandom(8)

#Generate a NT hash
nt_hash = generate_NT_Hash(password)

#Generate a LM hash with up to 14 char password
lm_hash = generate_LM_Hash(password)


#Pad the key


#Similar to the LM hash use the hash as the key for encrypting the server challenge
lm_response = DES_block(lm_hash[:7], C) + DES_block(lm_hash[:7:14], C) + DES_block(lm_hash[14:], C)
nt_response = DES_block(nt_hash[:7], C) + DES_block(nt_hash[:7:14], C) + DES_block(nt_hash[14:], C)


#K1 | K2 | K3 = LM/NT-hash | 5-bytes-0
combined_key = nt_hash + "\x00\x00\x00\x00\x00\x00"
key1, key2, key3 = combined_key

response = DES(K1,C) | DES(K2,C) | DES(K3,C)

Example:
u4-netntlm::kNS:338d08f8e26de93300000000000000000000000000000000:9526fb8c23a90751cdd619b6cea564742e1e4bf33006ba41:cb8086049ec4736c

Cracking Hashes
john --format=netntlm hash.txt
hashcat -m 5500 -a 3 hash.txt

NTLMv2 (A.K.A. Net-NTLMv2)

This is the new and improved version of the NTLM protocol, which makes it a bit harder to crack. The concept is the same as NTLMv1, only different algorithm and responses sent to the server. Also captured through Responder or similar. Default in Windows since Windows 2000.

Example:
admin::N46iSNekpT:08ca45b7d7ea58ee:88dcbe4446168966a153a0064958dac6:5c7830315c7830310000000000000b45c67103d07d7b95acd12ffa11230e0000000052920b85f78d013c31cdb3b92f5d765c783030

Algorithm

SC = 8-byte server challenge, random
CC = 8-byte client challenge, random
CC* = (X, time, CC2, domain name)
v2-Hash = HMAC-MD5(NT-Hash, user name, domain name)
LMv2 = HMAC-MD5(v2-Hash, SC, CC)
NTv2 = HMAC-MD5(v2-Hash, SC, CC*)
response = LMv2 | CC | NTv2 | CC*

Cracking Hashes

john --format=netntlmv2 hash.txt
hashcat -m 5600 -a 3 hash.txt

Different Windows Hashes

Relaying Hashes

Relaying hashes back to the same machine wont work with the MS08-068 update. It is possible to do so in a cross-protocol relay attack.
It is possible to relay the hash to another machine.

This is only possible with SMB signing turned off which is default except for Windows Server OS's.