Skip to content

Commit 28fa70d

Browse files
committed
Harden pwnshop challenge validation and runtimes
1 parent 25334e8 commit 28fa70d

10 files changed

Lines changed: 1030 additions & 40 deletions

File tree

runtime/platform/default.nix

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ let
1515

1616
docker = pkgs.docker;
1717

18+
kataShim = pkgs.writeShellApplication {
19+
name = "containerd-shim-kata-v2";
20+
text = ''
21+
last_arg=
22+
for arg in "$@"; do
23+
last_arg="$arg"
24+
done
25+
26+
# Kata's standalone cleanup helper otherwise detects this
27+
# user-namespaced process as rootless and looks below /run/user/0.
28+
if [[ "$last_arg" == "delete" ]]; then
29+
export XDG_RUNTIME_DIR=/
30+
fi
31+
32+
exec ${pkgs.kata-runtime}/bin/containerd-shim-kata-v2 "$@"
33+
'';
34+
};
35+
1836
jsonFormat = pkgs.formats.json { };
1937

2038
kataKernel = import ./kernel.nix { inherit pkgs name; };
@@ -54,7 +72,10 @@ let
5472
substituteInPlace "$out" \
5573
--replace-fail \
5674
"$annotations_line" \
57-
'enable_annotations = ["enable_iommu", "virtio_fs_extra_args", "kernel_params", "kernel_verity_params", "default_memory"]'
75+
'enable_annotations = ["enable_iommu", "virtio_fs_extra_args", "kernel_params", "kernel_verity_params", "default_memory"]' \
76+
--replace-fail \
77+
'sandbox_cgroup_only = false' \
78+
'sandbox_cgroup_only = true'
5879
'';
5980

6081
dockerDaemonJson = jsonFormat.generate "${name}-docker-daemon.json" {
@@ -72,7 +93,7 @@ let
7293

7394
"runtimes" = {
7495
"kata" = {
75-
"runtimeType" = "${pkgs.kata-runtime}/bin/containerd-shim-kata-v2";
96+
"runtimeType" = "${kataShim}/bin/containerd-shim-kata-v2";
7697
"options" = {
7798
"ConfigPath" = "${kataConfigToml}";
7899
};
@@ -159,9 +180,13 @@ pkgs.writeShellApplication {
159180
docker_service_unit="${name}-docker.service"
160181
containerd_service_unit="${name}-containerd.service"
161182
183+
current_socket_link="$(readlink -f "/run/systemd/system/$docker_socket_unit" 2>/dev/null || true)"
162184
current_service_link="$(readlink -f "/run/systemd/system/$docker_service_unit" 2>/dev/null || true)"
185+
current_containerd_link="$(readlink -f "/run/systemd/system/$containerd_service_unit" 2>/dev/null || true)"
163186
164-
if [[ "$current_service_link" == "${dockerSystemdServiceUnit}" ]] \
187+
if [[ "$current_socket_link" == "${dockerSystemdSocketUnit}" ]] \
188+
&& [[ "$current_service_link" == "${dockerSystemdServiceUnit}" ]] \
189+
&& [[ "$current_containerd_link" == "${containerdServiceUnit}" ]] \
165190
&& DOCKER_HOST="$docker_host" docker info >/dev/null 2>&1; then
166191
emit_environment
167192
exit 0

runtime/platform/kernel.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ let
3333
"FUNCTION_TRACER"
3434
"KPROBE_EVENTS"
3535
"KPROBES"
36+
"KVM"
37+
"KVM_AMD"
38+
"KVM_INTEL"
3639
"PERF_EVENTS"
3740
"PROFILING"
41+
"VIRTUALIZATION"
3842
];
3943
in
4044
pkgs.writeText "${name}.conf" (pkgs.lib.strings.concatLines (map (t: "CONFIG_${t}=y") enable));

runtime/workspace/agent/cmd/workspace-entrypoint/main.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const workspaceProfileBin = "/run/workspace/profile/bin"
2626
const workspaceProfileScript = "/run/workspace/profile/etc/profile.d/99-pwn-workspace.sh"
2727
const workspaceUserRunDir = "/run/workspace/user"
2828
const workspaceServicesDir = "/run/workspace/user/services"
29+
const legacyDojoWorkspaceDir = "/run/dojo/sys/workspace"
30+
const legacyDojoPrivilegedFile = "/run/dojo/sys/workspace/privileged"
2931
const systemProfileScript = "/etc/profile.d/99-pwn-workspace.sh"
3032
const userShell = "/run/workspace/profile/bin/bash"
3133

@@ -141,6 +143,9 @@ func prepareWorkspace(config workspaceConfig) error {
141143
if err := setupRunDirectories(); err != nil {
142144
return err
143145
}
146+
if err := setupLegacyDojoWorkspace(); err != nil {
147+
return err
148+
}
144149
if err := setupSystemEnvironment(); err != nil {
145150
return err
146151
}
@@ -182,6 +187,20 @@ func setupRunDirectories() error {
182187
return linkServiceDefinitions()
183188
}
184189

190+
func setupLegacyDojoWorkspace() error {
191+
privileged := "0\n"
192+
if os.Getenv("PWN_WORKSPACE_PRIVILEGED") == "1" {
193+
privileged = "1\n"
194+
}
195+
if err := os.MkdirAll(legacyDojoWorkspaceDir, 0755); err != nil {
196+
return err
197+
}
198+
if err := os.WriteFile(legacyDojoPrivilegedFile, []byte(privileged), 0644); err != nil {
199+
return err
200+
}
201+
return os.Chmod(legacyDojoPrivilegedFile, 0644)
202+
}
203+
185204
func linkChallengeBin() error {
186205
if _, err := os.Stat(challengeBin); errors.Is(err, os.ErrNotExist) {
187206
return nil

runtime/workspace/packages/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ let
66
pexpect
77
pwntools
88
pycryptodome
9+
pycryptodomex
910
requests
1011
scapy
1112
]

runtime/workspace/packages/extended.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ let
1919
psutil
2020
pwntools
2121
pycryptodome
22+
pycryptodomex
2223
pyroute2
2324
r2pipe
2425
requests

tools/dojo/parse-dojo-yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_valida
2222
ID_REGEX = re.compile(r"^[a-z0-9-]{1,32}$")
2323

2424
INHERIT_KEYS = ["privileged", "interfaces"]
25+
LOCAL_CHALLENGE_CONFIG_KEYS = {"allow_unsupported_tests"}
2526

2627

2728
class Visibility(BaseModel):
@@ -278,6 +279,11 @@ def main() -> int:
278279
if resource.get("type") == "challenge":
279280
challenge_id = resource.get("id")
280281
challenge_data, challenge_description_md = challenge_reads.get(challenge_id, ({}, None))
282+
challenge_data = {
283+
key: value
284+
for key, value in challenge_data.items()
285+
if key not in LOCAL_CHALLENGE_CONFIG_KEYS
286+
}
281287

282288
resource = {**challenge_data, **resource, "type": "challenge", "id": challenge_id}
283289
resource.setdefault(
@@ -300,6 +306,11 @@ def main() -> int:
300306

301307
challenge_id = challenge.get("id")
302308
challenge_data, challenge_description_md = challenge_reads.get(challenge_id, ({}, None))
309+
challenge_data = {
310+
key: value
311+
for key, value in challenge_data.items()
312+
if key not in LOCAL_CHALLENGE_CONFIG_KEYS
313+
}
303314

304315
resource = {**challenge_data, **challenge, "type": "challenge", "id": challenge_id}
305316
resource.setdefault(

0 commit comments

Comments
 (0)