Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ goto :EOF
:mc
:: Generate eventlog message compiler input file
go generate github.com/rabbitstack/fibratus/pkg/outputs/eventlog
windmc -r pkg/outputs/eventlog/mc pkg/outputs/eventlog/mc/fibratus.mc
windmc -c -r pkg/outputs/eventlog/mc pkg/outputs/eventlog/mc/fibratus.mc
windres -O coff -r -fo pkg/outputs/eventlog/mc/fibratus.res pkg/outputs/eventlog/mc/fibratus.rc
:: Link the resulting resource object
gcc pkg/outputs/eventlog/mc/fibratus.res -o pkg/outputs/eventlog/mc/fibratus.dll -s -shared "-Wl,--subsystem,windows"
Expand Down
13 changes: 9 additions & 4 deletions pkg/alertsender/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,19 @@ type Alert struct {
func (a Alert) String(verbose bool) string {
if verbose {
var b strings.Builder
if len(a.Events) > 1 {
b.WriteString("System events involved in this alert:\n\n")
} else {
b.WriteString("System event involved in this alert:\n\n")
}
for n, evt := range a.Events {
b.WriteString(fmt.Sprintf("Event #%d:\n", n+1))
b.WriteString(evt.String())
b.WriteString(fmt.Sprintf("\tEvent #%d:\n", n+1))
b.WriteString(strings.TrimSuffix(evt.StringShort(), "\t"))
}
if a.Text == "" {
return fmt.Sprintf("%s\n\n%s", a.Title, b.String())
return fmt.Sprintf("%s\n\nSeverity: %s\n\n%s", a.Title, a.Severity, b.String())
}
return fmt.Sprintf("%s\n\n%s\n\n%s", a.Title, a.Text, b.String())
return fmt.Sprintf("%s\n\n%s\n\nSeverity: %s\n\n%s", a.Title, a.Text, a.Severity, b.String())
}

if a.Text == "" {
Expand Down
4 changes: 2 additions & 2 deletions pkg/alertsender/alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestAlertString(t *testing.T) {
},
}}),
true,
"Credential discovery via VaultCmd.exe\n\nSuspicious vault enumeration via VaultCmd tool\n\nEvent #1:\n\n\t\tSeq: 0\n\t\tPid: 1023\n\t\tTid: 0\n\t\tType: CreateProcess\n\t\tCPU: 0\n\t\tName: CreateProcess\n\t\tCategory: process\n\t\tDescription: \n\t\tHost: ,\n\t\tTimestamp: 0001-01-01 00:00:00 +0000 UTC,\n\t\tKparams: cmdline➜ C:\\Windows\\system32\\svchost-fake.exe -k RPCSS, name➜ svchost-fake.exe,\n\t\tMetadata: ,\n\t \n\t\tPid: 0\n\t\tPpid: 345\n\t\tName: svchost.exe\n\t\tCmdline: C:\\Windows\\System32\\svchost.exe\n\t\tExe: \n\t\tCwd: \n\t\tSID: S-1-5-18\n\t\tUsername: SYSTEM\n\t\tDomain: NT AUTHORITY\n\t\tArgs: []\n\t\tSession ID: 0\n\t\tEnvs: map[]\n\t\t\n\t",
"Credential discovery via VaultCmd.exe\n\nSuspicious vault enumeration via VaultCmd tool\n\nSeverity: low\n\nSystem event involved in this alert:\n\n\tEvent #1:\n\n\t\tSeq: 0\n\t\tPid: 1023\n\t\tTid: 0\n\t\tName: CreateProcess\n\t\tCategory: process\n\t\tHost: \n\t\tTimestamp: 0001-01-01 00:00:00 +0000 UTC\n\t\tParameters: cmdline➜ C:\\Windows\\system32\\svchost-fake.exe -k RPCSS, name➜ svchost-fake.exe\n \n\t\tPid: 0\n\t\tPpid: 345\n\t\tName: svchost.exe\n\t\tCmdline: C:\\Windows\\System32\\svchost.exe\n\t\tExe: \n\t\tCwd: \n\t\tSID: S-1-5-18\n\t\tUsername: SYSTEM\n\t\tDomain: NT AUTHORITY\n\t\tArgs: []\n\t\tSession ID: 0\n\t\tAncestors: \n\t\n",
},
{
NewAlertWithEvents("Credential discovery via VaultCmd.exe", "", nil, Normal, []*kevent.Kevent{{
Expand All @@ -83,7 +83,7 @@ func TestAlertString(t *testing.T) {
},
}}),
true,
"Credential discovery via VaultCmd.exe\n\nEvent #1:\n\n\t\tSeq: 0\n\t\tPid: 1023\n\t\tTid: 0\n\t\tType: CreateProcess\n\t\tCPU: 0\n\t\tName: CreateProcess\n\t\tCategory: process\n\t\tDescription: \n\t\tHost: ,\n\t\tTimestamp: 0001-01-01 00:00:00 +0000 UTC,\n\t\tKparams: cmdline➜ C:\\Windows\\system32\\svchost-fake.exe -k RPCSS, name➜ svchost-fake.exe,\n\t\tMetadata: ,\n\t \n\t\tPid: 0\n\t\tPpid: 345\n\t\tName: svchost.exe\n\t\tCmdline: C:\\Windows\\System32\\svchost.exe\n\t\tExe: \n\t\tCwd: \n\t\tSID: S-1-5-18\n\t\tUsername: SYSTEM\n\t\tDomain: NT AUTHORITY\n\t\tArgs: []\n\t\tSession ID: 0\n\t\tEnvs: map[]\n\t\t\n\t",
"Credential discovery via VaultCmd.exe\n\nSeverity: low\n\nSystem event involved in this alert:\n\n\tEvent #1:\n\n\t\tSeq: 0\n\t\tPid: 1023\n\t\tTid: 0\n\t\tName: CreateProcess\n\t\tCategory: process\n\t\tHost: \n\t\tTimestamp: 0001-01-01 00:00:00 +0000 UTC\n\t\tParameters: cmdline➜ C:\\Windows\\system32\\svchost-fake.exe -k RPCSS, name➜ svchost-fake.exe\n \n\t\tPid: 0\n\t\tPpid: 345\n\t\tName: svchost.exe\n\t\tCmdline: C:\\Windows\\System32\\svchost.exe\n\t\tExe: \n\t\tCwd: \n\t\tSID: S-1-5-18\n\t\tUsername: SYSTEM\n\t\tDomain: NT AUTHORITY\n\t\tArgs: []\n\t\tSession ID: 0\n\t\tAncestors: \n\t\n",
},
}

Expand Down
41 changes: 18 additions & 23 deletions pkg/alertsender/eventlog/eventlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@
package eventlog

import (
"errors"
"fmt"
"github.com/rabbitstack/fibratus/pkg/alertsender"
evlog "github.com/rabbitstack/fibratus/pkg/util/eventlog"
"golang.org/x/sys/windows"
"hash/crc32"
"strings"
)

// source represents the event source that generates the alerts
const source = "Fibratus"

const minIDChars = 12

type eventlog struct {
Expand All @@ -45,11 +44,18 @@ func makeSender(config alertsender.Config) (alertsender.Sender, error) {
if !ok {
return nil, alertsender.ErrInvalidConfig(alertsender.Eventlog)
}
sourceName, err := windows.UTF16PtrFromString(source)
sourceName, err := windows.UTF16PtrFromString(evlog.Source)
if err != nil {
return nil, fmt.Errorf("could not convert source name: %v", err)
}

err = evlog.Install(evlog.Levels)
if err != nil {
if !errors.Is(err, evlog.ErrKeyExists) {
return nil, err
}
}

h, err := windows.RegisterEventSource(nil, sourceName)
if err != nil {
return nil, fmt.Errorf("could not register event source: %v", err)
Expand All @@ -59,24 +65,11 @@ func makeSender(config alertsender.Config) (alertsender.Sender, error) {

// Send logs the alert to the eventlog.
func (s *eventlog) Send(alert alertsender.Alert) error {
var etype uint16
switch alert.Severity {
case alertsender.Normal:
etype = windows.EVENTLOG_INFORMATION_TYPE
case alertsender.Medium:
etype = windows.EVENTLOG_WARNING_TYPE
case alertsender.High, alertsender.Critical:
etype = windows.EVENTLOG_ERROR_TYPE
default:
etype = windows.EVENTLOG_INFORMATION_TYPE
}

var eventID uint32

var code uint16
// despite the event id is 4-byte long
// we can only use 2 bytes to store the
// event identifier. Calculate the hash
// of the event id from alert identifier
// event code. Calculate the hash
// of the event code from alert identifier
// but keeping in mind collisions are
// possible since we're mapping a larger
// space to a smaller one
Expand All @@ -87,8 +80,7 @@ func (s *eventlog) Send(alert alertsender.Alert) error {
id := strings.Replace(alert.ID, "-", "", -1)
h := crc32.ChecksumIEEE([]byte(id[:minIDChars]))
// take the lower 16 bits of the CRC32 hash
eid := uint16(h & 0xFFFF)
eventID = uint32(eid)
code = uint16(h & 0xFFFF)
}

msg := alert.String(s.config.Verbose)
Expand All @@ -101,7 +93,10 @@ func (s *eventlog) Send(alert alertsender.Alert) error {
return fmt.Errorf("could not convert eventlog message to UTF16: %v: %s", err, msg)
}

return windows.ReportEvent(s.log, etype, 0, eventID, uintptr(0), 1, 0, &m, nil)
return windows.ReportEvent(s.log, windows.EVENTLOG_INFORMATION_TYPE, 0,
evlog.EventID(windows.EVENTLOG_INFORMATION_TYPE, code),
uintptr(0),
1, 0, &m, nil)
}

// Shutdown deregisters the event source.
Expand Down
63 changes: 56 additions & 7 deletions pkg/kevent/kevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ func (e *Kevent) String() string {
Name: %s
Category: %s
Description: %s
Host: %s,
Timestamp: %s,
Kparams: %s,
Metadata: %s,
Host: %s
Timestamp: %s
Kparams: %s
Metadata: %s
%s
`,
e.Seq,
Expand All @@ -150,9 +150,9 @@ func (e *Kevent) String() string {
Name: %s
Category: %s
Description: %s
Host: %s,
Timestamp: %s,
Kparams: %s,
Host: %s
Timestamp: %s
Kparams: %s
Metadata: %s
`,
e.Seq,
Expand All @@ -170,6 +170,55 @@ func (e *Kevent) String() string {
)
}

// StringShort returns event's string representation
// by removing some irrelevant event/process fields.
func (e *Kevent) StringShort() string {
e.mmux.RLock()
defer e.mmux.RUnlock()
if e.PS != nil {
return fmt.Sprintf(`
Seq: %d
Pid: %d
Tid: %d
Name: %s
Category: %s
Host: %s
Timestamp: %s
Parameters: %s
%s
`,
e.Seq,
e.PID,
e.Tid,
e.Name,
e.Category,
e.Host,
e.Timestamp,
e.Kparams,
e.PS.StringShort(),
)
}
return fmt.Sprintf(`
Seq: %d
Pid: %d
Tid: %d
Name: %s
Category: %s
Host: %s
Timestamp: %s
Parameters: %s
`,
e.Seq,
e.PID,
e.Tid,
e.Name,
e.Category,
e.Host,
e.Timestamp,
e.Kparams,
)
}

// Empty return a pristine event instance.
func Empty() *Kevent {
return &Kevent{
Expand Down
70 changes: 4 additions & 66 deletions pkg/outputs/eventlog/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,12 @@ package eventlog
import (
"bytes"
"errors"
"fmt"
"github.com/rabbitstack/fibratus/pkg/kevent/ktypes"
"github.com/rabbitstack/fibratus/pkg/util/eventlog"
"syscall"

"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)

const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application`

var categoryCount = len(ktypes.Categories())

// ErrKeyExists signals that the registry key already exists
var ErrKeyExists = fmt.Errorf("%s\\%s already exists", addKeyName, source)

// Eventlog provides access to the system log.
type Eventlog struct {
Handle windows.Handle
Expand Down Expand Up @@ -74,59 +65,6 @@ func OpenRemote(host, source string) (*Eventlog, error) {
return &Eventlog{Handle: h}, nil
}

// Install modifies PC registry to allow logging with an event source src.
// It adds all required keys and values to the event log registry key.
// Install uses msgFile as the event message file. If useExpandKey is true,
// the event message file is installed as REG_EXPAND_SZ value,
// otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and
// log.Info to specify events supported by the new event source.
func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error {
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY)
if err != nil {
return err
}
defer appkey.Close()

sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE)
if err != nil {
return err
}
defer sk.Close()
if alreadyExist {
return ErrKeyExists
}

err = sk.SetDWordValue("CustomSource", 1)
if err != nil {
return err
}
if useExpandKey {
err = sk.SetExpandStringValue("EventMessageFile", msgFile)
} else {
err = sk.SetStringValue("EventMessageFile", msgFile)
}
if err != nil {
return err
}
if useExpandKey {
err = sk.SetExpandStringValue("CategoryMessageFile", msgFile)
} else {
err = sk.SetStringValue("CategoryMessageFile", msgFile)
}
if err != nil {
return err
}
err = sk.SetDWordValue("TypesSupported", eventsSupported)
if err != nil {
return err
}
err = sk.SetDWordValue("CategoryCount", uint32(categoryCount))
if err != nil {
return err
}
return nil
}

// Close closes event log.
func (l *Eventlog) Close() error {
return windows.DeregisterEventSource(l.Handle)
Expand All @@ -151,15 +89,15 @@ func (l *Eventlog) report(etype uint16, eid uint32, category uint16, msg []byte)

// Info writes an information event msg with event id eid to the end of event log.
func (l *Eventlog) Info(eid uint32, category uint16, msg []byte) error {
return l.report(uint16(Info), eid, category, msg)
return l.report(uint16(eventlog.Info), eid, category, msg)
}

// Warning writes a warning event msg with event id eid to the end of event log.
func (l *Eventlog) Warning(eid uint32, category uint16, msg []byte) error {
return l.report(uint16(Warn), eid, category, msg)
return l.report(uint16(eventlog.Warn), eid, category, msg)
}

// Error writes an error event msg with event id eid to the end of event log.
func (l *Eventlog) Error(eid uint32, category uint16, msg []byte) error {
return l.report(uint16(Erro), eid, category, msg)
return l.report(uint16(eventlog.Erro), eid, category, msg)
}
28 changes: 1 addition & 27 deletions pkg/outputs/eventlog/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package eventlog

import (
"fmt"
"github.com/rabbitstack/fibratus/pkg/kevent"
"text/template"

Expand All @@ -33,31 +32,6 @@ const (
tmpl = "output.eventlog.template"
)

// Level is the type definition for the eventlog log level
type Level uint16

const (
// Info represents the info log level
Info Level = 4
// Warn represents the warning info level
Warn Level = 2
// Erro represents the error log level
Erro Level = 1
)

func levelFromString(s string) Level {
switch s {
case "info", "INFO":
return Info
case "warn", "warning", "WARN", "WARNING":
return Warn
case "erro", "error", "ERRO", "ERROR":
return Erro
default:
panic(fmt.Sprintf("unrecognized evtlog level: %s", s))
}
}

// Config contains configuration properties for fine-tuning the eventlog output.
type Config struct {
// Enabled determines whether the eventlog output is enabled.
Expand All @@ -81,7 +55,7 @@ func (c Config) parseTemplate() (*template.Template, error) {
// AddFlags registers persistent flags.
func AddFlags(flags *pflag.FlagSet) {
flags.String(tmpl, "", "Go template for rendering the eventlog message")
flags.String(level, "info", "Specifies the eventlog level")
flags.String(level, "info", "Specifies the eventlog level. Deprecated")
flags.String(remoteHost, "", "Address of the remote eventlog intake")
flags.Bool(enabled, false, "Indicates if the eventlog output is enabled")
}
Loading
Loading