Solution: LsaRetrievePrivateData
Windows can store key-value pairs in a hidden way and when automatic login is enabled on a system, the password is stored in the DefaultPassword field.
To retrieve it use the following way:
LsaOpenPolicy(NULL, &attribs, POLICY_ALL_ACCESS, &lsa_handle)
opens a LSA_HANDLE
object
- You create a
LSA_UNICODE_STRING
structure and let it point to a wchar_t
data buffer containing DefaultPassword
.
- And then you call
LsaRetrievePrivateData(lsa_handle, &key, &ptr_value)
, to which the string structure is passed as key.
- The returned pointer (
ptr_value
) holds a new Unicode string data structure where the decrypted password should be contained, or not if none exists.
The call must be made under administrator privileges, otherwise access will be denied.
Origin: https://www.opengate.at/blog/2021/11/win32-stored-password/
And here is the necessary C code which worked and revealed the password in my case:
#include <string>
#include <exception>
#include <wchar.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include "windows.h"
#include "Ntsecapi.h"
typedef std::string string_t;
typedef std::wstring wstring_t;
struct LsaHandle
{
private:
LsaHandle(const LsaHandle&);
LsaHandle& operator=(const LsaHandle&);
LSA_HANDLE handle;
public:
LsaHandle(LSA_HANDLE h = LSA_HANDLE())
: handle(h)
{
}
~LsaHandle()
{
if(this->handle)
{
LsaClose(this->handle);
}
}
LSA_HANDLE& operator*()
{
return this->handle;
}
};
wstring_t getSecretPwd()
{
LsaHandle hPolicy;
LSA_OBJECT_ATTRIBUTES attribs;
ZeroMemory(&attribs, sizeof(attribs));
NTSTATUS dwStatus = LsaOpenPolicy(NULL, &attribs, POLICY_ALL_ACCESS, &*hPolicy);
DWORD dwError = LsaNtStatusToWinError(dwStatus);
if(dwError != 0)
{
DWORD dwError = ::GetLastError();
std::stringstream ss;
ss << "Failed to open storage, error-code = " << dwError;
throw std::runtime_error(ss.str());
}
WCHAR wstrKeyName[] = L"DefaultPassword";
LSA_UNICODE_STRING keyName;
keyName.Buffer = wstrKeyName;
keyName.Length = static_cast<USHORT>(wcslen(wstrKeyName) * sizeof(wstrKeyName[0]));
keyName.MaximumLength = static_cast<USHORT>((wcslen(wstrKeyName) + 1) * sizeof(wstrKeyName[0]));
PLSA_UNICODE_STRING privateData = NULL;
dwStatus = LsaRetrievePrivateData(*hPolicy, &keyName, &privateData);
if(dwStatus != 0)
{
dwError = LsaNtStatusToWinError(dwStatus);
std::stringstream ss;
ss << "Failed to retrieve data, error-code = " << dwError;
throw std::runtime_error(ss.str());
}
wstring_t secret_pwd(privateData->Buffer, privateData->Length / sizeof(privateData->Buffer[0]));
LsaFreeMemory(privateData);
return secret_pwd;
}
struct KeyHandle
{
private:
HKEY handle;
KeyHandle(const KeyHandle&);
KeyHandle& operator=(const KeyHandle&);
public:
KeyHandle(HKEY h = HKEY())
: handle(h)
{
}
~KeyHandle()
{
if(this->handle)
{
RegCloseKey(this->handle);
}
}
HKEY& operator*()
{
return this->handle;
}
};
wstring_t getRegistryPwd()
{
wchar_t strPass[2048];
DWORD dwType;
DWORD dwSize = sizeof(strPass);
KeyHandle hKey;
LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0,
KEY_QUERY_VALUE,
&*hKey);
if(ERROR_SUCCESS != result)
{
throw std::exception("Failed to open storage");
}
result = RegQueryValueExW(*hKey, L"DefaultPassword", 0, &dwType, (LPBYTE)strPass, &dwSize);
switch(result)
{
case ERROR_SUCCESS:
{
if(dwType != REG_SZ)
return wstring_t();
else
return wstring_t(strPass, dwSize / 2);
break;
}
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
{
return wstring_t();
}
default:
{
throw std::exception("Failed to retrieve data");
}
}
}
void showPassword(wstring_t(*pwdfunc)(), char const* pwdname)
{
wstring_t pwd;
try
{
pwd = pwdfunc();
if(!pwd.empty())
{
std::cout << "[" << pwdname << " = ";
std::wcout << pwd;
std::cout << "]" << std::endl;
}
else
{
std::cout << "No " << pwdname << std::endl;
}
}
catch(std::exception& ex)
{
std::cout << "[" << pwdname << "] Exception: " << ex.what() << std::endl;
}
catch(...)
{
std::cout << "[" << pwdname << "] Unknown exception" << std::endl;
}
}
int main()
{
showPassword(&getSecretPwd, "Secret Password");
showPassword(&getRegistryPwd, "Default Password");
return 0;
}