Part One: Introduction
The VX-API is a powerful set of malicious functionality that can facilitate malware development. It provides a robust set of features that aid in everything from string manipulation, to hashing functions, and obfuscation.
In this writeup we're going to use discuss the default VX-API implant. We covered a little bit of the API in Achieving Access but here we're going to see a couple of the different techniques that the VX-API allows you to quickly implement in your tooling. Additionally, we're going to take a look at how it fairs against Windows Defender and discuss what that means for us.
Part Two: Getting Started
To begin, we're going to download the VX-API from github.
We can open up the project file, then open up Main.cpp and we should see something like this:
This is a significantly shorter main() function than what we've previously implemented in my writeups, so let's talk about what's actually going on.
Part Three: What is actually going on??
The primary difference in the template code and the way we've typically implemented our implants in the past is the use of the __SHELLCODE_EXECUTION_INFORMATION struct. If we open up Win32Helper.h, we can take a look at the definition of this struct.
We can see in the screenshot above that the VX-API consolidates the typically necessary payload arguments (payload pointer and size) and captures them into thE SHELLCODE_EXECUTION_INFORMATION struct. Additionally, there's a third field in the struct, MethodEnum, which appears to be interesting.
Looking in the Win32Helper.h, we see a comment that gives us some clarity.
Let's take a look at the values in SHELLCODE_EXECUTION_METHOD.
This is actually really cool. This typedef provides a list of supported payload execution methods that the VX-API implements for us. All you have to do to apply one of these techniques in your tooling is change the Sei.MethodEnum variable in main.cpp.
If you want to take a look at how these are implemented, you can open up .\VX-API\ShellcodeExecutionViaFunctionCallbackMain.cpp
Inside this same .cpp source file, we find that ShellcodeExecutionViaFunctionCallbackMain actually forwards our call to CreateThreadAndWaitForCompletion.
And by default, our templated main.cpp is written to use E_RTLUSERFIBERSTART, which we can see implemented here:
Part Four: So…how do I use it?
Let's go back to main.cpp and uncomment the ShellcodeExecutionViaFunctionCallback() function call, compile our program, and run it.
If everything goes well (which it should), you should see it work like this.
We can open up VX-API.exe in x64Dbg and take a look at what's going inside.
Based on what we saw in the source code, we know we can expect the following flow:
ShellcodeExecutionViaFunctionCallbackMain => CreateThreadAndWaitForCompletion => CreateThread =>RtlUserFiberStart()
At some point we'll have to resolve the parameters that were passed into CreateThread. Because the method we chose was E_RTLUSERFIBERSTART, we know we can expect calls to GetModuleHandleEx2W and GetProcAddress before out payload gets initiated.
Stepping through our program, that's exactly what we see.
CreateThreadAndWaitForCompletion => CreateThread
GetModuleHandleEx2W => GetProcAddressW => RtlUserFiberStart()
Part Five: Is it detected???
It depends. At the time of writing, Windows Defender on Windows 11 did not detect the dynamically linked Debug executable. But Defender did detect our dynamically linked Release build. This is more of an interesting fact rather than a useful bit of information. We are knowingly using a signatured MSFVenom payload so we can expect the payload to be detected against almost every scanner.
\Release\VX-API.exe detected bytes
Payload bytes detected by ThreatCheck
Static linking resulted in both executables being detected.
We can use a less signatured payload to get passed the payload detection and see if any portion of the VX-API is getting flagged.
msfvenom -p windows/x64/exec EXITFUNC=thread CMD=calc.exe -f c -a x64 -e x64/zutto_dekiru
Note: Do not forget to change the Sei.dwLengthOfPayloadInBytes variable in main.cpp
And if we run our scans again against the statically linked executables, we see the following.
What this last test tells us is that the VX-API contains strings that are signatured by Windows Defender, and these strings are retained in debug builds of our executable but not in release builds. We can validate this deduction by dumping the strings in our Debug build vs our Release build.
Obfuscating these strings is outside the scope of this article, and not necessary for our purposes because our implant does not use these strings. But know that the VX-API provides a robust string obfuscation capability and we'll investigate further future writeups.
Ultimately, this means that you can leverage a lot of the VX-API without worry that the API itself is signatured (at least as of the time of this writing).
Part Six: Conclusion
In this writeup we took a deep dive into the test implant of the VX-API and observed some of the capabilities the API can provide. We also saw that the VX-API provides a powerful set of capabilities, most of which were not discussed in detail.
We saw that the API uses signatured strings in some API functions, but we know that part of what the API offers is string obfuscation and we'll implement that in the future.
We've only just scratched the surface of what the API is capable of, but it's obvious that the API is incredibly helpful in applying many of the techniques that are most effective at achieving malicious code execution.