# Whoami

## Hi, I am Yasser! :wave:

<div align="left"><img src="https://komarev.com/ghpvc/?username=yaxser" alt=""></div>

**BIO**

* ⚙️ Cybersecurity Researcher
* 💜 Purple. I enjoy purple, but deep in my heart I know that I am totally red ❤️
* 💬 Implement innovative detection logic based on user behavior
* 💅 Implement malware techniques based on threat reports
* ⚔️ Perform attack simulations
* 🍨 Enjoy trying to find evasive attacking techniques (and detecting them!)
* 💭 I like to read and meditate

**On GitHub**

<div align="left"><img src="https://img.shields.io/github/followers/yaxser?style=social" alt="GitHub followers"> <img src="https://img.shields.io/github/stars/yaxser?style=social" alt="GitHub User&#x27;s stars"></div>

<div align="left"><figure><img src="https://github-readme-stats.vercel.app/api?username=yaxser&#x26;show_icons=true&#x26;theme=radical" alt=""><figcaption></figcaption></figure></div>

**Social Media** 🤝❤️

[![Email](https://img.shields.io/badge/ProtonMail-8B89CC?style=Social\&logo=protonmail\&logoColor=white)](Mailto:y.o.alhazmi@pm.me) [![Twitter](https://img.shields.io/badge/Twitter-1DA1F2?style=Social\&logo=twitter\&logoColor=white)](https://twitter.com/yas_o_h) [![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=Social\&logo=linkedin\&logoColor=white)](https://sa.linkedin.com/in/yasser-alhazmi) [![Discord](https://img.shields.io/badge/Discord-5865F2?style=Social\&logo=discord\&logoColor=white)](https://discordapp.com/users/986505084659716157)


# Task Scheduler COM Handler

### Introduction

The internet is full of resources about abusing scheduled tasks for persistence, privilege escalation, execution, lateral movement, and defense evasion. There are many articles on how to create, modify, or abuse a scheduled task using C\C++, Powershell, C#, and the command prompt. Two recent articles that I find interesting are [Random Notes on Task Scheduler Lateral Movement](https://riccardoancarani.github.io/2021-01-25-random-notes-on-task-scheduler-lateral-movement/) by [Riccardo Ancarani](https://twitter.com/dottor_morte) and [Scheduled Tasks Tampering](https://labs.withsecure.com/publications/scheduled-task-tampering) by [WithSecure](https://twitter.com/withsecure). However, none of the articles that I found online describes the creation of a COM handler for Scheduled Tasks abuse or the benefits of using a COM handler over an executable, so I wanted to write a short blog about it.&#x20;

We will start with a quick look at scheduled tasks actions and move on to the benefits of using a COM handler (will be called a handler in this blog). Then, we will take a quick look at the actions that must be taken to correlate between a scheduled task and its handler and the process tree when using a handler. We will conclude with snippets that can be re-used to operationalize COM handlers in engagements.

### Scheduled Tasks Actions

When we create a scheduled task, we specify the trigger (e.g., startup, login, etc.) and the action. The action can be a program to execute, send an email, display a message, or run a COM handler. Some might not be familiar with the last choice because the Scheduled Tasks GUI does not support using a COM handler as an action.

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2FglqLXHKggZyAV2g5LweF%2Fimage.png?alt=media&amp;token=6488fba7-3888-4c19-be09-b46ac8829ceb" alt=""><figcaption><p>Scheduled Tasks Actions</p></figcaption></figure>

Abusing scheduled tasks handlers is not a novel technique. [Dominic Chell](https://twitter.com/domchell) shows how to leverage COM Hijacking with scheduled tasks in [this blog](https://medium.com/@dmchell/persistence-the-continued-or-prolonged-existence-of-something-89fc5e362fed). In addition, COM handlers that are vulnerable to DLL hijacking can be abused to run a malicious DLL, which is demonstrated in Ricccardo's blog mentioned above. This blog is not about abusing existing COM handlers. It's about creating a new one.

### The Benefits

The main benefit of using handlers over executables is stealth. Many might think that scheduled tasks are obsolete and not opsec safe. However, if you read the articles linked above, you might come to the conclusion that they are far from becoming obsolete. COM handlers is one of the reasons why Scheduled Tasks are not obsolete and will probably not be obsolete for some time to come.

Why are handlers tricky to detect and investigate?

1. **There are many ways to perform the attack:** you could register a class yourself, modify a registered class, or replace a file on disk. The registration or modification can take many variations:  you could import a reg file, use the registry API directly, or the long forgotten INF files. You could create a task using schtasks, Task Scheduler API or the registry API. You could have your COM action as the second one, you could create a task or modify a new one, and so on. It does not matter how mature the SOC is, it seems that there is always a combination that is not covered by existing detections.&#x20;
2. **Process Tree:** detection engineers and threat hunters usually rely on the suspicious process tree to detect scheduled tasks abuse. This usually translates to a commonly abused LOLBIN `(Rundll32.exe, powershell.exe, cmd.exe, ...)` being the child of the Task Scheduler service host. When using a handler, there will be nothing special about the process tree. We will take a look at the process tree of a handler later.
3. **Command-line args:** most EDRs and SOCs rely on command-line args for their detection. Depending on the combination of steps an operator chooses, there might be no command-line indicators at all.

The combination of multiple variations, benign process tree, and failure of conventional detections makes the abuse of handler a trusted technique against both detection and investigation. We have used handlers on multiple occasions, and even when an alert was triggered during the operation, it was still closed as an FP because analysts could not connect the dots.

### Where Is My Handler?

Even when an analyst has access to a workstation, the COM handler action is trickier than its `start a program` counterpart from a quick triage perspective. This is because the GUI does not show COM handlers, it just says `Custom Handler`.

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2Fkm60ypXDhlGPtEZBUqUW%2Fimage.png?alt=media&amp;token=0638e86b-0712-4cd9-b7c0-4c6d21f3928b" alt=""><figcaption><p>Task Scheduler GUI is not very helpful when it comes to COM handlers</p></figcaption></figure>

&#x20;The `schtasks.exe`  also does not show the COM handler to be executed.&#x20;

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2FVQXyzv449gL4M7KBXYfH%2Fimage.png?alt=media&amp;token=fd8885b6-f01d-42cd-8f76-937da09aaca6" alt=""><figcaption><p>Schtasks is not better than the GUI when it comes to COM handlers</p></figcaption></figure>

What an analyst will have to do is (this can be automated indeed)

1. Retrieve the CLSID using the XML file or some scripting\programming
2. Find that CLSID in the registry
3. Observe the default value InProcSever32 for that CLSID
4. Check the file pointed at by the InProcServer32 for malicious\suspicious indicators

### Process Tree

When `start a program` is used as the action, the new process will spawn as a child process of the Task Scheduler service host process: `svchost.exe -k netsvcs -p -s Schedule`. `Rundll32`, `Powershell`, `cmd`, and LOLBINs in general are considered suspicious child processes of the Task Scheduler svchost *(but trust me, they still show up now and then as legitimate behavior!)*&#x20;

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2Fu5lnpv27qKN6p8uuNwRp%2Fimage.png?alt=media&amp;token=3fd83fcc-262b-458b-83b8-3ba59238aa28" alt=""><figcaption><p>Executable is child of the Task Scheduler Svchost. This CMD is suspicious</p></figcaption></figure>

Depending on the maturity of the organization, they might alert on the child process being a LOLBIN, unsigned, uncommon, or in a user-writable path. However, the detection is a bit trickier when a handler is used, mainly because most detection engineers are unaware of the process tree of scheduled task handlers. The detection is also trickier because the native logs alone cannot be used for detection, you either need Sysmon or an EDR for that. The following image shows the process tree of a Scheduled Task handler.

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2F0GWqHxTYi0rQiR6WGiwG%2Fimage.png?alt=media&amp;token=d8ec6a4b-9cf2-4658-a327-3eac54ec2bb1" alt=""><figcaption></figcaption></figure>

If you are wondering where the handle is, it is the `Dllhost.exe /Processid:{6B9279D0-D220-4288-AFDF-E424F558FEF2}`. The process tree itself (services -> svchost -> dllhost) is a normal tree, the command line itself is common, and the loading of a DLL by Dllhost is an expected behavior.&#x20;

### The Implementation

Scheduled Tasks COM handlers are expected to implement the ITaskHandler interface. However, the implementation of the ITaskHandler interface is not mandatory for persistence to take place. Regardless, since I am interested in COM, I decided to implement the interface on [the GitHub repository accompanying this post](https://github.com/Yaxser/Itaskhandler).

From an implementation perspective, there is nothing special about the handler. It is just a component that implements a documented interface. What I initially found challenging was the registration of the handler.

Before we take a look at the registration of the handler, let's take a look at the steps to accomplish persistence

1. **Persistent artifact on disk:** dropping the persistent payload. The payload location *and extension* are left to the discretion of the operator.
2. **Scheduled task creation:** self-explanatory. I like to use the `on class registration or modification` as a trigger while testing.
3. **Registry setup:** registering the handler. You may decide to modify a registered class or replace an existing DLL on disk instead.

Dropping an artifact to disk is straightforward, just keep in mind that the extension does ***not*** have to be a `.DLL`. The creation of the scheduled task (or modification of an existing one) should also be straightforward, but an XML is provided below for completion. The trickiest part from operational perspective is the registration. Normally, the abuse of COM handlers only requires the creation of a CLSID and its InProcServer32 subkey with the default value of the latter being the path of our DLL. However, ITaskHandler is expected to be a LocalServer32 `(CLSCTX_LOCAL_SERVER) is specified when calling CoCreateInstance`. Therefore, an InProcServer32 alone will not cut it. What we need is a DllSurrogate, which is specified in an AppId key. The correct structure is shown below

<table><thead><tr><th width="329">Key</th><th>Name</th><th>Value</th></tr></thead><tbody><tr><td>HKCU\SOFTWARE\Classes\CLSID\&#x3C;CLSID></td><td></td><td></td></tr><tr><td>HKCU\SOFTWARE\Classes\CLSID\{GUID}</td><td>AppID</td><td>{GUID2}</td></tr><tr><td>HKCU\SOFTWARE\Classes\CLSID\{GUID}\InProcServer32</td><td>(Default)</td><td>DLL Path</td></tr><tr><td>HKCU\SOFTWARE\Classes\CLSID\{GUID}\InProcServer32</td><td>ThreadingModel</td><td>Both</td></tr><tr><td>HKCU\SOFTWARE\Classes\AppID\{GUID2}</td><td>DllSurrogate</td><td>""</td></tr></tbody></table>

Using the `reg_set` BOF from [TrustedSec's Remote-Ops repo](https://github.com/trustedsec/CS-Remote-OPs-BOF)[sitory](https://github.com/trustedsec/CS-Remote-OPs-BOF), the structure is translated to the following

```
//Create the CLSID Guid
reg_set HKCU "Software\Classes\CLSID\{ECABD3A3-725D-4334-AAFC-BB13234F1202}" "" REG_SZ ""

//Fill in the AppId
reg_set HKCU "Software\Classes\CLSID\{ECABD3A3-725D-4334-AAFC-BB13234F1202}" "AppId" REG_SZ "{AFABD3A3-784D-BE34-4F3C-BB13234F1E4A}"

// create the InprocSever32 Key
reg_set HKCU "Software\\Classes\\CLSID\\{ECABD3A3-725D-4334-AAFC-BB13234F1202}\InprocServer32" "" REG_SZ ""

// Fill in the InProcSever32 values
reg_set HKCU "Software\\Classes\\CLSID\\{ECABD3A3-725D-4334-AAFC-BB13234F1202}\InprocServer32" "" REG_SZ "C:\Users\testuser11\AppData\comhandler.doc"
reg_set HKCU "Software\\Classes\\CLSID\\{ECABD3A3-725D-4334-AAFC-BB13234F1202}\InprocServer32" "ThreadingModel" REG_SZ "Both"

//Create the AppID guid
reg_set HKCU "Software\Classes\AppID\{AFABD3A3-784D-BE34-4F3C-BB13234F1E4A}" "" REG_SZ ""

//Fill in the DllSurrogate
reg_set HKCU "Software\Classes\AppID\{AFABD3A3-784D-BE34-4F3C-BB13234F1E4A}" "DllSurrogate" REG_SZ ""
```

This is an example XML that can be used for the registration

```xml
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Author>Microsoft Corporation</Author>
    <URI>\OneDrive Standalone Update Task-S-1-5-21-1299387972-143441575-8753562129-1001</URI>
  </RegistrationInfo>
  <Triggers>
    <RegistrationTrigger id="Registration Trigger">
      <Enabled>true</Enabled>
    </RegistrationTrigger>
    <CalendarTrigger>
      <Repetition>
        <Interval>PT1H</Interval>
        <Duration>P1D</Duration>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2022-01-12T12:40:56</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>false</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <Priority>7</Priority>
    <RestartOnFailure>
      <Interval>PT1H</Interval>
      <Count>999</Count>
    </RestartOnFailure>
  </Settings>
  <Actions Context="Author">
    <ComHandler>
      <ClassId>{ECABD3A3-725D-4334-AAFC-BB13234F1202}</ClassId>
    </ComHandler>
  </Actions>
</Task>
```

We can then create a scheduled task using the `schtaskscreate` BOF from [TrustedSec's remote-ops repository](https://github.com/trustedsec/CS-Remote-OPs-BOF).&#x20;

```
schtaskscreate "\OneDrive Standalone Update Task-S-1-5-21-1299387972-143441575-8753562129-1001" USER CREATE
```

And that's it for today ;-)


# Developing Cobalt Strike BOFs with Visual Studio

{% hint style="info" %}
This blog post was written while I was working for Securify B.V. and can be found [on their blog here](https://www.securify.nl/en/blog/creating-cobalt-strike-bofs-with-visual-studio/). The post is copied here (my personal blog) for archiving purposes.&#x20;
{% endhint %}

### Introduction

Cobalt Strike beacon object files (BOFs) is a feature that was added to the beacon in order to allow rapid beacon extendibility in a more OPSEC way. The BOF file is a common object file format (COFF) object that will be loaded and executed in the same process as the beacon, and therefore eliminates the need for using OPSEC expensive techniques like fork\&run. BOFs are written in C\C++ and can be built using Visual Studio or MinGW. The official documentation on BOFs can be found [here](https://www.cobaltstrike.com/help-beacon-object-files). TrustedSec provided a well-written [introduction to BOFs for developers](https://www.trustedsec.com/blog/a-developers-introduction-to-beacon-object-files/), and also provided an [example COFF loader](https://www.trustedsec.com/blog/coffloader-building-your-own-in-memory-loader-or-how-to-run-bofs/) for those interested in learning more about how BOFs work under the hood.

Developing Windows applications in Visual Studio has its advantages, mainly the ease of building, debugging, and testing as well as the integration of testing tools like virtual leak detector, application verifier, cppcheck, and so on. However, creating BOFs with Visual Studio is unpleasant because of the syntax of dynamic function resolution and because additional steps must be taken to generate the BOF and strip its debug symbols. Additionally, sometimes BOFs fail in engagements, and it would be handy to know the cause of the failure in production.

I wanted to create a baseline template that can be reused to develop BOFs with Visual Studio without having to worry about dynamic function resolution syntax, stripping symbols, compiler configurations, C++ name mangling, or unexpected runtime errors. Thus, the requirements were

1. Default debug and release build configurations for debugging and testing for aspects like memory or handle leaks
2. Custom BOF build configuration that will generate the BOFs, strip their debug symbols, and move them to an output directory
3. Prebuilt list of function definitions to allow developers to reuse existing code without having to add the dynamic function resolution syntax
4. Built-in error function that will print, in production, which line and function caused the error
5. Ability to write simple C++ code without having to worry about name mangling

A first prototype that fulfills these requirements was created and is available on GitHub.

### USAGE INSTRUCTIONS

#### IMPORTING THE TEMPLATE TO VISUAL STUDIO

To use the template, download and copy the latest zip file from the [GitHub page](https://github.com/securifybv/Visual-Studio-BOF-template/releases) into the project templates directory under My Documents. For Visual Studio 2019, the directory would be

```
%UserProfile%\Documents\Visual Studio 2019\Templates\ProjectTemplates 
```

The template will be automatically loaded into Visual Studio the next time you run it. After a restart, you can search for “beacon” on the new project wizard to find the template.

![img](https://www.datocms-assets.com/21957/1637059476-templateslocation.png)

![img](https://www.datocms-assets.com/21957/1637059466-newprojectwizard.png)

#### DEFAULT STRUCTURE

The structure of the solution is shown in the image below. The *Header Files* filter contains the two headers needed to develop BOFs. The first header is the beacon header provided by the Cobalt Strike team. The bofdefs header is a modified version of the bofdefs header from TrustedSec. Next, the *Resources* filter contains a PowerShell script that will strip the debugging symbols. This PowerShell script is based on the work of Matthew Graeber (@mattifestation) on his ObjDump script. Finally, the *Source Files* filter contains the source code of the BOF.

![img](https://www.datocms-assets.com/21957/1637059473-projectstructure.png)

#### USING THE TEMPLATE

The template makes it as easy as copying the *main* code to the *go* function for BOFs that take no arguments. The image below shows that the code in *go* is identical to the code in *main*. The ability to reuse the code without further modification increases the efficiency of porting existing projects into BOFs and eliminates the unnatural syntax of dynamic function resolution. For BOFs that take arguments, only the argument parsing part will be different. A goal for next versions is to create generic argument parsing macros or functions to eliminate the need for any modification.

![img](https://www.datocms-assets.com/21957/1637059461-code1.png)

When the code is ready, it can be built for x64 and x86 BOFs at one go using the predefined build configuration inside the project template. In order to do so, go to Build -> Batch Build and tick the two architectures.

![img](https://www.datocms-assets.com/21957/1637059455-building.png)

In order to build for only one architecture, select the appropriate BOF build configuration from the dropdown menu and hit build, as shown in the image below.

![img](https://www.datocms-assets.com/21957/1637059457-building2.png)

Regardless of the method used to build the BOF, the result is a stripped BOF ready to be used with Cobalt Strike.

![img](https://www.datocms-assets.com/21957/1637059470-output.png)

#### BOFS & COM OBJECTS

Sometimes, especially when dealing with component object model (COM) objects, it is easier to use C++ instead of C. The template supports that as well. Simply rename the Source.c to Source.cpp and compile. You will be spared from name mangling because the template will automatically add *extern “C”* if C++ is used. However, you should not get your hopes up too high with developing C++ BOFs, as this is not yet fully supported by Cobalt Strike.

#### ERROR REPORTING

The template comes with a built-in function to print errors in a meaningful manner for operators. The purpose is to provide the operators with sufficient information to understand why the BOF failed without revisiting the source code. The error message below is a screenshot from the Cobalt Strike console and provides the following information for the operators: the error occurred

* Name function causing the error: create\_folder
* Line number: 164
* Description by developer: “failed to create directory”
* WIN32 error code: 123

![img](https://www.datocms-assets.com/21957/1637059464-errors.png)

#### MODIFYING THE TEMPLATE

Different teams have different needs, and therefore this template might not be suitable for everyone. Modifying the template is easy. Just unzip the zip file, make your modifications to the files, and zip it again!

### EXAMPLE PROJECTS

To show how this template can be used, I have included two sample BOFs. The first BOF is a port of @am0nsec AppLocker enumerator, which will dump the effective AppLocker policy as beacon output. The second example is a scheduled task persistence that relies heavily on COM.

### FEATURE WORK

This template is an initial prototype and many more features can be added to it. For example, the features on my to-do list includes:

* Adding generic argument parsing that can be used by *main* and *go* without modifications
* Internal print that takes variadic arguments

Pull requests and ideas are also most welcome!

### ACKNOWLEDGMENTS

* Big thanks to the [TrustedSec](https://www.trustedsec.com/) team and [@FreeFirex](https://twitter.com/freefirex2) for sharing the code of the situational awareness BOFs [here](https://github.com/trustedsec/CS-Situational-Awareness-BOF). This template relied heavily on their work.
* Paul ([@am0nsec](https://twitter.com/am0nsec)) for sharing his code and experience with undocumented COM objects [here](https://ntamonsec.blogspot.com/2020/08/applocker-policy-enumeration-in-c.html).
* Matthew ([@mattifestation](https://twitter.com/mattifestation)) for sharing his work on PowerShell ObjDump [here](https://www.powershellgallery.com/packages/PowerSploit/1.0.0.0/Content/PETools\Get-ObjDump.ps1).


# Detecting Cobalt Strike Fork\&Run

## Introduction

Cobalt Strike (CS) is one of the most effective post-exploitation frameworks and is popular among red teamers and adversaries alike. One of the things that stood out to me while playing with Cobalt is its fork\&run behavior. Detecting the fork\&run through the default named pipes is well-established (see example detections on [Splunk](https://research.splunk.com/endpoint/5876d429-0240-4709-8b93-ea8330b411b5/), [Red Canary](https://redcanary.com/threat-detection-report/threats/cobalt-strike/), and [SigmaHQ](https://github.com/SigmaHQ/sigma/blob/master/rules/windows/pipe_created/pipe_created_mal_cobaltstrike_re.yml)). However, not much is written on detecting the fork\&run behavior itself, hence this blog post.

Operators can change the fork\&run behavior by using the `PROCESS_INJECT_SPAWN` and `PROCESS_INJECT_EXPLICIT` hooks, but detecting the default behavior is still valuable because customizing the hooks is not something that an average adversary is expected to be able to do. In addition, using the injection hooks has its own tradeoffs (not applicable for all post-ex capabilities, does not obfuscate injected payload) and thus won't be always used. Further, while the process injection hooks provide flexibility over the fork\&run behavior, not everything is easily customizable as we shall see (spoiler: named pipes behavior).

## Cobalt Strike Post-Exploitation

Cobalt strike provides two methods to execute post-exploitation capabilities inside a remote process: fork\&run and explicit injection.

1. **Fork\&run:** this is where the `PROCESS_INJECT_SPAWN` hook is used and is the default when an operator does not provide a target process pid. The beacon will create a new sacrificial process, inject the post-ex capability into it, and start listening for output using the preconfigured named pipe. The sacrificial process and its command line arguments are configurable through the malleable profile. Further, the name of the named pipe is also configurable in the malleable profile, and in some cases, an anonymous pipe is used as [pointed out by WithSecure](https://labs.withsecure.com/publications/detecting-cobalt-strike-default-modules-via-named-pipe-analysis).
2. &#x20;**Explicit:** this is where the `PROCESS_INJECT_EXPLICIT` hook is used. CS will use it when the operator selects a process from the process viewer and, for example, click on the `screenshot` button, or when the operator provides a pid when executing the post-ex capability. The beacon will inject into an existing process and start listening for output using the preconfigured named pipe.

As an occasional CS user, I must admit that I enjoy and admire its flexibility. Looking at fork\&run as an example, everything seems configurable. I can configure the name of the process to be spawned, its command line arguments, and also the named pipe it will create. This makes detection much harder. There is nothing special about creating a process, creating a pipe, or connecting to one. For a blue team to detect this, the attacker must make a mistake like specifying an abnormal child process or not including a command line when a command line is expected. Even the behavior that is not easily customizable like the creation of a named pipe by the child and listening to it by the beacon is now controllable thanks to the process injection hooks. As an attacker, you could hook CreatePipe and CreateFile to change the behavior to, for example, writing and reading from a file or shared memory.

## The Detection

We established that an attacker can easily influence single events in the fork\&run attacks using the malleable profile. However, some characteristics of fork\&run are not easily changed, and maybe we can rely on these characteristics for detection\hunting. For example, the attacker cannot change the fact that the child process is creating the named pipe, or that the parent process is communicating with an external IP and connecting to the named pipe that the child has created. Looking at the behavior as a whole instead of trying to detect individual events creates some interesting detection opportunities. The following screenshot shows the sequence of events that gets generated when fork\&run is used

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2FIXD8ZqS7ZRenqVF3lXkE%2Fimage.png?alt=media&amp;token=c291c7ed-c9c5-4dd8-b65c-a7af6a610f96" alt=""><figcaption><p>Sequence of events of fork&#x26;run</p></figcaption></figure>

This sequence of events can be translated into the following detection

```
// P1: beacon process
// P2: sacrificial child process
// NP1: Named pipe created for inter-process communication

Sequence of events
1. Process P1 Creates Child Process P2
2. Process P1 Access Process P2
3. Process P2 Creates named pipe NP1
4. Process P1 Connects to NP1
5. Process P1 connects to an external IP address
```

Note that the time window and sequence are key factors here. The entire chain occurs in less than five seconds and always occurs in the same sequence. Using our datasets and taking the time window and sequence into consideration, this behavior was unique to CS fork\&run and did not generate any FPs. In addition, the fact that these events are captured does not mean that all of them must be used for detection to be accurate. For example, dropping step four from the detection logic and testing against a our datasets produced no FPs. This logic can, and *should*, be customized depending on the environment and available resources.

## The Limitations

If you think that this is a bullet-proof detection for CS, then I am sorry to disappoint you! This detection can be bypassed if the process injection BOFs are modified, and there are also some corner cases to consider. After all, CS is meant to be flexible and its maintainers are doing a great job to ensure it is!

Four challenges that should be mentioned when discussing the application of this detection: pivoting, sysmon configuration, anonymous pipes, and resource consumption. The first three challenges are related to potential blind spots, and the last challenge is more about your SIEM engineers being unhappy because you threw this detection on the SIEM without proper testing. Let's discuss them one by one.

### Pivoting

The detection assumes that the beacon process will communicate with an external address. This detection cannot detect fork\&run in the later stages of the attack where SMB beacons are used for pivoting since no external connections are made by the SMB beacon. This limitation can be addressed by changing from *external* to *any* communication in the logic.

### Sysmon Configuration

If you rely on sysmon while researching or if your organization is using sysmon in production, your sysmon configuration might affect the results. For example, using the default [sysmon-modular](https://github.com/olafhartong/sysmon-modular) configuration and detection logic based on the sequence shown above will not detect the [jquery 4.7 profile](https://github.com/threatexpress/malleable-c2/blob/master/jquery-c2.4.7.profile). This is because the `jquery` profile is leveraging `Dllhost.exe` as a `spawnto` option, and `Dllhost.exe` is excluded from the process creation event of the default sysmon-modular configuration. This means that the first event in the sequence will not be captured. In addition, the fourth event in the sequence will also not be captured because the named pipe in the malleable profile is not included in the sysmon-modular configuration. The following screenshot shows the captured sysmon events. As you can see, event IDs 1 and 18 are missing due to the combination of sysmon-modular configuration and the malleable profile.&#x20;

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2F3zRPDJAsRJo0jEn6SKIi%2Fimage.png?alt=media&amp;token=4481e853-4ff7-44c1-b995-576b15effdc3" alt=""><figcaption><p>Default sysmon-modular and jquery profile generate less events</p></figcaption></figure>

Validating that your current sysmon config or telemetry provider can capture events from different malleable profiles is worthwhile. Therefore, I am working on a tool that will simulate CS behavior in the event log and hopefully it can be released soon.&#x20;

### Anonymous Pipes

As [noted by WithSecure](https://labs.withsecure.com/publications/detecting-cobalt-strike-default-modules-via-named-pipe-analysis), not all post-exploitation jobs create a named pipe. Some jobs like execute-assembly will use anonymous pipes and that will change the sequence of events to the following

<figure><img src="https://1576196674-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvVXyZ2EnHKRoVFQmypIH%2Fuploads%2FaL2bUq2pwSwR0U8ST2Xf%2Fimage.png?alt=media&amp;token=f793ec8b-0d33-4262-be28-50cc27d57aa9" alt=""><figcaption><p>Anonymous Pipes Events</p></figcaption></figure>

```
// P1: beacon process
// P2: sacrificial child process
// NP1: Named pipe created for communication

1. Process P1 Creates Anonymous Pipe AP1
2. Process P1 Connects to AP1
3. Process P1 Creates Child Process P2
4. Process P1 Access Child Process P2
5. Process P1 makes external network connection
```

Something that stood out to me in this sequence is event ID 18 (Pipe Connected). This event triggered when the parent (P1) connected to the pipe but did not trigger when the child connected. I looked it up and, [according to the documentation](https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon#event-id-18-pipeevent-pipe-connected), event ID 18 should have never triggered because it triggers only on *named* pipe connections, not anonymous pipes. If you know why the event was generated on the parent connection but not the child, please let me know ;-)

### Resource Consumption

Temporal near real-time detection rules might cause high SIEM resource consumption. Depending on how your SIEM work, you might want to limit this detection to threat-hunting exercises or properly configure the time window.&#x20;

And that's it for today ;-)


