What to build: Extend the network tracer to parse sockaddr_in6 and display IPv6 addresses.
Why it's useful: Many modern services and cloud environments use IPv6. Attackers can use IPv6 to bypass IPv4-only monitoring.
What you'll learn: IPv6 address structure, handling multiple address families in eBPF, expanding the event struct.
Hints:
- Add a
u8 addr_v6[16]field to the event struct - Check
sa.sin_family == AF_INET6inparse_sockaddr - Use Python's
ipaddress.IPv6Address(bytes(addr_v6))for conversion - Don't forget to update the ctypes
RawEventdefinition
Test it works: Run curl -6 http://ipv6.google.com while tracing and verify the IPv6 address appears in output.
What to build: For each event, walk up the process tree (via /proc//status) to show the full ancestry: bash -> python -> curl.
Why it's useful: Knowing that a suspicious connect came from systemd -> sshd -> bash -> python -> nc tells a much richer story than just "nc connected somewhere."
What you'll learn: /proc filesystem, process tree walking, performance tradeoffs of enrichment.
Hints:
- Read
/proc/<pid>/statusand parse thePPid:line - Walk up until PID 1 or a read error
- Cache results aggressively, process trees don't change often
- Consider a max depth (8-10) to avoid pathological cases
Test it works: Run bash -c "python3 -c 'import os; os.system(\"ls\")' and verify the ancestry chain appears.
What to build: When the tool exits (Ctrl+C), print a summary table showing total events by type, total detections by severity, and top 10 processes by event count.
Why it's useful: After a tracing session, you want a quick overview of what happened without scrolling through thousands of events.
What you'll learn: Data aggregation, Rich table formatting, clean shutdown patterns.
Hints:
- Add counters to the event processing pipeline (use
collections.Counter) - Register a cleanup function that prints the summary
- The
TableRendereralready shows how to use Rich tables
Test it works: Run the tracer for 30 seconds on a busy system, then Ctrl+C and verify the summary appears.
What to build: Detect whether events originate from inside a container and include the container ID in the output. Flag kernel module loads and mount operations from containers as CRITICAL.
Why it's useful: Container escapes are a major attack vector in Kubernetes. Detecting mount or init_module from inside a container namespace is a strong indicator of an escape attempt.
What you'll learn: Linux namespaces, cgroups, container runtime detection, how containers are just processes with extra isolation.
Hints:
- Read
/proc/<pid>/cgroupto detect container membership - Docker containers have cgroup paths like
/docker/<container_id> - Kubernetes pods have paths like
/kubepods/pod<uid>/<container_id> - PID 1 inside a container maps to a regular PID on the host
- Add a
container_idfield toTracerEvent
Test it works: Run docker run --rm alpine sh -c "ls /etc" while tracing and verify the container ID appears. Test that mount from a container triggers a CRITICAL detection.
What to build: Detect abnormal syscall rates. If a process makes more than N openat calls in T seconds (e.g., 100 opens in 5 seconds), flag it as "Rapid File Scanning."
Why it's useful: Automated tools scanning for credentials, sensitive files, or exploitable configurations generate distinctive patterns of rapid file access that normal usage doesn't produce.
What you'll learn: Sliding window rate calculation, threshold-based anomaly detection, tuning false positive rates.
Hints:
- Use the existing per-PID deque in the detection engine
- Count events of each type in the window
- Start with high thresholds to avoid noise, then tune down
- Consider different thresholds per event type (openat is naturally high-volume, ptrace is not)
- Add a
D011rule toDETECTION_RULES
Test it works: Write a script that opens 200 files in a loop and verify the detection triggers. Verify that normal ls of a large directory does not trigger it.
What to build: Add an output mode that sends events to a remote syslog server or as JSON over UDP.
Why it's useful: In production, you'd feed eBPF events into a SIEM (Splunk, Elastic, Wazuh). UDP/syslog is the simplest integration point.
What you'll learn: Network programming in Python, syslog protocol (RFC 5424), structured logging for SIEM integration.
Hints:
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)for UDP- Syslog format:
<priority>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID MSG - Map severity levels to syslog priorities (CRITICAL -> LOG_CRIT, etc.)
- Add
--syslog host:portCLI option
Test it works: Run nc -ul 1514 in one terminal, start the tracer with --syslog localhost:1514, and verify events arrive.
What to build: Move PID and comm filtering into the eBPF programs so filtered events never reach userspace. Currently, all events flow through the ring buffer and get filtered in Python.
Why it's useful: On a busy server generating 50K+ events per second, userspace filtering wastes ring buffer bandwidth. eBPF-level filtering reduces overhead by 10-100x for filtered workloads.
What you'll learn: BPF hash maps for configuration, passing filter state from Python to eBPF, verifier-safe conditional logic.
Hints:
- Use
BPF_HASH(pid_filter, u32, u32)as a set of PIDs to include - Populate the map from Python:
b["pid_filter"][ctypes.c_uint32(pid)] = ctypes.c_uint32(1) - In the eBPF program, check:
if (pid_filter.lookup(&pid) == NULL) return 0; - For comm filtering, use
BPF_HASH(comm_filter, char[16], u32) - An empty filter map means "trace all"
Test it works: Benchmark event rate with and without eBPF-level filtering on a process spawning 1000 child processes per second. Measure CPU usage difference.
What to build: A terminal-based dashboard using Rich's Live display that shows: event rate graph, active detections, top processes, and a scrolling event log, all updating in real time.
Why it's useful: Operational security monitoring needs at-a-glance visibility. A dashboard lets you watch system behavior during incident response without reading individual log lines.
What you'll learn: Rich Live and Layout for TUI design, concurrent data aggregation, refresh rate management.
Hints:
- Use
rich.live.Livewithrich.layout.Layoutfor multi-panel display - Update every 500ms (2 FPS is enough for human readability)
- Track event rate with a 1-second rolling window
- Use
rich.panel.Panelfor each section - Consider
rich.progress.SparklineColumnfor rate visualization
Test it works: Run the dashboard on a system under load (e.g., stress-ng --cpu 4 --io 4) and verify all panels update correctly.
What to build: Replace the hardcoded detection logic in detector.py with a rule engine that loads detection rules from YAML files, similar to Falco's rule format:
- rule: Reverse Shell Detected
condition:
sequence:
- event_type: connect
within: 10s
- event_type: execve
comm_in: [sh, bash, dash, zsh]
group_by: pid
severity: CRITICAL
mitre: T1059.004
description: Shell execution following outbound connectionWhy it's useful: Hardcoded detection rules require code changes and redeployment. A DSL lets security teams write and modify rules without touching Python code, which is how Falco, Sigma, and YARA work.
What you'll learn: Rule engine design, YAML schema validation, temporal pattern matching, DSL design principles.
Hints:
- Start with stateless rules (single event matching) before tackling sequences
- Use Pydantic for rule schema validation
- Support operators:
eq,in,startswith,regex,gt,lt - For sequence rules, reuse the existing deque-based correlation but make it configurable
- Add
--rules-dirCLI option to load rules from a directory - Consider rule priorities (first match vs best match)
Test it works: Port all 10 existing detection rules to YAML. Verify all existing tests still pass against the YAML-loaded rules. Add a custom rule and verify it detects correctly.
- Container + Rate Anomaly: Detect rapid file scanning inside containers (strong indicator of container reconnaissance before an escape attempt)
- IPv6 + Syslog: Full-stack monitoring with IPv6 support piped to a SIEM
- Dashboard + eBPF Filtering: High-performance dashboard that only shows filtered events
- Wazuh: Pipe JSON output to Wazuh's
ossec.logor use the API for real-time event ingestion - Elastic SIEM: Send JSONL output to Filebeat, which ships it to Elasticsearch
- Grafana/Loki: Use promtail to ship events, build dashboards for event rates and detection counts
- Slack/PagerDuty: Add a webhook renderer that sends CRITICAL detections to Slack or triggers PagerDuty incidents
- Generate load with
stress-ng --syscall 0 --timeout 60s - Measure events/second throughput
- Profile with
py-spyto find Python bottlenecks - Target: handle 50K+ events/second without dropping events
- Start with 256KB ring buffer
- Under load, check drop rate (add a lost event callback)
- Experiment with 512KB, 1MB, 4MB buffers
- Find the minimum buffer size that achieves zero drops for your workload
Monitor writes to critical system files (/etc/passwd, /etc/sudoers, /etc/ssh/sshd_config) and alert on any modification. This is what tools like AIDE and Tripwire do, but in real time.
Maintain a list of expected outbound connections per process. Alert when a process connects to an IP or port not in its allowlist. Start with a learning mode that auto-generates the allowlist.
Detect processes that try to evade tracing: renaming themselves to look like system processes, forking rapidly to confuse PID-based tracking, or using prctl(PR_SET_NAME) to change their comm.
- Port the eBPF programs from BCC to libbpf for production readiness
- Add eBPF LSM hooks for enforcement (block, not just detect)
- Build a web UI with WebSocket-based real-time event streaming
- Add Sigma rule format support (industry standard detection rules)
- Create systemd unit file for running as a daemon
- Easy 1: IPv6 Support
- Easy 2: Process Ancestry Chain
- Easy 3: Event Counter Summary
- Intermediate 4: Container-Aware Detection
- Intermediate 5: Rate-Based Anomaly Detection
- Intermediate 6: Syslog/UDP Output
- Advanced 7: eBPF-Level Filtering
- Advanced 8: Real-Time Dashboard
- Expert 9: Detection Rule DSL
Debugging eBPF programs: Add bpf_trace_printk("debug: %d\n", value) to your C code and read output with sudo cat /sys/kernel/debug/tracing/trace_pipe. This is the printf-debugging equivalent for eBPF.
Verifier errors: The eBPF verifier prints cryptic messages. Common causes: unbounded loop, memory access without bounds check, stack overflow (>512 bytes). Reduce struct sizes or use BPF maps for large data.
BCC issues: If BCC fails to compile, check that kernel headers match your running kernel: uname -r should match a directory in /lib/modules/.
Community resources:
- iovisor/bcc GitHub Issues - BCC-specific questions
- eBPF Slack - Community chat
- Brendan Gregg's Blog - eBPF performance analysis