Bluffy the AV Slayer
As an experiment, we converted default Cobalt Strike shellcode into various forms to see how it would do against static detection. Turns out, quite well...
Introduction
Michael Ranaldo proposed an idea:
How will static detection fare against shellcode hidden within realistic datatypes?
Avoiding all kinds of encryption and compression, two Windows APIs came to mind: UuidFromStringA and CLSIDFromString.
This function has already seen weaponisation, namely boku7/Ninja_UUID_Runner from 0xBoku which does just that. So, we spent some time thinking of methods we could use to do this, and we came up with a tool: Bluffy.
Bluffy takes in a bin file, and has the ability to wrap that bin file up into, currently, four different masks:
SVG
CSS
UUID
CSV
These are not uniform, they will require different setup. Lets get into a rundown of the examples and their ability to handle Windows Defender.
The one missing from this list that we wanted to include is CLSID. That is because we thought we would leave that one as a task for the reader if they felted so compelled. We also had plans for Json, but never got around to it. And then the final type which we couldn't think of a solution for, but there must be!, was JavaScript.
These are not uniform, they will require different setup, but we have tried to standardise it as much as we can. So, as of now, UUID is the only one which will call VirtualAlloc
and VirtualProtect
(so read, conversion writes, and then update the page); the rest will simply return a unsigned char
. Otherwise, it's pretty straight forward to add. In future, including some more randomisation to add in some more noise would be good, but as a minimal proof of concept, it works for now.
Before going into some examples of three of these masks vs. Defender, two disclaimers:
This was only looked at from a static perspective. If Defender catches this on execution, we don't care...
In a typical Defender-fashion, every other execution passed the dynamic detection.
Lets get into a rundown of the examples and their ability to handle Windows Defender.
UUID
First up, UUID
. This is by far the easiest one to use. Here is the API declaration:
This function takes in a string and returns a pointer to the now converted UUID. What makes this so useful is the second parameter: UUID *Uuid
. This parameter will automatically write the data to memory, so all that it needs are allocation and execution. Here is the function to allocate and write:
And then to execute it:
Again, because only execution is needed at this point, anything can be used to trigger it. Take a look at S4R1N/AlternativeShellcodeExec for a comprehensive list.
How does it fair against Defender:
It exceeded its requirement, it bypassed both static detection and execution.
SVG
For the reader who hasn't used SVG to pop XSS, SVG is the Scalable Vector Graphics. It's got some fanciness for appsec nerds, but what we liked was the XML structure and the plethora of places to drop integers.
Here is an example SVG from W3Schools:
If this was to render, it would be:
Our solution here was to take all the available integers from rect and split shellcode into them. Here is a snippet:
To achieve this, regex was required. This regex was what we went for, with a substitution on double quotes and "cm". In order to even do regex, we had to go down a bit of a rabbit hole. After a lot of Googling, we eventually landed on pcre2. This was new to us and eventually hacked something together based on luvit/pcre2.
Testing via Windows Defender:
Another one down.
CSS
The next one we thought obvious was CSS, it allows for a whole bunch of different places to store CSS. The following example shows the shellcode embedded into the RGB values of a border:
Using similar methods to SVG, it can be regexed out.
Running this against Defender:
Another one worked.
Conclusion
Out of these 3 techniques, they all bypassed Defender (statically) consitently. We had mixed responses against runtime detection, but that was not the focus of what we were looking for. We found that this method, naturally, has a much lower entropy value too. In hindsight, its kind of obvious that this type of masking would cause static detection to break because its literally just integer values in various datatype formats. This idea started as a meme and ended as a bypass (kinda) without going F U L L S T E G A N O G R A P H Y.
The code is available on GitHub.
Last updated