Skip to content

Commit 755d4af

Browse files
committed
Filter dangerous prctl(2) options in seccomp to prevent sandbox weakening
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent 6aa5264 commit 755d4af

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

src/sandlock/_seccomp.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@
8383
# another terminal session (terminal escape attack).
8484
TIOCSTI = 0x5412
8585

86+
# Dangerous prctl(2) options — these allow a sandboxed process to
87+
# weaken its own confinement.
88+
PR_SET_DUMPABLE = 4 # re-enable /proc/pid/mem writes
89+
PR_SET_SECCOMP_OPT = 22 # change seccomp mode from within sandbox
90+
PR_SET_SECUREBITS = 28 # alter LSM security bits
91+
PR_SET_PTRACER = 0x59616d61 # allow arbitrary ptrace attach
92+
_DANGEROUS_PRCTL_OPS = (
93+
PR_SET_DUMPABLE,
94+
PR_SET_SECCOMP_OPT,
95+
PR_SET_SECUREBITS,
96+
PR_SET_PTRACER,
97+
)
98+
8699

87100
# --- Per-architecture configuration ---
88101

@@ -289,6 +302,8 @@ def _build_arg_filters() -> bytes:
289302
Plain forks fall through to the main filter (USER_NOTIF if
290303
clone is in the notif list, or ALLOW if not).
291304
- ioctl(2): Block TIOCSTI (terminal input injection).
305+
- prctl(2): Block dangerous options (PR_SET_DUMPABLE,
306+
PR_SET_SECCOMP, PR_SET_SECUREBITS, PR_SET_PTRACER).
292307
"""
293308
insns = bytearray()
294309

@@ -316,6 +331,18 @@ def _build_arg_filters() -> bytes:
316331
insns += _bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, TIOCSTI, 0, 1)
317332
insns += _bpf_stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ERRNO_EPERM)
318333

334+
# --- prctl: block dangerous options that weaken the sandbox ---
335+
insns += _bpf_stmt(BPF_LD | BPF_W | BPF_ABS, OFFSET_NR)
336+
n_ops = len(_DANGEROUS_PRCTL_OPS)
337+
# if nr != prctl, skip: 1 (load arg0) + n_ops*2 (check+deny each)
338+
skip_count = 1 + n_ops * 2
339+
insns += _bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, _SYSCALL_NR["prctl"], 0, skip_count)
340+
# Load prctl option (arg0)
341+
insns += _bpf_stmt(BPF_LD | BPF_W | BPF_ABS, OFFSET_ARGS0_LO)
342+
for op in _DANGEROUS_PRCTL_OPS:
343+
insns += _bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, op, 0, 1)
344+
insns += _bpf_stmt(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ERRNO_EPERM)
345+
319346
# --- socket: block NETLINK_SOCK_DIAG (hides host socket info) ---
320347
_AF_NETLINK = 16
321348
_NETLINK_SOCK_DIAG = 4

tests/test_seccomp.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
_ARCH_AARCH64,
1313
_ARCH_X86_64,
1414
_CLONE_NS_FLAGS,
15+
_DANGEROUS_PRCTL_OPS,
1516
_MACHINE_TO_ARCH,
1617
_SYSCALL_NR,
1718
_arch,
@@ -100,6 +101,16 @@ def test_clone_ns_flags_defined(self):
100101
def test_tiocsti_defined(self):
101102
assert TIOCSTI == 0x5412
102103

104+
def test_prctl_in_syscall_map(self):
105+
assert "prctl" in _SYSCALL_NR
106+
107+
def test_dangerous_prctl_ops_defined(self):
108+
assert len(_DANGEROUS_PRCTL_OPS) == 4
109+
assert 4 in _DANGEROUS_PRCTL_OPS # PR_SET_DUMPABLE
110+
assert 22 in _DANGEROUS_PRCTL_OPS # PR_SET_SECCOMP
111+
assert 28 in _DANGEROUS_PRCTL_OPS # PR_SET_SECUREBITS
112+
assert 0x59616d61 in _DANGEROUS_PRCTL_OPS # PR_SET_PTRACER
113+
103114

104115
class TestBuildFilter:
105116
def test_empty_deny_list(self):

0 commit comments

Comments
 (0)