Skip to main content

Create a custom rule

Eolh supports Go and Rego for writing detection rules.

Write a detection rule in Go

Let's write a rule to detect by process name.

Put mimikatz.go in the eolh/pkg/signatures/ directory.

mimikatz.go
package signatures

import (
"eolh/pkg/detect"
"eolh/pkg/protocol"
"eolh/pkg/trace"
"fmt"
"strings"
)

type Mimikatz struct {
cb detect.SignatureHandler
}

func (sig *Mimikatz) GetMetadata() (detect.SignatureMetadata, error) {
return detect.SignatureMetadata{
ID: "CUSTOM-1",
Version: "1",
Name: "Mimikatz",
EventName: "mimikatz",
Description: "Mimikatz is found.",
Properties: map[string]interface{}{
"Severity": 4,
},
}, nil
}

func (sig *Mimikatz) GetSelectedEvents() ([]detect.SignatureEventSelector, error) {
return []detect.SignatureEventSelector{{
Source: "eolh", Name: "*", Origin: "*",
}}, nil
}

func (sig *Mimikatz) Init(ctx detect.SignatureContext) error {
sig.cb = ctx.Callback
return nil
}

func (sig *Mimikatz) OnEvent(event protocol.Event) error {
e, ok := event.Payload.(trace.Event)
if !ok {
return fmt.Errorf("failed to cast event's payload")
}
name := e.ProcessName
if !strings.Contains(name, "mimikatz.exe") {
return nil
}
metadata, err := sig.GetMetadata()
if err != nil {
return err
}
message := fmt.Sprintf("mimikatz.exe found")
sig.cb(detect.Finding{
SigMetadata: metadata,
Event: event,
Data: nil,
Msg: message,
})
return nil
}

func (sig *Mimikatz) OnSignal(signal detect.Signal) error {
return nil
}

func (sig *Mimikatz) Close() {}

Then register the detection rule by modifying eolh/pkg/signatures/signature.go as follows.

signature.go
func findGoSigs() []detect.Signature {
var sigs []detect.Signature
sigs = append(sigs, &Drop{})
sigs = append(sigs, &PidSpoofing{})
sigs = append(sigs, &CryptoMiner{})
// Add your signatures below
// sigs = append(sigs, &YOUR_SIGNATURE{})
+ sigs = append(sigs, &Mimikatz{})
return sigs
}

Then build Eolh.

env GOOS=windows GOARCH=amd64 go build -o eolh.exe cmd/main.go

And build the container image and push the image to your repository.

Now Eolh can detect mimikatz.exe. Note of course that detection by process name can quite easily be bypassed.

eolh/trace.Event type has the following members for detection:

type Event struct {
ProcessID int `json:"processId"` // Process ID
ThreadID int `json:"threadId"` // Thread ID (if present)
ParentProcessID int `json:"parentProcessId"` // Parent Process ID
ProcessName string `json:"processName"` // Process Name
Cmdline string `json:"cmdLine"` // CMD Line Argument
RawEvent etw.Event `json:"raw,omitempty"` // Raw Event Data of the source ETW Event
}

So you can create your own detection rule using this information.

Write a detection rule in Rego

When you make custom detection rules in Go, you need to modify Eolh code and to rebuild Eolh binary. To avoid that, you can write the rules in Rego.

If you have a list of C2 server addresses, you can detect communications to them.

c2.rego
package eolh.CUSTOM_2

__rego_metadoc__ := {
"id": "CUSTOM-2",
"version": "0.1.0",
"name": "C2 Connection",
"eventName": "c2_connection",
"description": "Connection to C2 is Detected.",
"tags": ["windows", "container", "network"],
"properties": {
"Severity": 4,
"MITRE ATT&CK": "Application Layer Protocol: Web Protocols",
},
}

eolh_selected_events[eventSelector] {
eventSelector := {
"source": "eolh",
"name": "*",
}
}

eolh_match {
arr := ["x.x.x.x"]
arr[x] == input.raw.EventData.dport
}

Remember that Rego rules may perform less well than Go rules.

To activate the Rego rule, put it in the signatures/rego directory.

# some-directory/
#   ├ eolh.exe
#  └ signatures/rego/c2.rego