Fake Dialog Boxes to Make Malware More Convincing
Let’s explore how SpiderLabs created and incorporated user prompts, specifically Windows dialog boxes into its malware loader to make it more convincing to phishing targets during a Red Team engagement.
The following GIF shows a demo of the end result, where the loader appears like an installer (XYZ is a fictitious company used as an example throughout the rest of this article):
Mariusz Banach’s (mgeeky) presentation “Desperate Infection Chains” proposes the following taxonomy to describe how an initial access payload can be crafted:
The “decoy” element serves to “continue pretext narration after detonating malware.” In Banach’s presentation, he describes how APT groups open PDF files while installing their malware to avoid raising suspicion by convincing phished users that everything was going as expected. The idea is that adding user prompts to malware loaders make them appear more like legitimate software to achieve a similar effect.
Red Team Engagement Example
SpiderLabs sent phishing emails to employees at, let’s call it, XYZ Corp., that pretended to be from the firm’s IT department. The phishing email’s pretext was the IT department had pushed updates to the corporate VPN due to a security vulnerability. However, the email says not all employees received the update requiring them to install the patch manually. The emails contained two attachments - a PDF guide and a ZIP file containing the malware.
SpiderLabs designed the PDF guide to look like a legitimate internal document from the IT department and included instructions on how to unzip and execute the “security patch installer” (i.e. the malware):
The fake installer seen above did not have any actual functionality. For instance, if the user clicked “Cancel” instead of “Install” at the prompt, the embedded C2 shellcode would still execute.
Additionally, the progress bar and the warning to refrain from disrupting the program did not affect the loader’s execution. If the malware failed to execute, it would display a popup window asking, “Do you want to submit diagnostic data to your IT administrator?” This was to convince users not to respond to the email or submit IT tickets so SpiderLabs could elude detection by the Blue Team. While installer file formats like MSI/MSIX could achieve a similar visual effect, SpiderLabs created a fake installer manually with dialog boxes because it executed its C2 malware via DLL-sideloading. This meant the logic and resource files for the fake installer had to be contained within the loader DLL.
Creating dialog boxes - Step 1: GUI
Now, let’s look at how SpiderLabs created the fake installer with dialog boxes.
We started with an empty C++ project in Visual Studio:
Added a dialog resource to the project:
The team used the resource editor to design the dialog box. We then adjusted the dimensions, added two “static text” labels and a progress bar, renamed the “Ok” button to “Install,” and placed a “Finish” button directly behind the “Cancel” button. The purpose of this is explained later in the article.
We customised the labels / appearance by right-clicking a component and viewing its “Properties” (e.g. the red boxes in the screenshot below show the options to centre the dialog box and change its font):
Preview and test the dialog box conveniently by selecting it in the resource editor, and clicking Format > Test Dialog:
Visual Studio generated two files - “resource.rc” and “resource.h”. The resource files are compiled into a binary format and linked with the final executable. The following shows the contents of resource.rc that Visual Studio generated. Note that SpiderLabs also added version information/metadata. This can help with avoiding some EDR detections (see: https://redops.at/en/blog/meterpreter-vs-modern-edrs-in-2023)
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DIALOG1 DIALOGEX 0, 0, 320, 101
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "XYZ VPN Security Patch Installer"
FONT 8, "Segoe UI", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "Finish",IDOK,254,73,50,14
PUSHBUTTON "Cancel",IDCANCEL,254,73,50,14
LTEXT "Click 'Install' to begin, or 'Cancel' to exit",IDC_STATIC,19,31,280,10
LTEXT "XYZ VPN Security Patch 4.2.0 installation",IDC_STATIC,19,16,280,10
DEFPUSHBUTTON "Install",IDOK2,196,73,50,14
CONTROL "",IDC_PROGRESS1,"msctls_progress32",PBS_SMOOTH | WS_BORDER,19,48,280,14
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 313
TOPMARGIN, 7
BOTTOMMARGIN, 94
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//
IDD_DIALOG1 AFX_DIALOG_LAYOUT
BEGIN
0
END
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,2,0,0
PRODUCTVERSION 4,2,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "XYZ"
VALUE "FileDescription", "vpn"
VALUE "FileVersion", "4.2.0.0"
VALUE "InternalName", "vpn.dll"
VALUE "LegalCopyright", "Copyright 2004-2022, XYZ."
VALUE "OriginalFilename", "vpn.dll"
VALUE "ProductName", "XYZ VPN"
VALUE "ProductVersion", "4.2.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
The following shows the contents of “resource.h”:
//
// Microsoft Visual C++ generated include file.
// Used by resource.rc
//
#define IDOK 1
#define IDCANCEL 2
#define IDD_DIALOG1 101
#define IDI_ICON1 103
#define IDB_PNG1 104
#define IDOK2 1001
#define IDC_PROGRESS1 1002
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 105
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Creating dialog boxes - Step 2: Functionality
Once satisfied with the appearance of the dialog box, we used it in our code with DialogBox():
int main() {
auto result = DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
return 0;
}
/*
src: <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxa>
void DialogBoxW(
[in, optional] hInstance,
[in] lpTemplate,
[in, optional] hWndParent,
[in, optional] lpDialogFunc
);
*/
Next, we created a callback function to respond to the user’s input (e.g. when they click a button):
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG: {
// Set the color of the progress bar to green: 0,255,0. The default is blue.
HWND hProgress = GetDlgItem(hDlg, IDC_PROGRESS1);
SendMessage(hProgress, PBM_SETBARCOLOR, 0, RGB(0, 255, 0));
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS1), SW_HIDE); // Hide progress bar. User will see it after they press the 'Install' button
ShowWindow(GetDlgItem(hDlg, IDOK), SW_HIDE); // Hide finish button
ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
ShowWindow(GetDlgItem(hDlg, IDOK2), SW_SHOW);
ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_SHOW);
}
return (INT_PTR) TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) { // LOWORD contains the resource ID, HIWORD contains the actual event
case IDOK2: // Install button
ShowWindow(GetDlgItem(hDlg, IDOK2), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDC_STATIC), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS1), SW_SHOW);
// Start fake installation process and update progress bar
for (int i = 0; i <= 100; i++) {
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETPOS, i, 0);
Sleep(40);
}
// When installation is complete, hide the progress bar and show the finish button
ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS1), SW_HIDE);
ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
break;
case IDOK: // Finish button
EndDialog(hDlg, LOWORD(wParam));
break;
case IDCANCEL: // Cancel button
EndDialog(hDlg, LOWORD(wParam));
break;
}
break;
default:
return (INT_PTR) FALSE;
}
return (INT_PTR) TRUE;
}
Here are some key points to understand the DialogProc function shown above:
- DialogProc is aent by the OS. The OS sends messages when the user performs events such as mouse clicks, keystrokes, and touch-screen gestures.
- We are interested in the messages WM_INITDIALOG and WM_COMMAND
- WM_INITDIALOG to initialise our dialog box. We initialise our progress bar to be green and show/hide components of the dialog box. For instance, the following line can be thought of as a selector to get the progress bar (IDC_PROGRESS1) and hide it (SW_HIDE): ShowWindow(GetDlgItem(hDlg, IDC_PROGRESS1), SW_HIDE);
- Instead of creating multiple dialog boxes to represent steps in a typical software installation, we created a single dialog box and displayed/hid elements selectively. Since our fake installer only had two steps, it was easier to place them within a single dialog box and display/hide their components. For instance, this is how the Dialog Box is initialised (i.e. what the user sees at the first step):
However, there is also a progress bar that is hidden at this stage. Once the user clicks “install”, the progress bar is then shown, and the “install” and “cancel” buttons are hidden.
As mentioned earlier, this selective showing and hiding of specific components is achieved with: ShowWindow(GetDlgItem(hDlg, <RESOURCE_ID>), SW_HIDE);
The RESOURCE_ID can be obtained from the resource editor (e.g. we know the progress bar has the resource ID IDC_PROGRESS1
- WM_COMMAND to process the messages that the OS sends to our callback function. The WM_COMMAND message contains the resource ID that the user interacted with. E.g., if the user clicks the “Install” button, we get the resource ID IDOK2 (which represents that button), and we respond to it with our logic in case IDOK2
- Here, we implemented the fake progress bar (i.e., the for loop).
- When the user clicks “Finish” or “Cancel”, we call EndDialog to destroy the dialog box.
- The dialog box procedure should return TRUE if it processed the message and FALSE if it did not (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-dlgproc).
Conclusion
The primary benefit of dialog boxes or other types of user prompts is that they can make the phishing pretext more convincing. Secondary benefits include evading some EDRs and sandboxes (e.g., some sandboxes may not be able to simulate user input if the loader is designed to only execute the malware after receiving such input).
Gaining initial access via phishing involves multiple steps, each with the potential to raise suspicion among users. While incorporating dialog boxes can be effective once the user has downloaded the malicious file, the preceding steps of the phishing attack must be equally convincing and stealthy. For example, this could involve crafting a believable phishing pretext, sending a well-formatted email, ensuring that the antivirus (AV)/endpoint detection and response (EDR) systems do not flag the downloaded file, and avoiding triggers for Defender Smart Screen alerts. Successful initial access requires meticulous attention to detail at each step. While this article focused on the utility of dialog boxes in the later stage, it acknowledges the critical need for a cohesive and undetectable approach throughout the entire phishing operation.
Additionally, it should be noted that creating dialog boxes in this manner will leave IOCs in the resource section of the final loader, as seen in the following screenshot. However, resources may also have the effect of lowering the risk score for the file in certain EDRs:
In summary, this article has demonstrated how SpiderLabs created a fake installer using dialog boxes in Visual Studio and provided an example scenario in which it was used during a red team engagement. The method of creating dialog boxes demonstrated in this article may not be the correct or conventional way to create dialog boxes. However, it proved effective for SpiderLabs' specific needs.
ABOUT TRUSTWAVE
Trustwave is a globally recognized cybersecurity leader that reduces cyber risk and fortifies organizations against disruptive and damaging cyber threats. Our comprehensive offensive and defensive cybersecurity portfolio detects what others cannot, responds with greater speed and effectiveness, optimizes client investment, and improves security resilience. Learn more about us.