By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.
18px_cookie
e-remove
Blog
Glossary
Customer Story
Video
eBook / Report
Solution Brief

When a Broken Fix Leads to RCE: How We Found CVE-2025-66626 in Argo

Treating a security patch as a signal, not a conclusion, led us to discover how arbitrary file writes became remote code execution in Argo Workflows.

Treating a security patch as a signal, not a conclusion, led us to discover how arbitrary file writes became remote code execution in Argo Workflows.

Treating a security patch as a signal, not a conclusion, led us to discover how arbitrary file writes became remote code execution in Argo Workflows.

Written by
Meenakshi S L
Meenakshi S L
Cris Staicu
Cris Staicu
Published on
December 12, 2025

Treating a security patch as a signal, not a conclusion, led us to discover how arbitrary file writes became remote code execution in Argo Workflows.

Treating a security patch as a signal, not a conclusion, led us to discover how arbitrary file writes became remote code execution in Argo Workflows.

A patch that "should" have been enough

CVE-2025-62156 was supposed to be the end of a dangerous ZipSlip traversal issue in Argo Workflows. The patch tightened validation around path traversal and symlinks during artifact extraction: validate where the symlink points to, ensure it stays inside the working directory, and we’re done.

Except we’re not.

When we reviewed the patch, something felt off: the patch contains one check for handling traditional path traversal and one for symbolic links, the latter looking like a copy of the former. The logic looked sound at a glance, but as we analyzed the patch in more detail, we found that reusing the check for classical path traversal would not always work to protect against symlink traversal. 

That mindset — treating a security patch as a signal, not a conclusion — is what ultimately led us to the discovery of a follow-up security problem, covered by CVE-2025-66626. Below, we also show how an arbitrary file write caused by a ZipSlip can lead to remote code execution.

The core bug: validating one path, using another

The patched Argo extraction logic aims to carefully handle symlinks: it derives a "safe" target under the destination directory, validates that, and only then creates the symlink.

In simplified form, it looks like this:

target := filepath.Join(dest, filepath.Clean(header.Name))
case tar.TypeSymlink:
    linkTarget := filepath.Join(filepath.Dir(target), header.Linkname)
    if !strings.HasPrefix(filepath.Clean(linkTarget), 
    filepath.Clean(dest)+string(os.PathSeparator)) {

        return fmt.Errorf("illegal symlink target: %s -> %s", header.Name, header.Linkname)
    }
    // BUG: we validate linkTarget, but never use it
    if err := os.Symlink(header.Linkname, target); err != nil {
        return err
    }

The intent is good: ensure linkTarget stays under dest, and reject anything that would escape. The problem is that the value being validated (linkTarget) is not the value actually used in os.Symlink (header.Linkname).

If an attacker supplies an absolute header.Linkname such as /etc, then:

  • linkTarget becomes something that looks safe, for example /work/tmp/etc, which passes the prefix check against /work/tmp.
  • But the final os.Symlink call still uses header.Linkname (/etc), creating a symlink that escapes the working directory entirely.

This mismatch between the checked path and the used path is the heart of CVE-2025-66626. It turns a well-intentioned validation step into a misleading safety check, while symlinks to /etc, /tmp, /var, or /usr still go through.

What this means for Argo Workflows users

In practice, this means that even after the 3.7.3 patch, Argo’s artifact extraction could still be abused to write outside the intended working directory.

With carefully constructed tarballs:

  • We were able to target locations like /tmp, /var, /usr, and /etc.
  • We demonstrated writes to these locations from inside the container, using only the workflow’s artifact handling.
  • We confirmed that the behavior was reproducible and not just a theoretical edge case.

The end result is that a workflow processing attacker-controlled archives can be steered into creating symlinks that point at sensitive paths on the filesystem, while the validation layer "thinks" it is enforcing a safe boundary. Using these malicious symlinks, we could write to arbitrary locations in the container.

Patching is not the finish line

Most security processes treat a patch as a binary event:

  • Vulnerability found
  • Patch shipped
  • Back to business as usual

But in complex systems, a patch is really a hypothesis: "We believe this change closes all practical paths to exploitation based on our current understanding."

CVE-2025-66626 shows how that hypothesis can be wrong. A small mismatch between what was validated and what was actually used in one part of the code was enough to leave a critical bypass in place, even after a well-intentioned fix.

The uncomfortable lesson is: if you only patch the specific proof-of-concept that was reported, i.e., overfit the patch, you might still leave room for neighboring variants of the same bug to survive.

The end-to-end exploit

The exploitation flow is surprisingly straightforward once you understand the validation mistake:

  1. Attacker crafts a tarball with malicious symlinks whose source is a local folder, e.g., “work”, and targets (header.Linkname) an absolute path like /etc or /tmp.
  2. Argo’s extraction logic derives and validates a linkTarget under the destination directory using filepath.Join, which produces innocuous-looking paths such as ./work/tmp/etc.
  3. All checks pass because linkTarget appears to stay under ./work/tmp.
  4. The actual symlink creation call still uses the original absolute header.Linkname (/etc, /tmp, etc.), escaping the container’s intended working directory.
  5. Arbitrary writes to sensitive filesystem locations become possible, enabling container compromise and potential privilege escalation.

We validated this with a working exploit that, for example, drops a file into /tmp as a proof of compromise.

From file write primitive to RCE 

Arbitrary file writes are serious enough, but we wanted to further investigate whether this can allow attackers to run arbitrary code. Our first attempts to overwrite files like “~/.zprofile” or “/usr/bin/python” were futile due to permission issues or difficulty in guessing the absolute path to the user’s folder. Moreover, they might rely on container/pod restart to trigger.

After further investigations, we identified the file /var/run/argo/argoexec, which gets executed each time the Kubernetes pod is started. Crucially, this file is initialized before artifact extraction, so our symlink payload can overwrite the initial file with malicious content. Since there are no additional mechanisms to protect this file, e.g., root write permissions, our attack can reliably trigger remote code execution by overwriting this important initialization file. 

We were a bit surprised to hear the maintainer’s view on this attack, once we shared the remote code execution payload with them. They declined our proposal to refine the Linux-level permissions of that file to prevent accidental writes with the following argument: 

“Not really, that file is created by the init container, so happening in this same container. If you can do any of this we're pretty stuffed. Replacing argoexec doesn't really raise the threat compared to replacing other files. Argoexec doesn't gatekeep. Workflows are like k8s Deployments - anyone who can create a workflow is essentially able to run the workload image they want to run anyway. “

Thus, in their threat model, arbitrary file writes are as serious as remote code execution, i.e., of maximum severity. This suggests that the community must reassess this misunderstood link between the two classes of vulnerabilities and their severity levels. 

Takeaways for engineering and security teams

If you’re maintaining systems like Argo Workflows — or anything that interacts with the filesystem — here are the key lessons from CVE-2025-66626:

  • Don’t over-trust framework helpers. filepath.Join is useful, but it doesn’t magically enforce security boundaries. If your threat model assumes otherwise, you have a problem.
  • Validate what you actually use. If you create a symlink with os.Symlink(header.Linkname, ...), then your validation needs to reason about header.Linkname itself, not just a derived path.
  • Don’t assume one patch kills an entire bug class. Shipping a fix for one exploit path doesn’t automatically eliminate all nearby variants of the same underlying mistake.

Code prompt library

40+ AI Prompts for Secure Vibe Coding

Find out More

The Challenge

The Solution

The Impact

Book a Demo

Book a Demo

Book a Demo

Welcome to the resistance
Oops! Something went wrong while submitting the form.

Book a Demo

Book a Demo

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Book a Demo