Last updated
Last updated
Even though dropping files to disk has a bad reputation, there are instances where it's fairly unavoidable if we want to use certain tactics. For instance, we can show that we have access to the File Server, but we can't PsExec to it because the default service binary payload is detected by Defender.
If we copy the payload to our local desktop and check the associated log, we can see that the "file" was detected.
These Cobalt Strike "artifacts" are nothing more than shellcode runners that inject Beacon shellcode when executed. As a rule of thumb, they inject the shellcode into themselves (e.g. using the VirtualAlloc & CreateThread pattern). The service binary is the one exception, as it spawns a new process and injects the shellcode into that instead. This is done so that when moving laterally with PsExec, the artifact can be deleted from disk immediately.
The Artifact Kit contains the source code for these artifacts and is designed to facilitate the development of sandbox-safe injectors. The idea is to develop artifacts that inject Beacon shellcode in a way that cannot be emulated by AV engines. There are several bypass techniques provided with the kit which you can modify, or you can implement entirely new ones yourself. Where the Artifact Kit does not help is with making Beacon resilient to detection once it's running in memory (e.g. from memory scanners).
The kit can be found in C:\Tools\cobaltstrike\arsenal-kit\kits\artifact
.
The code for the entry point of each artifact format (i.e. EXE and DLL) can be found in src-main
. These include dllmain.c
for the DLL artifacts, main.c
for the EXE artifacts, and svcmain.c
for the Service EXE artifacts. These just call a function called start
, so you should not need to modify these files in most cases. The implementation of this function can be found in each bypass file.
These can be found in src-common
and are named bypass-<technique>.c
. The included ones are:
mailslot - reads the shellcode over a mailslot.
peek - uses a combination of Sleep, PeekMessage and GetTickCount.
pipe - reads the shellcode over a named pipe.
readfile - artifact reads itself from disk and seeks to find the embedded shellcode.
Before making any modifications to the kit, let's just build one of these variants as it is. The kit includes a build script which uses mingw to compile the artifacts. Running it without any arguments will show the usage.
It looks a bit scary at first, but each option is explained in the help. You can also review the README.md file inside the Artifact Kit directory for more information. Let's build a new set of artifact templates using the bypass-pipe technique.
Each artifact flavour will be compiled to /mnt/c/Tools/cobaltstrike/artifacts/pipe/
in my case, along with an aggressor script, artifact.cna
.
The naming convention of these files tell you what they are used for.
'32/64' denotes 32 and 64bit architectures.
'big' denotes that it's stageless.
'svc' denotes that it's a service executable.
Ensure real-time protection is disabled in Defender before running ThreatCheck against binary artifacts.
Double-click on the imported file to open it in the CodeBrowser. When prompted, select Yes to analyze the binary (the default selected analyzers are fine). This may take a minute or so to complete - you will see a progress bar in the bottom-right of the window.
The next task is to find the portion of code reported by ThreatCheck, for which there are two easy ways. The first is to search for a specific byte sequence output by ThreatCheck, for example C1 83 E1 07 8A 0C 0A 41 30 0C 01 48 FF C0 EB E9
. Go to Search > Memory, paste the string into the search box and click Search All.
Here we have one result.
Clicking on it will take you to the location in the code browser.
The other method is to use the "bad bytes offset" as given by ThreatCheck. Select Navigation > Go To and enter file(n)
where n
is the offset. In this case it would be file(0xBEC)
.
Unfortunately, we don't have debug symbols for the compiled payloads so function and variables names will be quite generic, like FUN_xxx
and lVarx
. However, we can still quite easily see that the portion of highlighted code is a for
loop. We can go back to the Artifact Kit source code and search for any such loops.
We can dismiss most of these files because we didn't use the readfile bypass nor did we enable syscalls. Therefore, the candidates in patch.c
seem the most promising. Because this is a service binary payload, we know that it will perform a "migration" (i.e. it spawns a new process and injects Beacon shellcode into it before exiting). This spawn
function under an #ifdef _MIGRATE_
directive is a dead ringer for the decompiled version in Ghidra.
To break the detection, we just have to modify the routine so that it compiles to a different byte sequence. For example:
Rebuild the kit and scan the new version of the artifact. This time we have a different signature - this is an iterative process, so we must repeat these steps until all the detections have been removed.
This one seems related to the sprintf call used to create the pseudo-random pipe name in bypass-pipe.c.
We can get around this one from changing this:
To something like this:
In most cases, it doesn't really matter what you change things to, as long as it's different (and still functional). With that change, we finally have a clean artifact.
Note that these specific examples may differ as signatures and the Beacon payload changes over time, but this methodology should always get you through. Each artifact type will also likely have different signatures used to detect them that you'll need to work through.
To tell Cobalt Strike to use these new artifacts, we must load the aggressor script. Go to Cobalt Strike > Script Manager > Load and select the artifact.cna
file in your output directory. Any DLL and EXE payloads that you generate from hereon will use those new artifacts, so use Payloads > Windows Stageless Generate All Payloads to replace all of your payloads in C:\Payloads
.
It's strongly advised to delete the existing payloads first because they sometimes only get partially overwritten with the new ones.
We should now be able to move laterally to the file server using PsExec.
Simply unload the CNA from the Script Manager if you want to revert back to the default payloads.
Before loading these into Cobalt Strike, it's useful to analyse them with a tool like . This will split the file into little chunks and scan them with Defender to reveal any parts that trip static signatures. Note that ThreatCheck cannot emulate the AV sandbox, so this is for static signatures only.
Here, we can see that the stageless service binary artifact has something that Defender doesn't like. However, there's not much context around what this is or where it is in the binary. Reversing tools such as and can help here because it allows us to dissect the file. Launch Ghidra by running the start script at C:\Tools\ghidra-10.3.1\ghidraRun.bat
. Create a new non-shared project from File > New Project, then import your artifact by going to File > Import File.