41. Callback Code Execution
Callback Code Execution
Introduction
Callback functions are used to handle events or to perform an action when a condition is met. They are used in a variety of scenarios in the Windows operating system, including event handling, window management, and multithreading. Microsoft's definition of a callback function is as follows:
A callback function is code within a managed application that helps an unmanaged DLL function complete a task. Calls to a callback function pass indirectly from a managed application, through a DLL function, and back to the managed implementation.
Several ordinary Windows APIs possess the ability to execute payloads using callbacks. Using them provides a benefit against security solutions since these functions may appear benign and can potentially evade some security solutions.
Abusing Callback Functions
Windows callbacks can be executed using a function pointer. To run the payload, the address of the payload must be passed instead of a valid callback function pointer. Callback Execution can replace the use of the CreateThread
WinAPI and other thread-related techniques for payload execution. Additionally, there is no need to use the functions correctly by passing the appropriate parameters. The return value or functionality of these functions is not of any concern.
One important point about callback functions is that they only work in the local process address space and cannot be used to perform remote code injection techniques.
Sample Callback Functions
The following functions are all capable of execution callback functions.
CreateTimerQueueTimer's 3rd parameter
BOOL CreateTimerQueueTimer(
[out] PHANDLE phNewTimer,
[in, optional] HANDLE TimerQueue,
[in] WAITORTIMERCALLBACK Callback, // here
[in, optional] PVOID Parameter,
[in] DWORD DueTime,
[in] DWORD Period,
[in] ULONG Flags
);
EnumChildWindows's 2nd parameter
BOOL EnumChildWindows(
[in, optional] HWND hWndParent,
[in] WNDENUMPROC lpEnumFunc, // here
[in] LPARAM lParam
);
EnumUILanguagesW's 1st parameter
BOOL EnumUILanguagesW(
[in] UILANGUAGE_ENUMPROCW lpUILanguageEnumProc, // here
[in] DWORD dwFlags,
[in] LONG_PTR lParam
);
VerifierEnumerateResource's 4th parameter
ULONG VerifierEnumerateResource(
HANDLE Process,
ULONG Flags,
ULONG ResourceType,
AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback, // here
PVOID EnumerationContext
);
The following sections will provide detailed explanations for each of these functions. The payload used in the code samples is stored in the .text
section of the binary. This allows the shellcode to have the required RX
memory permissions without having to allocate executable memory using VirtualAlloc
or other memory allocation functions.
Using CreateTimerQueueTimer
CreateTimerQueueTimer
creates a new timer and adds it to the specified timer queue. The timer is specified using a callback function that is called when the timer expires. The callback function is executed by the thread that created the timer queue.
The snippet below runs the code located at Payload
as a callback function.
HANDLE hTimer = NULL;
if (!CreateTimerQueueTimer(&hTimer, NULL, (WAITORTIMERCALLBACK)Payload, NULL, NULL, NULL, NULL)){
printf("[!] CreateTimerQueueTimer Failed With Error : %d \n", GetLastError());
return -1;
}
Using EnumChildWindows
EnumChildWindows
allows a program to enumerate the child windows of a parent window. It takes a parent window handle as an input and applies a user-defined callback function to each of the child windows, one at a time. The callback function is called for each child window, and it receives the child window handle and a user-defined value as parameters.
The snippet below runs the code located at Payload
as a callback function.
if (!EnumChildWindows(NULL, (WNDENUMPROC)Payload, NULL)) {
printf("[!] EnumChildWindows Failed With Error : %d \n", GetLastError());
return -1;
}
Using EnumUILanguagesW
EnumUILanguagesW
enumerates the user interface (UI) languages that are installed on the system. It takes a callback function as a parameter and applies the callback function to each UI language, one at a time. Note that any value instead of MUI_LANGUAGE_NAME
flag still works.
The snippet below runs the code located at Payload
as a callback function.
if (!EnumUILanguagesW((UILANGUAGE_ENUMPROCW)Payload, MUI_LANGUAGE_NAME, NULL)) {
printf("[!] EnumUILanguagesW Failed With Error : %d \n", GetLastError());
return -1;
}
Using VerifierEnumerateResource
VerifierEnumerateResource
is used to enumerate the resources in a specified module. Resources are data that are stored in a module (such as an executable or a dynamic-link library) and can be accessed by the module or by other modules at runtime. Examples of resources include strings, bitmaps, and dialog box templates.
VerifierEnumerateResource
is exported from verifier.dll
, therefore the module must be dynamically loaded using the LoadLibrary
and GetProcAddress
WinAPIs to access the function.
Note that if the ResourceType
parameter is not equal to AvrfResourceHeapAllocation
then the payload will not be executed. AvrfResourceHeapAllocation
allows the function to enumerate heap allocation, including heap metadata blocks.
HMODULE hModule = NULL;
fnVerifierEnumerateResource pVerifierEnumerateResource = NULL;
hModule = LoadLibraryA("verifier.dll");
if (hModule == NULL){
printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError());
return -1;
}
pVerifierEnumerateResource = GetProcAddress(hModule, "VerifierEnumerateResource");
if (pVerifierEnumerateResource == NULL) {
printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
return -1;
}
// Must set the AvrfResourceHeapAllocation flag to run the payload
pVerifierEnumerateResource(GetCurrentProcess(), NULL, AvrfResourceHeapAllocation, (AVRF_RESOURCE_ENUMERATE_CALLBACK)Payload, NULL);
Conclusion
This module reviewed several callback functions and demonstrated their usage for payload execution. Callback functions are only beneficial when the payload is running in the memory address space of the local process.
Microsoft's documentation page can be searched to discover additional callback functions. Additionally, a GitHub repository was created that contains a list of the most common callback functions.