Hardening Containers with Seccomp: Hands-On Profiles, Pitfalls, and Real Exploits

Ben Hirschberg (co-founder and CTO · ARMO)

BSides Las Vegas 2025 · Day 1

Overview

This session frames seccomp as an underused Linux kernel capability that can materially constrain attackers inside containerized environments—even when initial compromise succeeds. The speaker, introducing himself as Ben and as co-founder and CTO at a cloud security company called Armo, positions the talk around adoption friction: seccomp is known in parts of the security community but rarely operationalized at scale in cloud-native fleets. He connects historical motivations (browser sandboxes, limiting dangerous syscalls) to modern Kubernetes and Docker defaults, then argues that the real gap is usability and observability, not raw kernel power. The presentation includes two live demonstrations: a comparison of unconfined versus default-profile containers attempting namespace and keyring-style operations, and a second demo that walks from successful exploitation of a vulnerable application to blocking repeat exploitation using a syscall profile derived from observed behavior (via tooling associated with the Kubescape open source project and eBPF-based observation).

Watch on YouTube

Visual summary for Hardening Containers with Seccomp: Hands-On Profiles, Pitfalls, and Real Exploits by Ben Hirschberg
Visual summary for Hardening Containers with Seccomp: Hands-On Profiles, Pitfalls, and Real Exploits by Ben Hirschberg

Key moments

  1. 2:00 Speaker introduces himself, Armo, Kubescape/CNCF, and the talk’s goal: make seccomp usable in cloud-native environments.
  2. 10:00 Threat model for containers: kernel escape via syscalls versus lateral movement when workloads are overly capable.
  3. 14:00 How seccomp actions work in profiles and why syscall logging often fails to help operators in cloud environments.
  4. 24:00 Kubernetes versus Docker defaults: early Kubernetes disabling seccomp inheritance and later reintroduction of profile options.
  5. 32:00 Live demo: unconfined container can create namespaces and use keyring APIs; default seccomp blocks those while nginx stays up.
  6. 38:00 Second demo setup: SSTI in a change-detection app used to steal the Kubernetes service account token via Jinja/Python.
  7. 42:00 After applying a behavior-derived seccomp profile, the same exploit path fails because a required syscall is blocked.
  8. 46:00 Q&A: justifying seccomp investment to a CISO despite imperfect CVE coverage; compliance-time-buying framing.

Hardening Containers with Seccomp: Hands-On Profiles, Pitfalls, and Real Exploits

Speakers: Ben, Co-founder and CTO, Armo

Conference: BSides Las Vegas

YouTube: https://www.youtube.com/watch?v=ofok7dW2txE

Overview

This session frames seccomp as an underused Linux kernel capability that can materially constrain attackers inside containerized environments—even when initial compromise succeeds. The speaker, introducing himself as Ben and as co-founder and CTO at a cloud security company called Armo, positions the talk around adoption friction: seccomp is known in parts of the security community but rarely operationalized at scale in cloud-native fleets. He connects historical motivations (browser sandboxes, limiting dangerous syscalls) to modern Kubernetes and Docker defaults, then argues that the real gap is usability and observability, not raw kernel power. The presentation includes two live demonstrations: a comparison of unconfined versus default-profile containers attempting namespace and keyring-style operations, and a second demo that walks from successful exploitation of a vulnerable application to blocking repeat exploitation using a syscall profile derived from observed behavior (via tooling associated with the Kubescape open source project and eBPF-based observation).

Background

▶ Watch: Speaker introduces himself, Armo, Kubescape/CNCF, and the talk’s goal: make s... (2:00)

The speaker describes seccomp as a long-standing Linux feature—on the order of roughly two decades in the kernel—originally aimed at building process sandboxes so that less trusted code (for example inside browsers) could not easily pivot even when exploited. A second motivation he cites is restricting access to syscalls that might be dangerous on specific kernel versions. He recounts an early “strict” mode notion where a process could limit itself to a very small set of syscalls (he lists read, write, exit, and close as the classic minimal set example), then notes the evolution toward more expressive filtering, including BPF (and later eBPF) for finer-grained rules and argument inspection.

For containers, he distinguishes the Linux seccomp API (complex, programmatic, steep learning curve) from container runtime seccomp profiles, which he characterizes as more schematic: lists of syscalls, optional argument filters, and prescribed actions. He enumerates common actions (allow, log, SECCOMP_RET_USER_NOTIF / notify-style hooks, return error, trap signal, kill thread or process group) and immediately highlights a practical cloud operations problem: logging syscall denials often fails to close the loop because kernel logs are not uniformly collected or correlated in many cloud deployments.

Key Findings

▶ Watch: How seccomp actions work in profiles and why syscall logging often fails to h... (14:00)

  1. Threat model in containers: The speaker summarizes two major post-compromise directions for malicious code in a container—attempting kernel exploitation / container escape via specific syscalls, and lateral movement using broad syscall and capability surface (network, filesystem, process creation, etc.). He positions seccomp as more about constraining progression than preventing the first bug.
  1. Kubernetes historically weakened defaults: He states that early Kubernetes effectively pushed workloads toward less seccomp inheritance than Docker’s default posture, with support improving in later releases (the recording’s wording around the version number is unclear). He outlines three modes in the Kubernetes mental model he uses: effectively unconfined, a default profile aligned with Docker’s baseline idea, and localhost / bring-your-own JSON.
  1. The “basic” profile is a blunt instrument: The built-in baseline blocks on the order of dozens of syscalls (he gives an approximate count and notes it changes over time), including syscall families that are uncommon for typical app containers but attractive for attackers (examples he mentions include mount, pivot_root, unshare). He notes exceptions where databases or specific storage behaviors conflict with blanket rules.
  1. Custom profiles are hard to reason about: Even JSON profiles become difficult because meaningful argument restrictions (for example path-based restrictions) often do not map cleanly to how the kernel sees pointers into userspace; he argues this limits how much “smart” filtering you can express in portable profile JSON.
  1. The human factor: A recurring theme is that many developers do not think in terms of syscalls at all (he uses the example of a Java developer unable to list syscalls their runtime uses). That knowledge gap makes “tailored profiles” socially and operationally expensive.
  1. Behavior-driven profiling as a bridge: The speaker describes a node-level agent approach (associated with Kubescape) that observes per-container syscall usage with eBPF, builds application profiles, and converts observed syscall sets into seccomp profiles automatically—intended to reduce guesswork.
  1. Alternatives exist but have adoption friction: Toward the end, the speaker briefly contrasts seccomp with other Linux hardening approaches. He expresses personal enthusiasm for Landlock LSM as interesting reading, while noting AppArmor and SELinux can be powerful but often suffer similar usability problems in containerized environments—frequently stuck between profiles that are too permissive or too brittle for real applications.

Technical Deep Dive

▶ Watch: Live demo: unconfined container can create namespaces and use keyring APIs; d... (32:00)

The talk’s technical spine moves from syscall-level theory to runtime behavior. In container land, seccomp profiles are treated as configuration artifacts: syscall names, match conditions, and actions. The speaker emphasizes the mismatch between security engineers’ desire for precise rules (for example blocking opens of sensitive paths) and kernel realities where argument inspection is limited or awkward in profile JSON.

He contrasts signature/reputation-heavy defenses with syscall filtering that reduces attacker degrees of freedom even when a vulnerability is unknown—while cautioning that not every vulnerability class is neutered by syscall denial (exact coverage depends on the syscall surface of the exploit chain).

For Kubernetes integration, the narrative focuses on how clusters commonly end up with unconfined workloads unless operators intentionally set profiles, and how “bring your own profile” risks availability breaks if the profile is wrong. The observability gap around denials exacerbates that risk: if you cannot see what was blocked, you cannot confidently tune.

The second demo’s exploit chain is described as a server-side template injection issue in a change detection application with a public proof-of-concept; the injected path uses Jinja templating to run Python that reads the Kubernetes service account token from the filesystem and exfiltrates it (in the demo, to a webhook service). After generating and applying a syscall profile from observed legitimate behavior and restarting the workload, the same exploit script fails because the exploit path required a syscall absent from the allowed set—so the attack stops mid-chain. The speaker notes a minor inconsistency where a shell still appeared available in one check, which he attributes to profiling artifacts capturing shell activity—this becomes a discussion point in Q&A.

He also spends time praising Docker from a security practitioner perspective: even if opinions on Docker vary, he argues attaching a default restrictive profile to ordinary docker run behavior was a meaningful security win in practice. That praise is meant to sharpen the contrast with early Kubernetes defaults, where “make everything work” pressures reportedly pushed seccomp aside—creating a multi-year debt many clusters still carry.

Demo / Proof of Concept

▶ Watch: Second demo setup: SSTI in a change-detection app used to steal the Kubernete... (38:00)

Demo 1: Two nginx containers—one unconfined and one with the default seccomp behavior—are compared using tooling installed in-container (the recording shows network hiccups during package install). The speaker checks for seccomp filters, attempts to create a new Linux namespace, and attempts keyring access. In the unconfined case, those operations succeed; with the default profile, they fail while nginx continues serving traffic.

Demo 2: On a Kubernetes cluster with the vulnerable application deployed and Kubescape running, the speaker executes the template-injection exploit, demonstrates token theft via the webhook callback, then retrieves an application profile CRD object, converts it for deployment, patches the workload to use the generated seccomp profile, waits for the pod to become healthy, and reruns the exploit. The second run produces no stolen token payload, attributed to syscall blocking during exploitation.

Defensive Implications

▶ Watch: Q&A: justifying seccomp investment to a CISO despite imperfect CVE coverage; ... (46:00)

Operators should assume default Docker-style seccomp is a useful baseline but not a complete program. Teams need explicit decisions in Kubernetes about securityContext seccomp settings rather than assuming safe inheritance. Where possible, pair syscall restrictions with telemetry that surfaces denials in the same planes you already monitor (SIEM / observability stack), because kernel logs alone are often insufficient in cloud environments.

For organizations building tighter profiles, the speaker’s implied playbook is: observe realistic traffic and behavior in controlled environments (for example staging), generate profiles from observed syscalls, roll out with change control, and treat profiling done during ad-hoc debugging as a security risk (captured attacker tooling or manual shells can “legitimize” dangerous syscalls). The Q&A exchange about CISO justification frames seccomp as a way to buy operational time against CVE backlogs—he describes this as aligning with how some compliance frameworks treat compensating controls, while explicitly stating it is not “hard science” mapping CVEs to syscall mitigation.

The final audience question returns to profiling integrity: if you learn syscalls from production-like observation, could you accidentally “bake in” dangerous capabilities because an attacker happened to use them during the capture window—or because an operator opened a shell during troubleshooting? The speaker interprets this as adjacent to a common question about capturing real attacks during profiling. His practical mitigation themes include generating profiles in CI/CD and staging environments, promoting profiles through reviewable change processes (for example via GitOps), and maintaining an auditable trail of what changed over time—accepting that edge cases can still occur and may need follow-up discussion offline.

Key Takeaways

  • Seccomp reduces attacker maneuvering room inside Linux containers even when application vulnerabilities exist, but adoption is limited by skill gaps, fear of breakage, and weak feedback loops for denials.
  • Container runtimes expose seccomp as JSON profiles—simpler than raw seccomp programming, still difficult to tune safely at scale.
  • Kubernetes history matters: clusters may not automatically give workloads the same syscall posture as interactive Docker runs unless configured.
  • Tailored profiles require knowing what syscalls an app truly needs; pure guesswork breaks production; pure static lists are hard to maintain.
  • eBPF-assisted observation and automated profile generation are presented as a practical path from “we have no idea what syscalls we use” to enforceable profiles.
  • Treat profiling and rollout as engineering processes with staging, audit trails, and monitoring—not one-off JSON edits in production.

About the Speaker(s)

The speaker identifies himself as Ben, co-founder and CTO at Armo, a cloud security company. He describes a long career in security, including a white hat background moving into product and development, and states he is a maintainer of the Kubescape open source project under the CNCF and an active CNCF member. He also references contributing to Kubernetes. Exact spelling of his surname in the introduction is unclear from the recording; titles beyond CTO are not stated.

Reviews

Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT

A practitioner-grounded tour of seccomp in containers with credible threat framing, honest limits on argument filtering, and a risky live demo that actually shows syscall policy blocking a real exploit chain.

Heather Calloway (CISO) — STRONG ACCEPT

This is actionable defensive engineering for anyone running Kubernetes at scale: it connects syscall policy to breach containment, names the governance pain (breakage risk, logging blind spots), and gives executives a sane compensating-control narrative without pretending seccomp replaces patching.

→ Top-rated talks at BSides Las Vegas 2025

All talks from BSides Las Vegas 2025