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.
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.
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.
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