Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 54 additions & 24 deletions runtime/workspace/agent/cmd/workspace-entrypoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import (

const agentUID = 1000
const agentGID = 1000
const challengeBin = "/challenge/bin"
const challengeRunDir = "/run/challenge"
const challengeRunBin = "/run/challenge/bin"
const workspaceRunDir = "/run/workspace"
const workspaceProfile = "/run/workspace/profile"
const workspaceUserRunDir = "/run/workspace/user"
const workspaceServicesDir = "/run/workspace/user/services"

Expand Down Expand Up @@ -142,11 +146,13 @@ func prepareWorkspace(config workspaceConfig) error {
}

func setupRunDirectories() error {
if err := os.MkdirAll(workspaceRunDir, 0755); err != nil {
return err
}
if err := os.Chmod(workspaceRunDir, 0755); err != nil {
return err
for _, directory := range []string{challengeRunDir, workspaceRunDir} {
if err := os.MkdirAll(directory, 0755); err != nil {
return err
}
if err := os.Chmod(directory, 0755); err != nil {
return err
}
}
for _, directory := range []string{workspaceUserRunDir, workspaceServicesDir} {
if err := os.MkdirAll(directory, 0700); err != nil {
Expand All @@ -159,9 +165,49 @@ func setupRunDirectories() error {
return err
}
}
if err := linkChallengeBin(); err != nil {
return err
}
Comment on lines +168 to +170

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve .init-created challenge bins

Because linkChallengeBin() runs during setupRunDirectories() before .init is executed, /run/challenge/bin is never created when a challenge creates /challenge/bin from .init instead of shipping it in the image. For challenges based on the generic challenges/common/Dockerfile.j2, which does not itself add /challenge/bin back to the image PATH, those runtime-created tools were previously found through the hard-coded /challenge/bin entry but now remain unreachable for the whole session; create the symlink unconditionally or refresh it after runInit().

Useful? React with 👍 / 👎.

if err := linkWorkspaceProfile(); err != nil {
return err
}
return linkServiceDefinitions()
}

func linkChallengeBin() error {
if _, err := os.Stat(challengeBin); errors.Is(err, os.ErrNotExist) {
return nil
} else if err != nil {
return err
}
if err := os.Remove(challengeRunBin); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return os.Symlink(challengeBin, challengeRunBin)
}

func linkWorkspaceProfile() error {
target, err := workspaceProfileTarget()
if err != nil {
return err
}
if err := os.Remove(workspaceProfile); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return os.Symlink(target, workspaceProfile)
}

func workspaceProfileTarget() (string, error) {
if len(os.Args) > 0 && filepath.IsAbs(os.Args[0]) {
return filepath.Clean(filepath.Join(filepath.Dir(os.Args[0]), "..")), nil
}
executable, err := os.Executable()
if err != nil {
return "", err
}
return filepath.Clean(filepath.Join(filepath.Dir(executable), "..")), nil
}

func runChallengeInit() error {
if err := runInit(); err != nil {
return fmt.Errorf("run .init: %w", err)
Expand Down Expand Up @@ -306,27 +352,11 @@ func linkServiceDefinitions() error {
}

func serviceDefinitionsDir() (string, error) {
candidates := make([]string, 0, 3)
if workspace := os.Getenv("PWN_WORKSPACE"); workspace != "" {
candidates = append(candidates, filepath.Join(workspace, "share", "workspace", "services"))
}
if len(os.Args) > 0 && filepath.IsAbs(os.Args[0]) {
candidates = append(candidates, filepath.Join(filepath.Dir(os.Args[0]), "..", "share", "workspace", "services"))
}
executable, err := os.Executable()
if err != nil {
directory := filepath.Join(workspaceProfile, "share", "workspace", "services")
if _, err := os.Stat(directory); err != nil {
return "", err
}
candidates = append(candidates, filepath.Join(filepath.Dir(executable), "..", "share", "workspace", "services"))

for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil {
return candidate, nil
} else if !errors.Is(err, os.ErrNotExist) {
return "", err
}
}
return "", fmt.Errorf("workspace service definitions not found in %v", candidates)
return directory, nil
}

func writeFlag(flag string) error {
Expand Down
21 changes: 19 additions & 2 deletions tools/pwnshop/src/pwnshop/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import base64
import contextlib
import json
import logging
import os
import pathlib
Expand Down Expand Up @@ -85,6 +86,20 @@ def ignore_git_crypt(current, names):
return rendered_directory


def image_path(challenge_image: str) -> str:
image_env = json.loads(
subprocess.check_output(
["docker", "image", "inspect", "--format={{json .Config.Env}}", challenge_image],
text=True,
)
)
for entry in image_env or []:
name, separator, value = entry.partition("=")
if separator and name == "PATH":
return value
return ""


@contextlib.contextmanager
def run_challenge(
challenge_path: pathlib.Path,
Expand Down Expand Up @@ -115,6 +130,9 @@ def run_challenge(
logger.debug("container runtime options for %s: %s", challenge_path, runtime_options)
if volumes:
logger.debug("mounting volumes: %s", volumes)
container_path = ":".join(
path for path in ["/run/challenge/bin", "/run/workspace/profile/bin", image_path(challenge_image)] if path
)
container = None
try:
container = subprocess.check_output(
Expand All @@ -125,8 +143,7 @@ def run_challenge(
"--user=0:0",
f"--env=PWN_FLAG={flag}",
"--env=PWN_USER=hacker",
f"--env=PWN_WORKSPACE={workspace}",
f"--env=PATH={workspace}/bin:/challenge/bin:/run/workspace/bin:/run/dojo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
f"--env=PATH={container_path}",
"--volume=/nix:/nix:ro",
*runtime_options,
*[f"--volume={volume}:{volume}:ro" for volume in (volumes or [])],
Expand Down