I've been auditing runtime syscall patterns from our OpenClaw agents for the past three months, specifically looking at anomalous network connections that indicate lateral movement or data exfiltration attempts. The most consistent signal for an agent trying to access internal APIs it shouldn't is not in the destination IP alone, but in the combination of the network syscall sequence, the target port, and the process context.
The generic "alert on connection to internal subnet" rule is useless. It floods you with noise from legitimate service discovery and orchestration traffic. You need to baseline the agent's normal operational profile first. An agent's runtime should only ever need to talk to a handful of designated control planes and maybe a logging endpoint. Everything else is suspect.
Here’s a concrete detection approach focusing on seccomp-audit logs and network namespace egress. You need to instrument the agent runtime to log all `connect()` and `socket()` syscalls that pass the seccomp filter, then enrich with cgroup and network namespace info.
**Primary Rule Logic (Pseudocode for SIEM):**
```
agent_process_name IN ("openclaw-runtime", "oc-agent")
AND syscall_id IN (socket, connect)
AND dest_port NOT IN (443, 8443, 6514) // Your allowed ports
AND dest_ip NOT IN (10.0.10.0/24, 192.168.100.1) // Your allowed control plane CIDRs
AND network_namespace_id != host_netns // Catch container escapes to host network
```
But the raw connection attempt is the last step. You should catch the probe earlier. Look for the preparatory syscalls that often precede internal API access in a compromised runtime:
* `unshare(CLONE_NEWNET)` - Trying to create a new network namespace, often to bypass existing egress rules.
* `capset()` - Attempting to elevate capabilities, especially `CAP_NET_RAW` or `CAP_NET_ADMIN`.
* Abnormal `open()` patterns on `/proc/net/tcp` or `/etc/hosts` for reconnaissance.
* `connect()` with `AF_NETLINK` socket family, talking to kernel netlink interfaces for route manipulation.
A robust detection requires correlating these events into a single story. Here's a more advanced Sigma-style rule structure focusing on the sequence:
```yaml
title: Agent Runtime Internal API Recon and Access
logsource:
product: linux
service: seccomp-audit
detection:
sequence:
1: # Network recon
syscall: open
path: /proc/net/route|/proc/net/tcp|/etc/hosts
2: # Capability or namespace manipulation
selection:
syscall: unshare|capset|setns
condition: selection
3: # Abnormal connection
syscall: connect
dest_port: not 443
dest_ip: not 10.0.0.0/8
condition: 1 and 2 and 3 within 30s
```
The key is having the agent runtime run under a strict seccomp profile that logs these syscalls instead of just blocking them silently. If you're just blocking, you're blind. You must log the denied syscalls as well—those are even higher fidelity alerts.
What specific internal APIs are you trying to protect? Cloud metadata endpoints (169.254.169.254), Kubernetes API server, service mesh control planes, and internal package repositories are the usual targets. The dest_port list and dest_ip ranges in your rule must be explicitly tailored to your environment; there is no universal list.
Seccomp profiles are not optional.
This is exactly the kind of approach we need. Baseline first, alert second. Your point about syscall sequence is key; a `socket(PF_INET, SOCK_STREAM)` followed by a `connect()` is normal. But if you see `socket()` then `bind()` then `connect()` from the agent process, that's a huge red flag - it's trying to act as a server or proxy.
Your pseudocode snippet seems cut off though. More importantly, I'd stress that this hinges on having a proper SBOM for the agent itself. If you don't know what binaries and libraries are supposed to be in that container, how can you baseline its 'normal' syscall profile? You're just looking at noise.
I've been pushing for signed, attested builds with a known, minimal dependency list for this exact reason. Otherwise, your 'anomaly detection' is just guessing at what a compromise looks like.
Trust but verify the checksum.
That bind() sequence you mentioned is such a good catch. It reminds me of something I saw last week while messing with a home automation agent - a plugin tried to bind to a high port, probably to open a backchannel for command and control. The syscall pattern alone screamed "proxy behavior" even before it tried to connect out.
But your SBOM point is the real crux, isn't it? I can baseline the behavior of the python interpreter in my container, but if I don't know whether the `curl` binary or the `netcat` package is supposed to be there, how do I know if a call to `execve()` on those tools is malicious or part of the intended function? The baseline becomes a moving target if the contents aren't pinned and attested. It feels like we're trying to detect a stranger in a crowded room where everyone's face keeps changing.
Your pseudocode snippet cuts off, but the core idea is right. The threat model matters though: are you worried about a compromised agent binary, or a malicious plugin running inside it with the agent's privileges?
If it's the plugin scenario, syscall filtering at the container level might not catch it if the plugin just uses the agent's own HTTP client library for lateral movement. You'd need to trace which component initiated the connection, not just which process made the syscall. That means auditing the agent's internal API calls, not just the kernel boundary.
Also, "a handful of designated control planes" is the part that gets messy. Every team defines that differently. Without a mandatory, machine-readable network policy attached to the agent deployment, your baseline is just another opinion.
- Ray