Parzival

Offensive Security Rants & Threat Actor Roleplay

Who ya gonna call? -m 27100!

25 August 2023

Scenario

You have been tasked with performing a network penetration test. After reading the SoW and kicking off the test, you start with everyone’s favorite tool, Responder and quickly capture several NetNTLMv2 hashes, some of which you assume are to privileged accounts. However, after a night of attempting to crack the hashes, you have exhausted your password cracking resources, shed a tear, and are about to move on, ultimately giving up on a quick and easy way to domain administrator (also for this scenario, let’s just assume you are not able to relay the hash or perform any other dark magic hacking with it).

Well, what if I told you that you might not have to ‘crack’ this hash the traditional way in order to use it? Instead, if you’re lucky you can convert the NetNTLMv2 hash you’ve captured to an NT hash and use it for pass-the-hash evilness on the network, securing your way to one of those privileged accounts you have been eyeing.

Hashcat Saves the Day (maybe)

A few years ago I discovered Hashcat mode 27100 from this tweet and haven’t seen any discussion of it since other than a brief mention from mpgn last year.

So, what does Hashcat 27100 do? The simple explanation is that it allows you to input NetNTLMv2 hashes captured from Responder and run them against a wordlist of NT hashes. Hashcat will work its magic and attempt to find a match between the NetNTLMv2 hash you’ve obtained and ultimately output the matching NT hash.

Even though Hashcat isn’t providing you with the plaintext password, the NT hash would allow you to perform pass-the-hash attacks and potentially provide you with some sweet sweet access that you would’ve missed out on before.

If this all sounds good to you and you’re wondering how to create your own NT hashes wordlist, I would recommend the following resources:

Example

Let’s take the following NetNTLMv2 hash provided on Hashcat’s example hashes page:

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

Additionally, I have created a wordlist containing several NT hashes:

f39934a2710a469b3c63ce1487794514
eae53d3a9e5c403dad6d0b05de86d7f2
fdfd06a12ae88696d4f3c92f98b61c26
58cad76c96e9f0c6a1e0b71d67f2439c
a4f49c406510bdcab6824ee7c30fd852
6097f7cefeeaae0a3fba8334dbbe1135
49d757826420543dda51ed9635ed9ab6
B4B9B02E6F09A9BD760F388B67351E2B

The following screenshot demonstrates Hashcat being ran, note that a match is found with the NT hash B4B9B02E6F09A9BD760F388B67351E2B:

Hashcat-Crack

Benchmarks

Benchmarks were conducted using a GeForce RTX 4090 on Windows 11 running the latest drivers at the time of writing this post:

Command ran: hashcat -m 5600 --benchmark

Hashcat5600

Command ran: hashcat -m 27100 --benchmark

Hashcat27100

From our benchmark tests, we can see that using mode 27100 with Hashcat was over 4x faster than using mode 5600 in recovering the hash.

Creating Your Own Wordlists

Since mode 27100 appears to be much faster than cracking a NetNTLMv2 hash to plaintext, I wrote a simple Go script which will take a plaintext wordlist and convert it to NT hashes. Please note that I have not done the math for how much time this could save you, it’s very likely it won’t save you any.

Go Script

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	"golang.org/x/crypto/md4"
)

func ntowfv1(password string) []byte {
	utf16Password := unicodeToUtf16(password)
	hash := md4.New()
	hash.Write(utf16Password)
	return hash.Sum(nil)
}

func unicodeToUtf16(s string) []byte {
	utf16 := make([]uint16, len(s))
	for i, c := range s {
		utf16[i] = uint16(c)
	}
	result := make([]byte, len(utf16)*2)
	for i, c := range utf16 {
		result[i*2] = byte(c)
		result[i*2+1] = byte(c >> 8)
	}
	return result
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: go run main.go password_file.txt")
		return
	}

	passwordFilePath := os.Args[1]
	passwordFile, err := os.Open(passwordFilePath)
	if err != nil {
		fmt.Printf("Error opening password file: %v\n", err)
		return
	}
	defer passwordFile.Close()

	scanner := bufio.NewScanner(passwordFile)
	for scanner.Scan() {
		password := strings.TrimSpace(scanner.Text())
		ntlmHash := ntowfv1(password)
		fmt.Printf("%x\n", ntlmHash)
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Error reading password file: %v\n", err)
	}
}