Skip to Content

Hunting the Undead: Accelerating NetNTLMv1 Lookups Without GPUs 

Outdated protocols, forgotten configurations, and legacy dependencies continue to create opportunities for attackers in modern environments. This research explores how NetNTLMv1 can still be leveraged today and how improvements in attack tooling are reducing the cost and complexity of exploiting it.

Nate Drier Image

This article is part of an ongoing series from our own specialists, sharing the cutting-edge offensive security research they use to strengthen our world-leading Managed Detection and Response (MDR) services and protect customers against evolving AI Era threats. 

Cyberattacks rarely succeed because of a single, clever technique used on its own. More often, they succeed because threat actors are able to uncover the assumptions, shortcuts, and outdated mechanisms that organizations have accumulated over time. These weaknesses aren't always obvious. Instead, they sit quietly in your environment for years, surviving upgrades, migrations, and code reviews.

As Red Teamers, some of the most valuable findings come from identifying a technology in use that no longer meets modern security requirements. An obsolete protocol, a forgotten configuration, a legacy dependency, or a flawed design decision can all provide attackers with the opportunity they're looking for. Understanding these risks helps organizations plan and deploy remediation where it will have the greatest impact on closing exposures and reducing the attack surface overall.

This article examines one such example, explaining how aging authentication mechanisms can create exploitable weaknesses in modern environments — so that defenders can find them first.

A protocol that refuses to die

NetNTLMv1 (officially called the NTLMv1 Challenge-Response protocol) is a legacy Microsoft authentication protocol used to verify a user's identity without sharing their actual password over the network. NetNTLMv1 refers specifically to the network payload exchanged during this login process.  

It is now considered obsolete and high risk because of two major design flaws: 

  • Weak cryptography (DES encryption): NetNTLMv1 uses 56-bit DES (Data Encryption Standard) keys to encrypt. Because 56-bit encryption is mathematically weak, an attacker who captures a NetNTLMv1 response on the network can easily decrypt the DES keys to directly extract the user's underlying NT hash, regardless of password length or complexity.
  • Predictable/pre-computed attacks: With tools like pre-computed lookup tables (rainbow tables), an attacker who controls the server challenge can look up the client's response in seconds to reverse-engineer the user's secret keys. More on this later...

NetNTLMv1 is a useful finding during penetration tests because it frequently still shows up in Active Directory environments through legacy systems, misconfigured hosts, aging appliances, and the occasional domain controller nobody wants to touch. 

Despite its age and poor security, Sophos consultants continue to encounter it during real-world assessments. Across thousands of penetration tests and red team engagements, NetNTLMv1 downgrade opportunities remain common and seeing a v1 hash float by is a common daydream of ours.

The attack path will be familiar to anyone who has spent time testing internal networks: 

  1. Coerce a target (ideally a domain controller) into authenticating back to you
  2. Force negotiation down to NetNTLMv1 by supplying a server challenge instead of allowing a stronger protocol — PetitPotam, PrinterBug, Coercer, and poisoned name-resolution all lead to the same destination
  3. With the static challenge “1122334455667788”, the response is no longer something you need to brute-force. It becomes something you can look up

This all works because of NetNTLMv1’s now obsolete 56-bit key DES (superseded by Advanced Encryption Standard (AES), which supports 128-bit, 192-bit, and 256-bit keys). The path is as follows: 

  1. A NetNTLMv1 response takes a victim's NT hash
  2. It pads (extends) the hash to 21 bytes, splitting it into three 7-byte segments
  3. It then uses each 7-byte segment as a DES key to encrypt the same 8-byte server challenge
  4. When the challenge is fixed and known, recovering the NT hash becomes three independent DES key-recovery problems against known plaintext
  5. The third segment is trivial: padding leaves only 2 bytes of real entropy, creating just 65,536 possibilities
  6. Modern CPUs recover these almost instantly, with the first two chunks each containing a full 56-bit DES keyspace

This is where rainbow tables enter the picture. 

Diagram showing an attack path using NetNTLMv1 to enable NT hash recovery from a known challenge

Rainbow tables 

A rainbow table is a large, prebuilt lookup file that can be used to reverse cryptographic password hashes. Instead of calculating every password guess in real-time, threat actors use these prebuilt databases to match stolen, unsalted hashes back to their original plaintext passwords. Modern password hashes are typically salted with a unique random value, meaning attackers can’t rely on a single precomputed table and must crack each hash individually. 

In 2026, Mandiant published a complete NetNTLMv1 DES rainbow table set for this fixed-challenge scenario: 4,096 files of roughly 2 GB each, covering the full 2^56 keyspace (about 9TB total). The chains are long, at 881,689 links. They are also dense, at about 134 million chains per table. Given a captured response, the lookup recovers the two DES keys needed to reconstruct the NT hash. 

The usual sticking point is the lookup tooling. The common options (crackalack from Rainbow Crackalack and the classic rcrack) assume the GPU is the right place to do the work. That might make sense in isolation, but I can imagine from here the team grumbling when a v1 downgrade ties up the GPU rigs for half a day.  

So, maybe there's another way...

The GPU tax

The bottleneck in GPU-based rainbow-table lookup is not always the GPU. 

Walking a rainbow chain for one endpoint takes a few hundred thousand DES operations. Across roughly 880,000 endpoints per ciphertext, which equates to 388 billion DES operations for a lookup precompute. That sounds huge, but the search phase still has to read the multi-gigabyte table set from disk. At that point, sequential I/O is a major part of the runtime. Even with hot NVMe disk, it’s still a bottleneck. The compute-heavy phase is also well within reach of modern many-core CPUs. 

Handing the whole job to a GPU ties up the most contended resource in the rig for work that is partly I/O-bound and partly CPU-friendly. Every minute spent regenerating rainbow chains is a minute those cards are not running hashcat queues, WPA handshakes, bcrypt lists, or other workloads that genuinely need the cores. 

We saw this on our own cracking fleet. A full NetNTLMv1 downgrade lookup used to take up to eight hours, with the GPUs unavailable for any other jobs for the entire run. During that window, the CPUs in the same chassis were mostly moving data and waiting. The GPUs were busy, but disk and CPU were staring off into space. 

So, can the lookup avoid the GPU entirely? 

Bitslicing DES, 256 at a time

If the goal is to free up GPUs, the obvious answer to use CPU isn't enough on its own. A straightforward scalar implementation reaches about 144 million DES/s on a 64-core EPYC. At that rate, one precompute takes about 45 minutes, with most of the time spent in the DES key schedule rather than encryption. 

The faster approach is bitslicing. Instead of treating a CPU register as one 64-bit value, bitslicing treats it as 64 one-bit lanes and compute 64 independent DES encryptions in parallel. Each DES S-box becomes a small Boolean gate network built from AND, OR, XOR, and ANDNOT operations. Matthew Kwan published compact DES S-box expressions in 1998, and that work still underpins fast bitslice DES implementations today. 

Two more tricks make this approach work: 

  • AVX2 widens the slice: Replacing a 64-bit word with a 256-bit AVX2 vector increases parallelism fourfold, allowing 256 DES operations to be processed simultaneously instead of 64.
  • The key schedule disappears: In a rainbow chain the keys are derived deterministically, so instead of recomputing the DES key schedule at every step, a precomputed map wires each subkey bit straight to its source key bit. The key schedule was 85% of the scalar cost. Bitslicing makes it vanish.

The result is about 2.1 billion DES/s on a single 64-core EPYC, roughly 15× faster than the scalar baseline. That brings a precompute down from about 45 minutes to about three, without even waking up the GPUs. 

Three phases and a freebie

This pipeline follows the standard rainbow-table workflow but splits it into three separate tools so that each phase can be tuned independently.

  1. Precompute generates the ~880,000 candidate endpoints for a target ciphertext. This is the DES-heavy phase, and it's where bitslicing pays off
  2. Search merge-scans the sorted table files for matching endpoints. The tables are sorted once, up front, so the search is a linear streaming pass.  Pure sequential I/O, the thing disks are best at
  3. Check takes the handful of candidates that matched and walks each chain with bitslice DES to recover the actual 7-byte key

There is also a freebie. The third NetNTLMv1 block, with only two bytes of entropy, is brute-forced locally (and instantly). It does not need a table or a GPU. 

Going wide

The table set is large and naturally shardable, so the process scales horizontally. We run multiple 4u boxes in our datacenter, each with a pair of AVX2-capable CPUs. We then rolled a wrapper that pushes tasks out to all the servers, each with a slice of the sorted v1 tables hosted on local NVMe disk. The more servers you have, the smaller the shard and the faster this goes. 

Decryption speeds

So, how much does this speed up the decryption process? End-to-end, on a small cluster of dual-CPU, 64-core EPYC systems, the following times can be achieved: 

Process Time achieved (m) 
CT3 brute-force 0 (effectively instant) 
Precompute 3–5 minutes (parallelized) 
Search 4–6 (sharded) 
Check ≈3 
Orchestration overhead ≈3 

The same downgrade lookup that previously occupied GPUs for up to eight hours now completes in under 20 minutes on a single server, and faster across a small cluster, without consuming a single GPU cycle. The cracking cards stay available for the rest of the queue while the CPU lookup runs alongside them.

Of course, the catch is you need the captured v1 hash with the static challenge. Sometimes you can get this by toggling the bits in Responder (--disable-ess, --lm), and sometimes you can’t. When it works, it's a thing of beauty. When it doesn't work, the GPUs are the fallback. 

Why this matters

At our size (Sophos Red Team, with lots of consultants, lots of pentests, and so many hashes), saving GPU cycles can make or break engagements. A GPU lookup may be fast in isolation, but it still monopolizes the scarcest resource in the rig for work that is partly I/O-bound and partly within CPU reach. Moving the lookup to the CPU turns a serial dependency (crack this hash, then resume GPU jobs) into two workloads running in parallel on different parts of the same system. (Also, MOAR GPUs made my boss's eyes roll.)  

What defenders can do

The lesson here extends far beyond NetNTLMv1. Threat actors thrive on the availability of legacy technology that survives long after its security integrity has expired. Obsolete protocols, unsupported operating systems, weak cryptography, forgotten configurations, and aging infrastructure often provide the easiest path into otherwise modern environments. Regular security assessments help uncover these hidden weaknesses before attackers do, allowing organizations to prioritize remediation where it will have the greatest impact to reduce risk and shrink the attack surface. The most dangerous exposures are often the ones everyone assumes disappeared years ago. 

Get the code: v1-nightshift

Want to try our process yourself? To support our NetNTLMv1 research and testing workflows, we developed v1-nightshift, a CPU-based rainbow-table lookup tool designed to offload NetNTLMv1 lookups from GPUs. 

The toolset is pure C with no dependencies beyond a C compiler and pthreads. It targets the public Mandiant table set (which you’ll need to sort; tools also included here) and builds with `make` (add `AVX2=1` for the 256-way path on x86-64). Build it on the system where it will run, because the binaries are not cross-platform. 

GitHub: v1-nightshift

Acknowledgments 

Matthew Kwan's 1998 bitslice DES S-box expressions are still doing useful work nearly thirty years later. The rainbow tables are Mandiant's public release.