CKS — Syscall Monitoring
Syscall Monitoring
Every action a container takes — reading a file, opening a network connection, spawning a process — is a system call (syscall). The Linux kernel provides hundreds of syscalls, and monitoring them is the foundation of runtime security. Falco, sysdig, and strace all operate at this layer.
For the CKS exam, you need to understand which syscalls indicate which threats, how Falco intercepts them, and how to use complementary tools to investigate suspicious activity.
How Syscalls Work
When a process inside a container needs to interact with the operating system (read a file, create a socket, fork a child process), it makes a syscall. The kernel handles the request and returns a result.
┌──────────────────────────────────┐
│ Container Process │
│ cat /etc/shadow │
│ ↓ │
│ libc: open("/etc/shadow", ...) │
└──────────┬───────────────────────┘
│ syscall: openat()
┌──────────▼───────────────────────┐
│ Linux Kernel │
│ ┌───────────────────────────┐ │
│ │ Falco Driver (kmod/eBPF) │ │
│ │ Captures event + metadata │ │
│ └───────────┬───────────────┘ │
│ │ │
│ Kernel processes the syscall │
└──────────────────────────────────┘
│
▼
Falco Engine evaluates rules
Key insight: Containers share the host kernel. There is no separate kernel per container. This means a single Falco instance on the host can monitor all containers running on that node.
How Falco Intercepts Syscalls
Falco uses one of two drivers to intercept syscalls before the kernel processes them:
Kernel Module (kmod)
Application → Syscall → Falco Kernel Module → Kernel → Result
↓
Event Buffer → Falco User Space
- Loaded via
insmodorfalco-driver-loader module - Fast, direct access to syscall table
- Risk: a buggy module can crash the kernel
- Requires kernel headers for compilation
eBPF Probe
Application → Syscall → eBPF Tracepoint → Kernel → Result
↓
eBPF Map → Falco User Space
- Loaded via the BPF subsystem
- Verified by the kernel verifier — cannot crash the kernel
- Requires kernel ≥ 4.14 with BPF support
- Slightly higher overhead than kmod
Common Syscalls and Their Security Implications
| Syscall | What It Does | Security Relevance |
|---|---|---|
execve |
Execute a program | Process spawning — shell access, malware execution |
open / openat |
Open a file | File access — reading credentials, writing malware |
read |
Read from file descriptor | Data exfiltration, credential theft |
write |
Write to file descriptor | File modification, log tampering |
connect |
Initiate network connection | C2 beaconing, data exfiltration, reverse shell |
accept |
Accept incoming connection | Backdoor listener |
bind |
Bind to a port | Unauthorized service |
clone / fork |
Create child process | Fork bomb, spawning malicious processes |
setuid / setgid |
Change user/group ID | Privilege escalation |
mount |
Mount filesystem | Container escape via host filesystem |
ptrace |
Trace/debug process | Process injection, debugging to extract secrets |
unlink |
Delete a file | Evidence destruction |
rename |
Rename a file | Covering tracks |
socket |
Create network socket | Network activity setup |
sendto / recvfrom |
Send/receive data | Data transfer — exfiltration or C2 communication |
Syscall-to-Threat Mapping
| Attack | Syscalls Involved | Falco Detection |
|---|---|---|
| Shell access | execve (bash/sh) |
spawned_process and container and shell_procs |
| Credential theft | openat, read (/etc/shadow) |
open_read and sensitive_files |
| Reverse shell | execve, connect, dup2 |
spawned_process and proc.cmdline contains "/dev/tcp" |
| Container escape | mount, open (/proc/1/root) |
evt.type = mount and container |
| Crypto mining | execve (xmrig), connect (pool) |
proc.name in (xmrig, minerd) |
| Privilege escalation | setuid, setgid |
evt.type = setuid and container |
| Data exfiltration | connect, sendto |
evt.type = connect and fd.sip.name = <external> |
| Persistence | open_write (/etc/crontab) |
open_write and fd.name startswith /etc/cron |
| Service account abuse | openat, read (token path) |
fd.name startswith /var/run/secrets/kubernetes.io |
| Log tampering | unlink, write (/var/log) |
(evt.type = unlink or open_write) and fd.name startswith /var/log |
Creating Detection Rules for Specific Syscall Patterns
Detect Mount Syscall in Containers (Container Escape Attempt)
- rule: Mount attempted in container
desc: Detect mount syscall inside a container (potential escape)
condition: >
evt.type = mount and container
output: >
Mount attempted in container (user=%user.name command=%proc.cmdline
container=%container.name namespace=%k8s.ns.name pod=%k8s.pod.name)
priority: CRITICAL
tags: [container, filesystem, mitre_privilege_escalation]
Detect ptrace in Containers (Process Injection)
- rule: Ptrace detected in container
desc: Detect ptrace syscall in a container (debugging/injection)
condition: >
evt.type = ptrace and container
and not proc.name in (strace, gdb, ltrace)
output: >
Ptrace detected (user=%user.name command=%proc.cmdline
container=%container.name namespace=%k8s.ns.name)
priority: CRITICAL
tags: [container, process, mitre_privilege_escalation]
Detect Outbound Connections from Restricted Pods
- rule: Unexpected outbound connection
desc: Detect outbound network connections from containers not expected to make them
condition: >
evt.type = connect and container
and fd.typechar = 4
and not fd.sip.name in (kubernetes.default.svc.cluster.local)
and k8s.ns.name = "restricted"
output: >
Outbound connection from restricted namespace
(user=%user.name command=%proc.cmdline dest=%fd.name
container=%container.name namespace=%k8s.ns.name pod=%k8s.pod.name)
priority: WARNING
tags: [container, network, mitre_exfiltration]
Sysdig — Complementary Syscall Tool
Sysdig is an open-source tool from the same company that created Falco. While Falco is a real-time rule-based alerting engine, sysdig is a capture and inspection tool — think of it as tcpdump for syscalls.
Installing sysdig:
sudo apt-get install -y sysdig
Common sysdig commands for investigation:
# Watch all syscalls from a specific container
sudo sysdig container.name=nginx
# Watch only execve syscalls (process launches) in containers
sudo sysdig "evt.type=execve and container"
# Spy on users — see every command executed
sudo sysdig -c spy_users
# Watch file opens in a specific container
sudo sysdig "evt.type in (open, openat) and container.name=web-app"
# Watch network activity in containers
sudo sysdig "evt.type in (connect, accept) and container"
# Capture syscalls to a file for later analysis
sudo sysdig -w /tmp/capture.scap "container.name=suspicious-pod"
# Read captured syscalls
sudo sysdig -r /tmp/capture.scap
# Filter captured data
sudo sysdig -r /tmp/capture.scap "evt.type=execve"
Sysdig chisels (built-in scripts):
# List all available chisels
sudo sysdig -cl
# Top processes by CPU
sudo sysdig -c topprocs_cpu
# Top files by read bytes
sudo sysdig -c topfiles_bytes
# See all commands users execute
sudo sysdig -c spy_users
# Network connections per container
sudo sysdig -c topconns
Using strace for Targeted Debugging
strace traces syscalls for a single process. Useful for debugging a specific container process:
# Trace all syscalls for a process
strace -p <PID>
# Trace only file-related syscalls
strace -e trace=file -p <PID>
# Trace only network-related syscalls
strace -e trace=network -p <PID>
# Trace with timestamps
strace -t -p <PID>
# Trace a command directly
strace ls /etc/
# Trace syscalls inside a container (find PID first)
# From the host:
PID=$(docker inspect --format '{{.State.Pid}}' <container_id>)
sudo strace -p $PID -f
strace vs sysdig vs Falco:
| Tool | Purpose | Scope | Real-time Alerts |
|---|---|---|---|
| strace | Debug a single process | One PID | No |
| sysdig | Capture and inspect syscalls | All containers on host | No (manual inspection) |
| Falco | Rule-based runtime alerting | All containers on host | Yes |
Performance Impact of Syscall Monitoring
Syscall monitoring adds overhead. Understanding the tradeoffs:
| Factor | Kernel Module | eBPF Probe | No Monitoring |
|---|---|---|---|
| CPU overhead | 1-3% | 2-5% | 0% |
| Latency per syscall | ~1-2μs | ~2-4μs | 0 |
| Memory usage | Moderate | Moderate | None |
| Safety | Can crash kernel | Cannot crash kernel | N/A |
| Hot reload | No — restart needed | Possible | N/A |
Best practices to minimize overhead:
- Use specific conditions in rules — avoid overly broad rules
- Limit the number of enabled rules to what you need
- Use macros and lists to optimize condition evaluation
- Choose eBPF for safety-critical environments
- Monitor Falco’s own resource usage:
systemctl status falco
Filtering Syscalls by Container and Namespace
Falco conditions can target specific containers or namespaces:
# Only monitor a specific namespace
- rule: Shell in production
condition: >
spawned_process and container and shell_procs
and k8s.ns.name = "production"
# Exclude system namespaces
- rule: Shell in user namespace
condition: >
spawned_process and container and shell_procs
and not k8s.ns.name in (kube-system, kube-public, kube-node-lease)
# Monitor a specific container image
- rule: Shell in custom app
condition: >
spawned_process and container and shell_procs
and container.image.repository = "mycompany/secure-app"
# Monitor by pod label (if Falco K8s metadata enrichment is enabled)
- rule: Shell in labeled pod
condition: >
spawned_process and container and shell_procs
and k8s.pod.label.security = "high"
Examining Falco’s Syscall Event Stream
You can run Falco in a mode that prints raw events (useful for understanding what data is available):
# Print all events Falco sees (very verbose — use briefly)
sudo falco -A
# Print events matching a specific rule
sudo falco -A -t shell
# Print events in JSON format for machine parsing
sudo falco -A -o json_output=true
Practical Workflow: Investigating a Suspicious Container
- Falco alert fires — “Shell spawned in container”:
Warning A shell was spawned in container
(container=web-app namespace=production pod=web-app-7d4f)
- Check what’s happening in the container with sysdig:
sudo sysdig container.name=web-app "evt.type=execve"
- See what files are being accessed:
sudo sysdig container.name=web-app "evt.type in (open, openat)"
- Check for network connections:
sudo sysdig container.name=web-app "evt.type in (connect, sendto)"
- Capture traffic for forensic analysis:
sudo sysdig -w /tmp/incident-capture.scap container.name=web-app
- Take action: Delete the pod, cordon the node, review logs.
Practice Questions
Question 1
Which syscall is triggered when someone executes kubectl exec -it pod -- /bin/bash?
Show Answer
The primary syscall is execve — it executes the /bin/bash program inside the container. Specifically:
cloneorforkcreates a new processexecvereplaces the process image with/bin/bashread/writeon the terminal PTY for I/O
Falco detects this via the spawned_process macro which checks evt.type = execve and evt.dir = <.
Question 2
An attacker inside a container runs cat /etc/shadow. Which syscalls are involved and which Falco rule detects this?
Show Answer
Syscalls involved:
execve— to start thecatprocessopenat— to open/etc/shadowfor readingread— to read the file contentswrite— to write the output to stdout
Falco detection: The default rule “Read sensitive file untouched” fires because:
open_readmatches theopenatcallsensitive_filesmacro matches/etc/shadowcontainermacro confirms it’s inside a container
Question 3
What is the difference between sysdig and Falco? When would you use each?
Show Answer
| Aspect | Falco | sysdig |
|---|---|---|
| Purpose | Real-time rule-based alerting | Syscall capture and inspection |
| Mode | Always running as a service | Ad-hoc investigation tool |
| Output | Alerts (stdout, syslog, file, HTTP) | Raw syscall events or filtered views |
| Rules | YAML rule files with conditions | Command-line filters |
| Use case | “Alert me when X happens” | “Show me what happened” |
Use Falco for continuous monitoring and alerting.
Use sysdig for incident investigation after a Falco alert or to understand a container’s behavior.
Question 4
Write a sysdig command to watch all process executions inside a container named web-server.
Show Answer
sudo sysdig "evt.type=execve and container.name=web-server"
For more detail including the full command line:
sudo sysdig -p "%evt.time %proc.name %proc.cmdline" \
"evt.type=execve and container.name=web-server"
To also see the parent process:
sudo sysdig -p "%evt.time %proc.pname → %proc.name %proc.cmdline" \
"evt.type=execve and container.name=web-server"
Question 5
Why does Falco need to run with elevated privileges (either as root or with the kernel module loaded)?
Show Answer
Falco intercepts kernel-level syscalls, which requires privileged access:
- Kernel module — loading a kernel module (
insmod) requires root privileges. The module hooks into the syscall table. - eBPF probe — loading BPF programs requires
CAP_SYS_ADMINorCAP_BPFcapability. The probe attaches to kernel tracepoints.
Without these privileges, Falco cannot see any syscalls and is useless. When deployed as a DaemonSet, the Falco pods run with privileged: true and hostPID: true for this reason.
Next: Immutable Containers →