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
1 change: 1 addition & 0 deletions cmd/aurora/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ func (a *Agent) Run() error {
_ = a.listener.AddSource(ebpfprovider.SourceProcessExec)
_ = a.listener.AddSource(ebpfprovider.SourceFileCreate)
_ = a.listener.AddSource(ebpfprovider.SourceNetConnect)
_ = a.listener.AddSource(ebpfprovider.SourceBpfEvent)

if err := a.listener.Initialize(); err != nil {
return fmt.Errorf("initializing eBPF listener: %w", err)
Expand Down
7 changes: 6 additions & 1 deletion lib/consumer/sigma/sigmaconsumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"sync/atomic"
"time"

sigma "github.com/markuskont/go-sigma-rule-engine"
"github.com/Nextron-Labs/aurora-linux/lib/provider"
sigma "github.com/markuskont/go-sigma-rule-engine"
log "github.com/sirupsen/logrus"
"golang.org/x/time/rate"
)
Expand Down Expand Up @@ -106,6 +106,7 @@ func (s *SigmaConsumer) InitializeWithRules(ruleDirs []string) error {
NoCollapseWS: s.noCollapse,
})
if err != nil {
log.WithError(err).Error("SigmaConsumer: failed to create sigma ruleset")
return fmt.Errorf("creating sigma ruleset: %w", err)
}

Expand Down Expand Up @@ -303,6 +304,10 @@ type sigmaEventWrapper struct {

// Select implements sigma.Selector — performs key-value lookup for structured data.
func (w *sigmaEventWrapper) Select(key string) (interface{}, bool) {
// EventID lives on the event identifier, not in the data fields.
if key == "EventID" {
return int(w.event.ID().EventID), true
}
v := w.event.Value(key)
if !v.Valid {
return nil, false
Expand Down
142 changes: 142 additions & 0 deletions lib/provider/ebpf/bpf/bpf_monitor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-2.0
// bpf_monitor.c — BPF programs for tracepoint/syscalls/sys_{enter,exit}_bpf
//
// Traces bpf() syscall invocations by pairing sys_enter_bpf (capture command,
// program type, and program name) with sys_exit_bpf (check return value).
// Only successful calls are emitted. For BPF_PROG_LOAD, the program type and
// name are read from the user-space bpf_attr union.

//go:build ignore

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#define BPF_OBJ_NAME_LEN 16

// Offsets within the bpf_attr union for BPF_PROG_LOAD:
// prog_type at offset 0 (u32)
// prog_name at offset 48 (char[16])
#define ATTR_PROG_TYPE_OFF 0
#define ATTR_PROG_NAME_OFF 48

// Temporary storage for in-flight bpf() calls, keyed by pid_tgid.
struct bpf_call_args {
__u32 cmd;
__u32 prog_type;
char prog_name[BPF_OBJ_NAME_LEN];
};

struct bpf_event {
__u64 timestamp_ns;
__u32 pid;
__u32 uid;
__u32 cmd;
__u32 prog_type;
__s64 ret_val; // fd on success, negative errno on failure
char prog_name[BPF_OBJ_NAME_LEN];
};

// Per-CPU hash to correlate enter/exit
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10240);
__type(key, __u64);
__type(value, struct bpf_call_args);
} bpf_args SEC(".maps");

// Ring buffer for bpf events
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 2 * 1024 * 1024); // 2 MB
} bpf_events SEC(".maps");

// Lost event counter
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u64);
} bpf_lost_events SEC(".maps");

// PIDs that should be excluded from telemetry (Aurora itself).
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 64);
__type(key, __u32);
__type(value, __u8);
} self_pids SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_bpf")
int trace_sys_enter_bpf(struct trace_event_raw_sys_enter *ctx) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
if (bpf_map_lookup_elem(&self_pids, &pid))
return 0;

// args: cmd(0), attr(1), size(2)
__u32 cmd = (__u32)ctx->args[0];
const void *uattr = (const void *)ctx->args[1];

struct bpf_call_args args = {};
args.cmd = cmd;

// For BPF_PROG_LOAD (cmd=5), read program type and name from uattr
if (cmd == 5 && uattr) {
bpf_probe_read_user(&args.prog_type, sizeof(args.prog_type),
uattr + ATTR_PROG_TYPE_OFF);
bpf_probe_read_user_str(args.prog_name, sizeof(args.prog_name),
uattr + ATTR_PROG_NAME_OFF);
}

bpf_map_update_elem(&bpf_args, &pid_tgid, &args, BPF_ANY);
return 0;
}

SEC("tracepoint/syscalls/sys_exit_bpf")
int trace_sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
if (bpf_map_lookup_elem(&self_pids, &pid))
return 0;

struct bpf_call_args *args = bpf_map_lookup_elem(&bpf_args, &pid_tgid);
if (!args)
return 0;

// Clean up immediately
struct bpf_call_args saved = *args;
bpf_map_delete_elem(&bpf_args, &pid_tgid);

long retval = ctx->ret;

// Only emit successful calls (retval >= 0)
if (retval < 0)
return 0;

struct bpf_event *evt = bpf_ringbuf_reserve(&bpf_events, sizeof(*evt), 0);
if (!evt) {
__u32 key = 0;
__u64 *count = bpf_map_lookup_elem(&bpf_lost_events, &key);
if (count)
__sync_fetch_and_add(count, 1);
return 0;
}

evt->timestamp_ns = bpf_ktime_get_ns();
evt->pid = pid;

__u64 uid_gid = bpf_get_current_uid_gid();
evt->uid = uid_gid & 0xFFFFFFFF;

evt->cmd = saved.cmd;
evt->prog_type = saved.prog_type;
evt->ret_val = retval;

__builtin_memcpy(evt->prog_name, saved.prog_name, BPF_OBJ_NAME_LEN);

bpf_ringbuf_submit(evt, 0);
return 0;
}

char LICENSE[] SEC("license") = "GPL";
15 changes: 15 additions & 0 deletions lib/provider/ebpf/bpf_stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,18 @@ func (o *netMonitorObjects) Close() error { return nil }
func loadNetMonitorObjects(objs *netMonitorObjects, opts *ciliumebpf.CollectionOptions) error {
return fmt.Errorf("BPF programs are only available on Linux; run go generate on a Linux host")
}

// bpfMonitorObjects holds the BPF objects for the bpf syscall monitor.
type bpfMonitorObjects struct {
TraceSysEnterBpf *ciliumebpf.Program
TraceSysExitBpf *ciliumebpf.Program
BpfEvents *ciliumebpf.Map
BpfLostEvents *ciliumebpf.Map
SelfPids *ciliumebpf.Map
}

func (o *bpfMonitorObjects) Close() error { return nil }

func loadBpfMonitorObjects(objs *bpfMonitorObjects, opts *ciliumebpf.CollectionOptions) error {
return fmt.Errorf("BPF programs are only available on Linux; run go generate on a Linux host")
}
1 change: 1 addition & 0 deletions lib/provider/ebpf/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
EventIDProcessCreation uint16 = 1
EventIDNetworkConnection uint16 = 3
EventIDFileEvent uint16 = 11
EventIDBpfEvent uint16 = 100
)

// ebpfEvent is the concrete Event implementation for events from the eBPF provider.
Expand Down
118 changes: 118 additions & 0 deletions lib/provider/ebpf/fieldmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,124 @@ func buildNetFieldsMap(
return fields
}

// BPF command names aligned with Sysmon output.
var bpfCmdNames = map[uint32]string{
0: "BPF_MAP_CREATE",
1: "BPF_MAP_LOOKUP_ELEM",
2: "BPF_MAP_UPDATE_ELEM",
3: "BPF_MAP_DELETE_ELEM",
4: "BPF_MAP_GET_NEXT_KEY",
5: "BPF_PROG_LOAD",
6: "BPF_OBJ_PIN",
7: "BPF_OBJ_GET",
8: "BPF_PROG_ATTACH",
9: "BPF_PROG_DETACH",
10: "BPF_PROG_TEST_RUN",
11: "BPF_PROG_GET_NEXT_ID",
12: "BPF_MAP_GET_NEXT_ID",
13: "BPF_PROG_GET_FD_BY_ID",
14: "BPF_MAP_GET_FD_BY_ID",
15: "BPF_OBJ_GET_INFO_BY_FD",
16: "BPF_PROG_QUERY",
17: "BPF_RAW_TRACEPOINT_OPEN",
18: "BPF_BTF_LOAD",
19: "BPF_BTF_GET_FD_BY_ID",
20: "BPF_TASK_FD_QUERY",
21: "BPF_MAP_LOOKUP_AND_DELETE_ELEM",
22: "BPF_MAP_FREEZE",
23: "BPF_BTF_GET_NEXT_ID",
24: "BPF_MAP_LOOKUP_BATCH",
25: "BPF_MAP_LOOKUP_AND_DELETE_BATCH",
26: "BPF_MAP_UPDATE_BATCH",
27: "BPF_MAP_DELETE_BATCH",
28: "BPF_LINK_CREATE",
29: "BPF_LINK_UPDATE",
30: "BPF_LINK_GET_FD_BY_ID",
31: "BPF_LINK_GET_NEXT_ID",
32: "BPF_ENABLE_STATS",
33: "BPF_ITER_CREATE",
34: "BPF_LINK_DETACH",
35: "BPF_PROG_BIND_MAP",
}

// BPF program type names aligned with Sysmon output.
var bpfProgTypeNames = map[uint32]string{
0: "UNSPEC",
1: "SOCKET_FILTER",
2: "KPROBE",
3: "SCHED_CLS",
4: "SCHED_ACT",
5: "TRACEPOINT",
6: "XDP",
7: "PERF_EVENT",
8: "CGROUP_SKB",
9: "CGROUP_SOCK",
10: "LWT_IN",
11: "LWT_OUT",
12: "LWT_XMIT",
13: "SOCK_OPS",
14: "SK_SKB",
15: "CGROUP_DEVICE",
16: "SK_MSG",
17: "RAW_TRACEPOINT",
18: "CGROUP_SOCK_ADDR",
19: "LWT_SEG6LOCAL",
20: "LIRC_MODE2",
21: "SK_REUSEPORT",
22: "FLOW_DISSECTOR",
23: "CGROUP_SYSCTL",
24: "RAW_TRACEPOINT_WRITABLE",
25: "CGROUP_SOCKOPT",
26: "TRACING",
27: "STRUCT_OPS",
28: "EXT",
29: "LSM",
30: "SK_LOOKUP",
31: "SYSCALL",
}

func bpfCmdName(cmd uint32) string {
if name, ok := bpfCmdNames[cmd]; ok {
return name
}
return strconv.FormatUint(uint64(cmd), 10)
}

func bpfProgTypeName(pt uint32) string {
if name, ok := bpfProgTypeNames[pt]; ok {
return name
}
return strconv.FormatUint(uint64(pt), 10)
}

// buildBpfFieldsMap constructs the DataFieldsMap for a bpf_event.
func buildBpfFieldsMap(
pid, uid uint32,
image string,
username string,
cmd uint32,
progType uint32,
retVal int64,
progName string,
) enrichment.DataFieldsMap {
fields := make(enrichment.DataFieldsMap, 8)

fields.AddField("Image", image)
fields.AddField("User", username)
fields.AddField("ProcessId", strconv.FormatUint(uint64(pid), 10))
fields.AddField("BpfCommand", bpfCmdName(cmd))
fields.AddField("BpfProgramType", bpfProgTypeName(progType))
fields.AddField("BpfProgramId", strconv.FormatInt(retVal, 10))

if progName == "" {
fields.AddField("BpfProgramName", "-")
} else {
fields.AddField("BpfProgramName", progName)
}

return fields
}

// formatIPv4 formats a v4-mapped-v6 address (bytes 12-15) as dotted decimal.
func formatIPv4(addr [16]byte) string {
return strconv.Itoa(int(addr[12])) + "." +
Expand Down
1 change: 1 addition & 0 deletions lib/provider/ebpf/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ package ebpf
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang execMonitor bpf/exec_monitor.c -- -I/usr/include -I./bpf/headers
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang fileMonitor bpf/file_monitor.c -- -I/usr/include -I./bpf/headers
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang netMonitor bpf/net_monitor.c -- -I/usr/include -I./bpf/headers
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang bpfMonitor bpf/bpf_monitor.c -- -I/usr/include -I./bpf/headers
Loading
Loading