.c files, ~60,000 LOC non-vendor)
The ScorpioX codebase is a multi-tool platform encompassing a TUI chat client, container runtime, VM hypervisor, web server, mail server, traffic interceptor, and numerous utility programs. The permission model varies dramatically by component:
| Risk Level | Component | Reason |
|---|---|---|
| 🔴 CRITICAL | scorpiox-traffic | Disables TLS verification globally for child processes |
| 🔴 CRITICAL | scorpiox-sshpass | Disables all SSH host key checking by design |
| 🟠 HIGH | scorpiox-unshare | Container runtime with --privileged mode bypassing user namespaces |
| 🟠 HIGH | scorpiox-vm | Direct KVM access via /dev/kvm ioctls, mmap of guest memory |
| 🟠 HIGH | scorpiox-server | Fork-per-request model executing arbitrary Python scripts via execlp |
| 🟠 HIGH | scorpiox-thunderbolt4 | Requires root for raw BPF Ethernet frame access |
| 🟡 MEDIUM | sx.c (main TUI) | Extensive system()/popen() calls for subcommand dispatch |
| 🟡 MEDIUM | sxmux_session | Daemon fork with Unix domain sockets in /tmp/sxmux |
| 🟢 LOW | scorpiox-podman | Thin wrapper delegating to Podman; no direct privilege use |
| Component | Privilege Required | Reason |
|---|---|---|
scorpiox-thunderbolt4 | Root (sudo) | Opens /dev/bpf* for raw Ethernet frames (line 1410: geteuid() != 0 check) |
scorpiox-unshare --privileged | Root | Skips user namespace, runs container as real root (line 2182: getuid() != 0 check) |
scorpiox-vm | kvm group | Opens /dev/kvm (O_RDWR) for hardware virtualization (line 1997) |
scorpiox-unshare (cgroup memory) | Root or cgroup2 delegation | Creates cgroup dirs at /sys/fs/cgroup/sx- (line 881) |
The majority of tools run as the invoking user:
sx.c (main TUI) — no privilege escalationscorpiox-bash — shell command executor, inherits caller privilegesscorpiox-server — HTTP server binding to user ports (>1024 by default)scorpiox-agent — git clone + dotnet build + launchscorpiox-unshare (default) — uses user namespaces for rootless containerssxmail_*) — user-level mail handlingsetuid/setgid Binary InstallationThe codebase does not install setuid binaries. It references external setuid helpers:
/usr/bin/newuidmap and /usr/bin/newgidmap (system-provided, line 775-776 in scorpiox-unshare.c) for full UID/GID range mapping in user namespaces.| Mechanism | Count | Risk Level |
|---|---|---|
system() | 107 | 🟠 HIGH — shell injection surface |
exec*() family | 66 | 🟡 MEDIUM — direct exec |
popen() | 61 | 🟠 HIGH — shell-mediated pipe |
fork() | 47 | 🟡 MEDIUM — process isolation |
CreateProcessA() (Windows) | 40 | 🟡 MEDIUM |
clone() (Linux namespaces) | 1 | 🟠 HIGH — namespace creation |
system() — Shell Command Injection SurfaceThe most pervasive pattern. Many system() calls construct commands with snprintf():
system(transcript_cmd) — transcript generationsystem(cmd) — configuration commandssystem(cmd) — subcommand dispatchsystem(config_cmd) — config operationssystem(rewind_cmd) — rewind operationssystem(voice_cmd) — voice commandssystem()
sxmail_maildir.c: Lines 704, 711 — mail directory operations
scorpiox-unshare.c: Line 1581 — system("ip link set lo up 2>/dev/null") inside container (low risk — isolated namespace)
Risk: system() invokes /bin/sh -c and is vulnerable to shell metacharacter injection if any argument is user-controlled. While most calls use snprintf() with controlled formats, the attack surface is broad.
Positive Note: sx.c line 662 explicitly uses fork()+execlp() for the editor launch "to avoid shell metacharacter injection" — demonstrating awareness of the issue, though this pattern is not consistently applied.
popen() — Shell-Mediated Pipe Readssx.c lines 1202-1204: popen("git rev-parse --abbrev-ref HEAD ...") — git branch detectionsxtmux_backend_tmux.c line 42: popen("tmux list-sessions ...") — tmux enumerationsxtmux_backend_sxmux.c line 140: popen("which tmux 2>/dev/null") — tmux detectionclone() with Namespace Flagsint clone_flags = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD;
if (!skip_userns)
clone_flags |= CLONE_NEWUSER;
if (use_netns)
clone_flags |= CLONE_NEWNET;
pid_t child = clone(container_main, stack_top, clone_flags, &cfg);
Namespaces created:
CLONE_NEWNS) — alwaysCLONE_NEWPID) — alwaysCLONE_NEWUTS) — alwaysCLONE_NEWUSER) — unless running as rootCLONE_NEWNET) — unless --net hostCLONE_NEWIPC is not used — IPC namespace is shared with host.
fork() + exec*() Patternsfork() → handle_request() → execlp("python3", ...))fork() + execlp("scorpiox-multiplexer", ...)fork() + execvp() for proxied command executionCreateProcessA() (Windows)| Path Pattern | Component | Access Type |
|---|---|---|
~/.scorpiox/ | All tools | R/W — config, sessions, conversations, logs |
.scorpiox/ (CWD-relative) | sx.c, agents | R/W — sessions, hooks, skills |
/tmp/sxmux/ | sxmux_session | R/W — Unix socket dir, PID files |
/tmp/sx_ | Various | R/W — temporary files (voice, emit, slirp sockets) |
/proc/, gid_map, setgroups | scorpiox-unshare | W — user namespace setup |
/sys/fs/cgroup/sx- | scorpiox-unshare | R/W — memory cgroup |
/etc/subuid, /etc/subgid | scorpiox-unshare | R (W if root) — UID mapping |
/dev/kvm | scorpiox-vm | R/W — KVM hypervisor |
/dev/net/tun | scorpiox-vm | R/W — TAP networking |
/dev/bpf | scorpiox-thunderbolt4 | R/W — raw Ethernet |
/var/mail/ | sxmail_* | R/W — mail storage |
/usr/lib/scorpiox/vmlinuz | scorpiox-vm | R — default kernel |
/opt/scorpiox-agent/ | scorpiox-agent | R/W — agent cache |
Inside scorpiox-unshare, the container child sets up an extensive filesystem:
| Mount | Type | Flags | |||
|---|---|---|---|---|---|
/ (root) | MS_REC \ | MS_PRIVATE | Prevents mount propagation to host | ||
| rootfs | overlayfs or bind | MS_NODEV on overlay | |||
/dev | tmpfs | MS_NOSUID | |||
/dev/pts | devpts | MS_NOSUID \ | MS_NOEXEC | ||
/dev/shm | tmpfs | MS_NOSUID \ | MS_NODEV | ||
/proc | procfs | MS_NOSUID \ | MS_NODEV \ | MS_NOEXEC | |
/sys | sysfs | MS_NOSUID \ | MS_NODEV \ | MS_NOEXEC \ | MS_RDONLY |
/tmp | tmpfs | MS_NOSUID \ | MS_NODEV | ||
/run | tmpfs | MS_NOSUID \ | MS_NODEV | ||
/workspace | bind (host dir) | Optional: --bind flag | |||
/persist | bind (host dir) | Optional: --persist flag | |||
/opt/host-bin | bind (host exe dir) | Read-only (host binaries) | |||
| GPU devices | bind | Optional: --gpu flag |
MS_NOSUID, MS_NODEV, and MS_NOEXEC where appropriate.
/tmp usage: Multiple tools create world-readable files in /tmp (/tmp/sx_*, /tmp/.sx-unshare-list.html, /tmp/sxmux/). This is a symlink attack surface on multi-user systems.scorpiox-unshare automatically bind-mounts the host's scorpiox binary directory into containers at /opt/host-bin (read-only). While read-only, this exposes the host binary layout.--gpu is used, NVIDIA device nodes (/dev/nvidia, /dev/dri/) are bind-mounted into the container, expanding the attack surface.The VM module performs extensive KVM operations:
| ioctl | Purpose | Line |
|---|---|---|
KVM_GET_API_VERSION | API compatibility check | 2007 |
KVM_CREATE_VM | Create VM instance | 2021 |
KVM_SET_TSS_ADDR | TSS configuration | 2027 |
KVM_CREATE_IRQCHIP | Interrupt controller | 2030 |
KVM_CREATE_PIT2 | Timer (PIT) | 2034 |
KVM_SET_USER_MEMORY_REGION | Map guest memory (×3) | 2055, 2067, 2085 |
KVM_CREATE_VCPU | Create virtual CPU | 2096 |
KVM_GET_VCPU_MMAP_SIZE | vCPU run struct size | 2102 |
KVM_GET_SUPPORTED_CPUID | CPU feature detection | 2123 |
KVM_SET_CPUID2 | Set guest CPUID | 2144 |
KVM_GET_SREGS/KVM_SET_SREGS | Segment registers | 2384, 2443 |
KVM_SET_REGS | General-purpose registers | 2455 |
KVM_IRQ_LINE | Inject interrupts | 917, 1463, 1538, 1897, 1969 |
// Line 2038: Guest memory allocation - RW, no exec
g_guest_mem = mmap(NULL, g_guest_mem_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
// Line 2108: KVM run struct mapping
g_kvm_run = mmap(NULL, (size_t)mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_vcpu_fd, 0);
Positive: Guest memory is mapped as PROT_READ | PROT_WRITE without PROT_EXEC — no executable host pages.
// Line 1662: Open TUN device
int fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
// Line 1672: Configure TAP interface
ioctl(fd, TUNSETIFF, &ifr);
// Lines 1687-1703: Network interface configuration
ioctl(sock, SIOCGIFFLAGS, &ifr_up);
ioctl(sock, SIOCSIFFLAGS, &ifr_up);
ioctl(sock, SIOCBRADDIF, &ifr_br);
Opens /dev/bpf* devices for raw Ethernet frame transmission — requires root.
scorpiox-server.c: SIGCHLD handler for reaping forked children (line 2800)scorpiox-vm.c: Signal handlers for clean VM shutdownscorpiox-unshare.c: Parent catches signals to forward to container childSIGILL handler for CPU feature detection (SHA256/SHA512)SIGPIPE set to SIG_IGN for network socketsscorpiox-unshare — Linux Container Runtime| Namespace | Used | Notes |
|---|---|---|
User (CLONE_NEWUSER) | ✅ | Skipped when running as root |
Mount (CLONE_NEWNS) | ✅ | Always — private mount tree |
PID (CLONE_NEWPID) | ✅ | Always — isolated PID space |
UTS (CLONE_NEWUTS) | ✅ | Always — custom hostname |
Network (CLONE_NEWNET) | ✅ | Default; --net host disables |
| IPC | ❌ | Not isolated — shares host IPC |
| Cgroup | ❌ | Not isolated — v2 memory limits only |
MS_REC | MS_PRIVATE on root prevents mount propagation to hostCLONE_NEWNET + slirp4netns for userspace networkingadd_hostfwd command)--net host bypasses all network isolation--memory flag: cgroup v2 memory.max enforcementclearenv() wipes inherited environment (line 1546)PATH, HOME, TERM, LANG, USERSCORPIOX_BRANCH, CONTAINER_PACKAGES explicitly-e KEY=VALUE vars applied after sanitizationscorpiox-vm — KVM Virtual Machine/dev/kvm access required — enforced by kernelscorpiox-wsl — WSL2 Executionwsl.exe --distribution for executionchroot $OV/merged /bin/sh -c '...')scorpiox-podman — Podman Wrapperpodman run command and calls system(cmd)| Gap | Impact | Recommendation |
|---|---|---|
No IPC namespace in scorpiox-unshare | Container can access host System V IPC (shared memory, semaphores, message queues) | Add CLONE_NEWIPC to clone flags |
| No cgroup namespace isolation | Container can see host cgroup hierarchy | Add CLONE_NEWCGROUP |
| No seccomp filter | Container processes can invoke all syscalls | Apply seccomp-bpf filter for container child |
| No capability dropping | Root-in-userns retains all user-namespace capabilities | Drop unnecessary capabilities with prctl(PR_SET_KEEPCAPS) + capset() |
/proc/sys/kernel/random synthetic mount | Only provides static file contents (uuid, boot_id) — not cryptographically valid | Document limitation; consider bind-mounting from host |
/tmp/sxmux/ (defined in sxmux_session.h line 26)AF_UNIX, SOCK_STREAM (lines 1573, 1718)bind() + listen() + accept() loopconnect() to named socket/tmp/sxmux/ with standard permissions. On multi-user systems, another user could potentially connect.
\\.\pipe\sxmux- (line 22)CreateNamedPipeA() (line 1976)CreateFileA() + WaitNamedPipeA()Used extensively for parent-child communication:
| Component | Purpose |
|---|---|
scorpiox-unshare.c | sync_pipe — UID map readiness signal (line 1787) |
scorpiox-unshare.c | net_pipe — network namespace readiness (line 1805) |
scorpiox-server.c | stdin_pipe/stdout_pipe — CGI I/O (line 1660) |
scorpiox-bash.c | stdin/stdout/stderr pipes to child shell |
forkpty() / manual PTY allocation for full terminal emulation.
SIGCHLD — child reaping in server and container parentSIGPIPE — ignored in network code (mbedtls, server)SIGILL — CPU feature probing (mbedtls SHA)scorpiox-traffic.c)Lines 975-980 in child process before execvp():
setenv("NODE_TLS_REJECT_UNAUTHORIZED", "0", 1);
setenv("REQUESTS_CA_BUNDLE", custom_ca_path, 1);
setenv("CURL_CA_BUNDLE", custom_ca_path, 1);
setenv("SSL_CERT_FILE", custom_ca_path, 1);
setenv("PYTHONHTTPSVERIFY", "0", 1);
setenv("CURLOPT_SSL_VERIFYPEER", "0", 1);
Also sets HTTP_PROXY/HTTPS_PROXY to local MITM proxy (lines 942-945).
scorpiox-traffic has TLS verification completely disabled. This is by design (traffic inspection tool), but represents a significant security boundary.
scorpiox-traffic.c)Lines 942-945: Sets HTTP_PROXY, HTTPS_PROXY, http_proxy, https_proxy to local MITM proxy endpoint.
scorpiox-unshare.c)Lines 1546-1554: clearenv() followed by controlled setenv():
clearenv();
setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
setenv("HOME", "/root", 1);
// ... controlled vars only
Positive: This is a security-positive pattern — sanitizes inherited environment.
scorpiox-server.c)Lines 1692-1725: Sets CGI standard environment variables from HTTP request data:
setenv("REQUEST_METHOD", req->method, 1);
setenv("CONTENT_TYPE", req->content_type, 1);
setenv("HTTP_COOKIE", req->cookie, 1);
setenv("QUERY_STRING", req->query, 1);
setenv("HTTP_AUTHORIZATION", req->authorization, 1);
Also line 1504: All HTTP headers mapped to HTTP_* environment variables via set_cgi_header_envs().
SXMUX_INIT_CMD — multiplexer session init command (temporary, unset after use)TRAFFIC_OUTDIR, TRAFFIC_PORT — traffic capture configVNC_PORT — container VNC port| Pattern | Location | Assessment | ||
|---|---|---|---|---|
| User namespaces for rootless containers | scorpiox-unshare.c | ✅ Excellent — no root needed for basic containers | ||
O_CLOEXEC on sensitive file descriptors | scorpiox-vm.c (KVM, TUN) | ✅ Good — prevents FD leaks to children | ||
MS_NOSUID | MS_NODEV | MS_NOEXEC mount flags | scorpiox-unshare.c | ✅ Good — restricts container mount capabilities |
clearenv() before container exec | scorpiox-unshare.c | ✅ Good — clean environment | ||
fork()+execlp() for editor (avoiding system()) | sx.c line 662 | ✅ Good — prevents shell injection | ||
| Read-only host binary mount | scorpiox-unshare.c | ✅ Good — host binaries protected | ||
| EACCES guidance for KVM | scorpiox-vm.c | ✅ Good — guides users to kvm group instead of root |
| Violation | Location | Recommendation |
|---|---|---|
107 system() calls across codebase | Various | Replace with fork()+exec() where possible; critical when arguments contain user input |
61 popen() calls | Various | Replace with pipe+fork+exec for safety |
--privileged mode skips user namespace | scorpiox-unshare.c line 2181 | Add warning banner; require explicit --i-know-what-im-doing flag |
| No seccomp profile for containers | scorpiox-unshare.c | Apply minimal seccomp-bpf filter |
| No capability bounding set | scorpiox-unshare.c | Drop capabilities after namespace setup |
| SSH host key checking disabled | scorpiox-sshpass.c line 42 | Add prominent warning; never enable by default |
| TLS verification disabled in child env | scorpiox-traffic.c | Scope to proxy CA only; don't blanket-disable all verification |
World-accessible /tmp files | Multiple tools | Use mkdtemp() or $XDG_RUNTIME_DIR with restrictive permissions |
| No IPC namespace in containers | scorpiox-unshare.c | Add CLONE_NEWIPC |
| No resource limits beyond optional memory | scorpiox-unshare.c | Add PID limits, CPU quotas |
| Risk | Likelihood | Impact | Mitigation Status |
|---|---|---|---|
Shell injection via system()/popen() | Medium | High | ⚠️ Partial — some paths use fork+exec |
| Container escape via missing IPC namespace | Low | Critical | ❌ Not mitigated |
| Container escape via missing seccomp | Low | Critical | ❌ Not mitigated |
TLS MITM on scorpiox-traffic children | Medium | High | ❌ By design — needs scope limitation |
SSH credential exposure (scorpiox-sshpass) | High | Medium | ⚠️ Documented as "dangerous" |
Privilege escalation via --privileged | Low | Critical | ⚠️ Requires explicit flag + root |
| Tmpfile symlink attacks | Low | Medium | ❌ Not mitigated |
| CGI env var injection | Medium | Medium | ⚠️ Standard CGI practice |
| KVM escape via guest code | Very Low | Critical | ✅ Hardware-enforced (KVM) |
Unix socket hijacking (/tmp/sxmux/) | Low | Medium | ❌ No socket authentication |
system()/popen() with fork()+exec() in all paths where arguments may contain user-controlled data (estimated 40+ call sites at risk).CLONE_NEWIPC and CLONE_NEWCGROUP to scorpiox-unshare clone flags for complete namespace isolation.mount, reboot, kexec_load, open_by_handle_at, init_module.capset() — only retain those needed for container operation./tmp files to user-owned directories: Use $XDG_RUNTIME_DIR or $HOME/.scorpiox/run/ instead of /tmp for sockets and temporary files.scorpiox-traffic: Instead of NODE_TLS_REJECT_UNAUTHORIZED=0, only set *_CA_BUNDLE to the custom CA that includes both system + proxy CAs. Remove the blanket TLS disable variables.SO_PEERCRED to verify connecting client UID matches server UID for sxmux sessions.| File | system() | popen() | fork() | exec() | CreateProcess |
|---|---|---|---|---|---|
| sx.c | 8 | 3 | 1 | 1 | — |
| scorpiox-server.c | — | — | 3 | 2 | — |
| scorpiox-unshare.c | 1 | — | — | 3 | — |
| scorpiox-vm.c | — | — | — | — | — |
| scorpiox-bash.c | — | — | 1 | 1 | 1 |
| scorpiox-traffic.c | — | — | 2 | 1 | — |
| scorpiox-wsl.c | — | — | — | — | 2+ |
| sxmux_session.c | — | — | 1 | — | 1 |
| sxtmux_backend_.c | 4 | 2 | 1 | 1 | — |
| scorpiox-hook.c | many | — | many | many | — |
| scorpiox-agent.c | many | — | — | many | — |
| sxmail_maildir.c | 2 | — | — | — | — |
| (others) | distributed | distributed | distributed | distributed | distributed |
| Namespace | scorpiox-unshare | Docker (default) | Podman (rootless) |
|---|---|---|---|
| User | ✅ | ❌ (opt-in) | ✅ |
| Mount | ✅ | ✅ | ✅ |
| PID | ✅ | ✅ | ✅ |
| UTS | ✅ | ✅ | ✅ |
| Network | ✅ | ✅ | ✅ |
| IPC | ❌ | ✅ | ✅ |
| Cgroup | ❌ | ✅ | ✅ |
| Seccomp | ❌ | ✅ | ✅ |
| AppArmor/SELinux | ❌ | ✅ (if available) | ✅ (if available) |