I've been doing a lot of analysis on malicious docs (maldocs) lately and, among a popular variant circulating right now, is a technique that I found particularly interesting. Effectively, it abuses native Windows function calls to transfer execution to shellcode that it loads into memory. I thought it was cool in this context, and not something that I was super familiar with, even though I've since learned it's a very old technique, so I set out to do some research in identifying additional functions that could be abused in a similar way and how to leverage them.
To give you an idea of how this works, I'll go over a quick example of how shellcode can be executed through a function. For this, I'll use EnumResourceTypesA.
As stated by Microsoft, this function "enumerates resource types within a binary module" and the second argument, the interesting bit, is "a pointer to the callback function to be called for each enumerated resource type". If I supply the memory address of the shellcode to lpEnumFunc, it will pass each enumerated resource to that function but, since it's the shellcode, it just executes whatever is at the memory address I provided - keep in mind the memory page still needs to allow for code execution.
In the context of maldocs, VBA gives you the capabilities to directly call Windows functions yet, outside of VBA, these functions can also be leveraged during typical exploitation attacks if you know the target application already has the function imported. You could possibly save ROP chain space that might typically be used for certain gadgets that carry out like functionality as well, depending on the function and required arguements. In addition, from a general offsec perspective, if you continue using the same function calls for your maldocs, you leave a very clear pattern of dynamic and static artifacts that make it trivial to track and detect, so it's nice to have the option of mixing things up a bit. As the saying goes, variety is the spice of life!
To enumerate all of the possible functions, I looked at the C header files that come in the Windows 7 x86 SDK.
The meat of it is the grep for '(Func|Proc|CallBack| lpfn| lpproc)', the rest is mainly attempting to normalize the header file function structures for easier parsing since they were all over the place in terms of style.
After getting a list of candidate functions, I set out testing each one to try and figure out which would be the most likely to be used in maldocs. This equated to me reading the MSDN article to understand the purpose of the function and then a few quick lines of VBA to see if I could get it working. While most of these can most likely be massaged into executing code at your specified address, there isn't a lot of reward in chaining together multiple functions to do so with the abundance of "easy" ones. For example, the DestroyCluster function has a similar callback argument, but you'd have to also call CreateCluster and OpenCluster to setup the environment first, which is a bit much for the usecase.
The below table lists the identified functions which appeared to have the ability to accept a memory address for code execution and may be open to possible abuse.
AddClusterNode | BluetoothRegisterForAuthentication | CMTranslateRGBsExt |
CallWindowProcA | CallWindowProcW | CreateCluster |
CreateDialogIndirectParamA | CreateDialogIndirectParamW | CreateDialogParamA |
CreateDialogParamW | CreatePrintAsyncNotifyChannel | CreateTimerQueueTimer |
DavRegisterAuthCallback | DbgHelpCreateUserDump | DbgHelpCreateUserDumpW |
DdeInitializeA | DdeInitializeW | DestroyCluster |
DialogBoxIndirectParamA | DialogBoxIndirectParamW | DialogBoxParamA |
DialogBoxParamW | DirectSoundCaptureEnumerateA | DirectSoundCaptureEnumerateW |
DirectSoundEnumerateA | DirectSoundEnumerateW | DrawStateA |
DrawStateW | EnumCalendarInfoA | EnumCalendarInfoW |
EnumChildWindows | EnumDateFormatsA | EnumDateFormatsW |
EnumDesktopWindows | EnumDesktopsA | EnumDesktopsW |
EnumEnhMetaFile | EnumFontFamiliesA | EnumFontFamiliesExA |
EnumFontFamiliesExW | EnumFontFamiliesW | EnumFontsA |
EnumFontsW | EnumICMProfilesA | EnumICMProfilesW |
EnumLanguageGroupLocalesA | EnumLanguageGroupLocalesW | EnumMetaFile |
EnumObjects | EnumPropsExA | EnumPropsExW |
EnumPwrSchemes | EnumResourceLanguagesA | EnumResourceLanguagesExA |
EnumResourceLanguagesExW | EnumResourceLanguagesW | EnumResourceNamesA |
EnumResourceNamesExA | EnumResourceNamesExW | EnumResourceNamesW |
EnumResourceTypesA | EnumResourceTypesW | EnumResourceTypesExA |
EnumResourceTypesExW | EnumResourceTypesW | EnumSystemCodePagesA |
EnumSystemCodePagesW | EnumSystemLanguageGroupsA | EnumSystemLanguageGroupsW |
EnumSystemLocalesA | EnumSystemLocalesW | EnumThreadWindows |
EnumTimeFormatsA | EnumTimeFormatsW | EnumUILanguagesA |
EnumUILanguagesW | EnumWindowStationsA | EnumWindowStationsW |
EnumWindows | EnumerateLoadedModules | EnumerateLoadedModulesEx |
EnumerateLoadedModulesExW | EventRegister | GetApplicationRecoveryCallback |
GrayStringA | GrayStringW | KsCreateFilterFactory |
KsMoveIrpsOnCancelableQueue | KsStreamPointerClone | KsStreamPointerScheduleTimeout |
LineDDA | MFBeginRegisterWorkQueueWithMMCSS | MFBeginUnregisterWorkQueueWithMMCSS |
MFPCreateMediaPlayer | MQReceiveMessage | MQReceiveMessageByLookupId |
NotifyIpInterfaceChange | NotifyStableUnicastIpAddressTable | NotifyTeredoPortChange |
NotifyUnicastIpAddressChange | PerfStartProvider | PlaExtractCabinet |
ReadEncryptedFileRaw | RegisterApplicationRecoveryCallback | RegisterForPrintAsyncNotifications |
RegisterServiceCtrlHandlerExA | RegisterServiceCtrlHandlerExW | RegisterWaitForSingleObject |
RegisterWaitForSingleObjectEx | SHCreateThread | SHCreateThreadWithHandle |
SendMessageCallbackA | SendMessageCallbackW | SetTimerQueueTimer |
SetWinEventHook | SetWindowsHookExA | SetWindowsHookExW |
SetupDiRegisterDeviceInfo | SymEnumLines | SymEnumLinesW |
SymEnumProcesses | SymEnumSourceLines | SymEnumSourceLinesW |
SymEnumSymbols | SymEnumSymbolsForAddr | SymEnumSymbolsForAddrW |
SymEnumSymbolsW | SymEnumTypes | SymEnumTypesByName |
SymEnumTypesByNameW | SymEnumTypesW | SymEnumerateModules |
SymEnumerateModules64 | SymEnumerateSymbols | SymEnumerateSymbols64 |
SymEnumerateSymbolsW | SymSearch | SymSearchW |
TranslateBitmapBits | WPUQueryBlockingCallback | WdsCliTransferFile |
WdsCliTransferImage | WinBioCaptureSampleWithCallback | WinBioEnrollCaptureWithCallback |
WinBioIdentifyWithCallback | WinBioLocateSensorWithCallback | WinBioRegisterEventMonitor |
WinBioVerifyWithCallback | WlanRegisterNotification | WriteEncryptedFileRaw |
WsPullBytes | WsPushBytes | WsReadEnvelopeStart |
WsRegisterOperationForCancel | WsWriteEnvelopeStart | mciSetYieldProc |
midiInOpen | midiOutOpen | mixerOpen |
mmioInstallIOProcA | mmioInstallIOProcW | waveInOpen |
waveOutOpen |
Out of that list, I was able to get the 49 functions, highlighted in red, to execute basic Calc shellcode with little to no additional interaction. At most, I had to provide some unique data, such as a handle to a process or specific values, but for the most part they are standalone and accept a 0 or 1 as values to every other argument the function needs.
I wrapped all of this into a small little script I'm calling trigen (think 3 combo-generator) which randomly puts together a VBA macro using API calls from pools of functions for allocating memory (4 total), copying shellcode to memory (2 total), and then finally abusing the Win32 function call to get code execution (48 total - I left SetWinEventHook out due to aforementioned need to chain functions). In total, there are 384 different possible macro combinations that it can spit out.
The tool can be downloaded from here and will generate output similar to the below. It takes one argument, which is the hex-string of the shellcode, but it will do some minimal parsing of msfvenom output too (C or Py).
The logic of the code is fairly straight forward. Allocate memory, copy shellcode into memory, transfer execution to the shellcode via abused function call. The script will include the necessary code for each part.
I'm also including the VBA I worked from as I went through my testing, which has some notes and other tidbits. Feel free to experiment with that or pick up where I left off.
Cheers!