Skip to content
This repository was archived by the owner on Mar 14, 2026. It is now read-only.

Commit ed25ef5

Browse files
koki-developclaude
andcommitted
Add 17 new sandbox security exploit tests (batch 2)
Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bd4e967 commit ed25ef5

18 files changed

Lines changed: 724 additions & 0 deletions

AGENTS.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ codize run --json tests/rlimit_override.py # Resource limit override test
122122
codize run --json tests/signal_resistance.py # Signal resistance test
123123
codize run --json tests/readonly_write.py # Read-only filesystem write test
124124
codize run --json tests/proc_info_leak.py # /proc information leakage test
125+
codize run --json tests/unix_socket.py # Unix domain socket cross-sandbox test
126+
codize run --json tests/sysv_shm.py # SysV shared memory cross-sandbox test
127+
codize run --json tests/sysv_msg.py # SysV message queue and semaphore cross-sandbox test
128+
codize run --json tests/ptrace_attach.py # ptrace attach process manipulation test
129+
codize run --json tests/mount_escape.py # mount syscall filesystem escape test
130+
codize run --json tests/chroot_escape.py # Double chroot escape technique test
131+
codize run --json tests/namespace_escape.py # unshare namespace escape test
132+
codize run --json tests/thread_bomb.py # Thread bomb resource exhaustion test
133+
codize run --json tests/fd_exhaustion.py # File descriptor exhaustion test
134+
codize run --json tests/pipe_exhaustion.py # Pipe and kernel buffer exhaustion test
135+
codize run --json tests/core_dump.py # Core dump information leak test
136+
codize run --json tests/sysfs_info.py # /sys filesystem information leak test
137+
codize run --json tests/timing_side_channel.py # CPU cache timing side channel test
138+
codize run --json tests/kernel_module.py # Kernel module loading test
139+
codize run --json tests/raw_syscall.py # Raw restricted syscall invocation test
140+
codize run --json tests/cross_process_kill.py # Cross-process signal delivery test
141+
codize run --json tests/raw_socket.py # Raw and packet socket creation test
125142
```
126143
127144
Package tests are automatically executed via GitHub Actions (`package-pr.yaml`).

tests/chroot_escape.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Description
3+
Using the classic double-chroot escape technique: create a new chroot,
4+
then chdir to the original root via relative path traversal to break
5+
out of the sandbox chroot
6+
7+
The sandbox should prevent chroot by running as an unprivileged user
8+
without CAP_SYS_CHROOT capability
9+
10+
"""
11+
12+
import ctypes
13+
import ctypes.util
14+
import os
15+
16+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
17+
18+
try:
19+
os.makedirs("/tmp/escape", exist_ok=True)
20+
except OSError:
21+
pass
22+
23+
ret = libc.chroot(b"/tmp/escape")
24+
if ret == 0:
25+
try:
26+
os.chdir("/" + "../" * 20)
27+
ret2 = libc.chroot(b".")
28+
if ret2 == 0:
29+
try:
30+
entries = os.listdir("/")
31+
if "etc" in entries and "home" in entries:
32+
print(f"CHROOT ESCAPED - see real root: {entries[:10]} (CRITICAL)")
33+
except:
34+
pass
35+
except:
36+
pass

tests/core_dump.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
Description
3+
Triggering a crash to generate a core dump file that might contain
4+
sensitive information from the process memory such as environment
5+
variables or runtime secrets
6+
7+
The sandbox should prevent core dump generation via RLIMIT_CORE=0,
8+
ensuring no core files are written to disk
9+
10+
"""
11+
12+
import os
13+
import ctypes
14+
import ctypes.util
15+
import resource
16+
17+
soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
18+
print(f"RLIMIT_CORE: soft={soft}, hard={hard}")
19+
20+
if hard > 0:
21+
print(f"WARNING: RLIMIT_CORE hard limit is {hard}, core dumps may be possible")
22+
23+
try:
24+
resource.setrlimit(resource.RLIMIT_CORE, (hard + 1, hard + 1))
25+
new_soft, new_hard = resource.getrlimit(resource.RLIMIT_CORE)
26+
print(f"RAISED RLIMIT_CORE to {new_hard} (CRITICAL)")
27+
except (ValueError, resource.error):
28+
pass
29+
30+
try:
31+
with open("/proc/sys/kernel/core_pattern", "r") as f:
32+
pattern = f.read().strip()
33+
print(f"core_pattern: {pattern}")
34+
except (PermissionError, FileNotFoundError):
35+
pass
36+
37+
secret = "SENSITIVE_DATA_LEAK_TEST_12345"
38+
39+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
40+
try:
41+
ctypes.string_at(0, 1)
42+
except:
43+
pass
44+
45+
for path in ["/tmp/core", "/box/core", "core", f"/tmp/core.{os.getpid()}"]:
46+
if os.path.exists(path):
47+
size = os.path.getsize(path)
48+
print(f"Core dump found at {path}, size={size} (CRITICAL)")

tests/cross_process_kill.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Description
3+
Sending signals to processes outside the sandbox by targeting PID 1
4+
and other process IDs, attempting to disrupt the sandbox keeper or
5+
other system processes
6+
7+
The sandbox should confine signal delivery within the PID namespace,
8+
making it impossible to signal processes outside the namespace
9+
10+
"""
11+
12+
import os
13+
import signal
14+
15+
targets = [
16+
(1, signal.SIGTERM, "PID 1 (namespace init)"),
17+
(1, signal.SIGKILL, "PID 1 (namespace init)"),
18+
(2, signal.SIGKILL, "PID 2"),
19+
]
20+
21+
for pid, sig, desc in targets:
22+
if pid == os.getpid():
23+
continue
24+
try:
25+
os.kill(pid, sig)
26+
print(f"Signal {sig} to {desc}: DELIVERED")
27+
except ProcessLookupError:
28+
pass
29+
except PermissionError:
30+
pass
31+
except OSError:
32+
pass
33+
34+
for pid in [1000, 2000, 5000, 10000, 32000]:
35+
try:
36+
os.kill(pid, 0)
37+
print(f"PID {pid} EXISTS inside namespace")
38+
except ProcessLookupError:
39+
pass
40+
except PermissionError:
41+
print(f"PID {pid} exists but permission denied")
42+
except OSError:
43+
pass

tests/fd_exhaustion.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Description
3+
Opening the maximum number of file descriptors to exhaust the
4+
per-process fd table, potentially causing denial of service for
5+
the sandbox infrastructure that needs fds to function
6+
7+
The sandbox should enforce RLIMIT_NOFILE to cap the number of
8+
open file descriptors per process
9+
10+
"""
11+
12+
import os
13+
14+
fds = []
15+
i = 0
16+
while True:
17+
try:
18+
fd = os.open("/dev/null", os.O_RDONLY)
19+
fds.append(fd)
20+
i += 1
21+
except OSError:
22+
break
23+
24+
print(f"Opened {i} file descriptors before hitting limit")
25+
26+
if i > 10000:
27+
print(f"WARNING: Opened {i} fds, limit may be too high")
28+
29+
for fd in fds:
30+
try:
31+
os.close(fd)
32+
except:
33+
pass

tests/kernel_module.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Description
3+
Attempting to load kernel modules via init_module and finit_module
4+
syscalls, which would allow arbitrary kernel code execution
5+
6+
The sandbox should prevent module loading by running as an
7+
unprivileged user without CAP_SYS_MODULE capability
8+
9+
"""
10+
11+
import ctypes
12+
import ctypes.util
13+
import os
14+
15+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
16+
17+
SYS_init_module = 175
18+
SYS_finit_module = 313
19+
SYS_delete_module = 176
20+
21+
syscall = libc.syscall
22+
syscall.restype = ctypes.c_long
23+
24+
dummy_module = b"\x00" * 64
25+
ret = syscall(SYS_init_module, dummy_module, len(dummy_module), b"")
26+
if ret == 0:
27+
print("init_module SUCCEEDED (CRITICAL)")
28+
29+
try:
30+
fd = os.open("/dev/null", os.O_RDONLY)
31+
ret = syscall(SYS_finit_module, fd, b"", 0)
32+
if ret == 0:
33+
print("finit_module SUCCEEDED (CRITICAL)")
34+
os.close(fd)
35+
except OSError:
36+
pass
37+
38+
ret = syscall(SYS_delete_module, b"dummy_module", 0)
39+
if ret == 0:
40+
print("delete_module SUCCEEDED (CRITICAL)")

tests/mount_escape.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Description
3+
Using the mount syscall to attempt mounting filesystems such as proc,
4+
sysfs, or bind mounts to access data outside the chroot boundary
5+
6+
The sandbox should prevent mount operations by running as an
7+
unprivileged user without CAP_SYS_ADMIN capability
8+
9+
"""
10+
11+
import ctypes
12+
import ctypes.util
13+
import os
14+
15+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
16+
17+
try:
18+
os.makedirs("/tmp/mnt", exist_ok=True)
19+
except OSError:
20+
pass
21+
22+
mount_attempts = [
23+
(b"none", b"/tmp/mnt", b"proc", 0, b"", "mount proc"),
24+
(b"none", b"/tmp/mnt", b"sysfs", 0, b"", "mount sysfs"),
25+
(b"none", b"/tmp/mnt", b"tmpfs", 0, b"", "mount tmpfs"),
26+
(b"/", b"/tmp/mnt", b"none", 4096, b"", "bind mount /"),
27+
]
28+
29+
for source, target, fstype, flags, data, desc in mount_attempts:
30+
ret = libc.mount(source, target, fstype, flags, data)
31+
if ret == 0:
32+
print(f"{desc}: SUCCEEDED (CRITICAL)")
33+
try:
34+
entries = os.listdir("/tmp/mnt")
35+
print(f" Contents: {entries[:5]}")
36+
except:
37+
pass
38+
libc.umount(target)

tests/namespace_escape.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Description
3+
Using unshare syscall to create new user, mount, or PID namespaces
4+
in an attempt to gain elevated privileges or bypass sandbox
5+
constraints
6+
7+
The sandbox should prevent unprivileged namespace creation, either
8+
via lacking capabilities or kernel settings restricting
9+
user namespace creation
10+
11+
"""
12+
13+
import ctypes
14+
import ctypes.util
15+
16+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
17+
18+
CLONE_NEWUSER = 0x10000000
19+
CLONE_NEWNS = 0x00020000
20+
CLONE_NEWPID = 0x20000000
21+
CLONE_NEWNET = 0x40000000
22+
CLONE_NEWIPC = 0x08000000
23+
24+
namespaces = [
25+
("CLONE_NEWUSER", CLONE_NEWUSER),
26+
("CLONE_NEWNS", CLONE_NEWNS),
27+
("CLONE_NEWPID", CLONE_NEWPID),
28+
("CLONE_NEWNET", CLONE_NEWNET),
29+
("CLONE_NEWIPC", CLONE_NEWIPC),
30+
]
31+
32+
for name, flag in namespaces:
33+
ret = libc.unshare(flag)
34+
if ret == 0:
35+
print(f"unshare({name}) SUCCEEDED (CRITICAL)")
36+
37+
ret = libc.unshare(CLONE_NEWUSER | CLONE_NEWNS)
38+
if ret == 0:
39+
print("unshare(NEWUSER|NEWNS) SUCCEEDED (CRITICAL)")

tests/pipe_exhaustion.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Description
3+
Creating a massive number of pipes and filling their buffers to
4+
exhaust kernel memory allocated for pipe buffers
5+
6+
The sandbox should enforce RLIMIT_NOFILE to limit the number of
7+
pipes, and the memory cgroup should prevent kernel buffer
8+
exhaustion from affecting the host
9+
10+
"""
11+
12+
import os
13+
14+
pipes = []
15+
i = 0
16+
while True:
17+
try:
18+
r, w = os.pipe()
19+
pipes.append((r, w))
20+
i += 1
21+
try:
22+
os.write(w, b'x' * 65536)
23+
except:
24+
pass
25+
except OSError:
26+
break
27+
28+
print(f"Created {i} pipes before hitting limit")
29+
30+
for r, w in pipes:
31+
try:
32+
os.close(r)
33+
os.close(w)
34+
except:
35+
pass

tests/ptrace_attach.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Description
3+
Using ptrace to attach to other processes visible in the sandbox
4+
and attempt to read or manipulate their memory
5+
6+
The sandbox should prevent ptrace by running as an unprivileged user
7+
without CAP_SYS_PTRACE, and the PID namespace limits visible targets
8+
9+
"""
10+
11+
import ctypes
12+
import ctypes.util
13+
import os
14+
15+
libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
16+
17+
PTRACE_ATTACH = 16
18+
PTRACE_PEEKDATA = 2
19+
PTRACE_DETACH = 17
20+
21+
targets = [1, os.getppid()]
22+
23+
for pid in targets:
24+
if pid == os.getpid() or pid <= 0:
25+
continue
26+
ret = libc.ptrace(PTRACE_ATTACH, pid, None, None)
27+
if ret == 0:
28+
print(f"ptrace ATTACH to PID {pid} SUCCEEDED (CRITICAL)")
29+
libc.ptrace(PTRACE_DETACH, pid, None, None)
30+
31+
# Also try to ptrace our own child
32+
pid = os.fork()
33+
if pid == 0:
34+
import time
35+
time.sleep(1)
36+
os._exit(0)
37+
else:
38+
import time
39+
time.sleep(0.1)
40+
ret = libc.ptrace(PTRACE_ATTACH, pid, None, None)
41+
if ret == 0:
42+
libc.ptrace(PTRACE_DETACH, pid, None, None)
43+
try:
44+
os.waitpid(pid, 0)
45+
except:
46+
pass

0 commit comments

Comments
 (0)