Update: 11/18/2024
Todays work will be to revisit something I like to hit on every once in a while. Please note: the author Steven Covey covers the phrase “Sharpen the Saw”. Well going back and revisiting this subject shows you why re-visiting and keeping current during changing times is important.
I will lead off to say that it is tempting to just google python and find zinger’s like:
reference: https://trustedsec.com/blog/generate-an-ntlm-hash-in-3-lines-of-python
Python. import hashlib,binascii
hash = hashlib.new('md4', "thisismyhashvalue".encode('utf-16le')).digest()
print binascii.hexlify(hash)
From the URL we are to infer that we have an easy solution with only three lines of code. From this we are going to learn a few important lessons. One lesson being you just know something isn’t right when you see the print function used without parenthesis. These are the signs. (Bill Engvall is a comedian and so you know it might just do your heart good to learn more about signs). Now having said that, you might think the lesson ends there…. but no. This is a gift that keeps on giving. In today’s world, using modern code this will fail. You will note that these 3 lines of code are predicated on using the “hashlib”. And this would be great if you roll back time … but hashlib as it turns out this uses openssl. And so when Openssl dropped md4 because it was finally decided that md4 is insecure then it led to that being dropped which in the end broke the hashlib of using the “md4” digest. Opps. in short md4 as a digest is no longer included and so you can scrap using this solution. Well there went a few minutes of time you will never get back.
Let’s take another approach at this. Let’s see if there are some a number of websites that might be online that can do the work for us. And indeed there are.
References:
https://crackstation.net/
https://www.lambdatest.com/free-online-tools/ntlm-hash-generator
https://tobtu.com/lmntlm.php
https://github.com/NickRHR/hashfunctions/blob/2b64eb3fcdc0c6a68ce2f54f34f4721201c5ad8e/MD4/MD4_nfc.py
https://codebeautify.org/ntlm-hash-generator#google_vignette
For these websites we can take a pretty popular password, say “l3tmein” and encode it. You will get exactly: 88AB0CF559CB462BC44F9EA6A4794805
Ok. That is something positive We have redeemed a little of the lost time and creating a tangible metric that we can use in the future.
Now you might be tempted to use look at MD4. You might find a url like: https://gist.github.com/kangtastic/c3349fc4f9d659ee362b12d7d8c639b6
You might note the following:
References:
https://www.ietf.org/rfc/rfc1320.txt
https://en.wikipedia.org/wiki/MD4
[ ISBN 9780849385230: “Handbook of Applied Cryptography” by Alfred J. Menezes, Paul C. van Oorschot, and Scott A. Vanstone]
This is were I skip a head a bit and just ask ChatGPT. I am going to further skip ahead a bit and just remind us that there is a fundamental difference between NTLM and MD4. I would invite you to read up on UTF-16
References:
https://en.wikipedia.org/wiki/UTF-16
Basically, NTLM is md4 applied to the UTF-16 encoding of the password. If you take the MD4 of ‘l3tmein’ you will get: b7ddb280ec4f489e128a9a16af6217ad. i think we agree the two results do not match.
References:
https://gchq.github.io/CyberChef/#recipe=MD4()&input=bDN0bWVpbg
https://emn178.github.io/online-tools/md4.html
https://en.toolpage.org/tool/md4
Jumping ahead just a bit we can ask ChatGPT how to solve this and it can produce the following python code
def _left_rotate(value, shift):
"""Left rotate a 32-bit integer value by a specified shift amount."""
return ((value << shift) & 0xFFFFFFFF) | (value >> (32 - shift))
def _F(x, y, z):
return (x & y) | (~x & z)
def _G(x, y, z):
return (x & y) | (x & z) | (y & z)
def _H(x, y, z):
return x ^ y ^ z
def _process_chunk(chunk, h):
"""Process a single 512-bit chunk of data."""
X = [int.from_bytes(chunk[i:i + 4], byteorder='little') for i in range(0, 64, 4)]
A, B, C, D = h
# Round 1
S = [3, 7, 11, 19]
for i in range(16):
k = i
A = _left_rotate((A + _F(B, C, D) + X[k]) & 0xFFFFFFFF, S[i % 4])
A, B, C, D = D, A, B, C
# Round 2
S = [3, 5, 9, 13]
for i in range(16):
k = (i % 4) * 4 + (i // 4)
A = _left_rotate((A + _G(B, C, D) + X[k] + 0x5A827999) & 0xFFFFFFFF, S[i % 4])
A, B, C, D = D, A, B, C
# Round 3
S = [3, 9, 11, 15]
for i in range(16):
k = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15][i]
A = _left_rotate((A + _H(B, C, D) + X[k] + 0x6ED9EBA1) & 0xFFFFFFFF, S[i % 4])
A, B, C, D = D, A, B, C
h[0] = (h[0] + A) & 0xFFFFFFFF
h[1] = (h[1] + B) & 0xFFFFFFFF
h[2] = (h[2] + C) & 0xFFFFFFFF
h[3] = (h[3] + D) & 0xFFFFFFFF
def md4(data):
"""Compute the MD4 hash of the given data."""
h = [
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476
]
original_byte_len = len(data)
original_bit_len = original_byte_len * 8
data += b'\x80'
data += b'\x00' * ((56 - (original_byte_len + 1) % 64) % 64)
data += original_bit_len.to_bytes(8, byteorder='little')
for i in range(0, len(data), 64):
_process_chunk(data[i:i + 64], h)
return b''.join(word.to_bytes(4, byteorder='little') for word in h)
def ntlm_hash(password):
"""Compute the NTLM hash of a given password."""
# Convert password to UTF-16LE
password_utf16le = password.encode('utf-16le')
# Compute the MD4 hash of the encoded password
return md4(password_utf16le)
if __name__ == "__main__":
# Example usage
password = "password"
ntlm = ntlm_hash(password)
print("NTLM Hash:", ntlm.hex())
And you will see we have just tied the two pieces together neatly. NTLM is truly a implementation of md4 using the UTF-16 encoding of the password.
We can change the “password” to “l3tmein” and check.
root@nodex:~/john/x# python test.py
NTLM Hash: 88ab0cf559cb462bc44f9ea6a4794805
Life can be sweet.