Full Report
This research was done in January of 2023 but was published recently. in September of 2023, Nathan Kirkland and I decided to do some auditing of the Gravity Bridge ourselves. So, interesting seeing the crossover here! The Gravity Bridge is a Ethereum to Cosmos bridge for various assets. The Gravity Bridge is compromised of three parts: Ethereum smart contracts, an orchestrator which acts as both a relayer and signer and the Cosmos blockchain. When going from Ethereum to Cosmos, the sendToCronos function is called, which triggers handling within the orchestrator to vote on the event occurring on Cosmos. When going the other direction, the message MsgSendToEthereum is sent. Once these are batched up, the orchestrator will query for transactions and sign it with their key. Once enough signatures have been found, it is relayed to Ethereum to call the submitBatch() function. When we say bridge we really mean lock the original token in the contract and create a representation of the original token on the other chain. As a result, there is a function called deployERC20() to create an Ethereum representation of a Cosmos asset. Within the Gravity Bridge, this will trigger an event to store the token information locally. When processing events, the lastEventNonce must increase monotonically in order to be processed. If this isn't the case in the orchestrator, then it will not be processed. So, can we break this invariant? By creating a token with too many characters in the name, an error will be returned. Now, all transactions will not be processed by the orchestrator, leading a denial of service. The Gravity Bridge had many extra checks to ensure that the system wasn't in a bad place. If something weird happened then it would simply shut off as a defense mechanism. In particular, if the function k.Handle(xCtx, event) for a given event ever failed then the bridge would disable/turn itself off. So, the author decided to find a way to trigger this! For the handling of a token send from Ethereum to Cosmos the function SendToCosmosEvent is called. Users can send arbitrary tokens with arbitrary values so this can be interesting to play with. One of the validations is that the token supply is not larger than 256 bits. If that's true, then the program errors out! Since we can create our own token, we can trigger this. Simply send more than 2 ** 256 of a given token and the bridge will lock itself up. Both of these were rated as medium severity issues, which I disagree with. I feel these are high, considering they turn everything off for a while. Overall, it's a great post! Personally, I didn't consider looking for functionality to hit the disable bridge code. So, it's cool to see research being done on similar targets to see ideas, setups and things to improve in the future.
Analysis Summary
# Vulnerability: Gravity Bridge Logic Errors Leading to Bridge Halting (DoS)
## CVE Details
- **CVE ID**: Not explicitly assigned in the report.
- **CVSS Score**: Not provided (Researcher disagrees with the "Medium" rating by the vendor and suggests **High** due to total availability loss).
- **CWE**: CWE-20 (Improper Input Validation), CWE-755 (Improper Handling of Exceptional Conditions).
## Affected Systems
- **Products**: Cronos Gravity Bridge (Ethereum to Cosmos bridge).
- **Versions**: Found against commit `155515ccdfc03a1376385113d2fc6919510d25ac`.
- **Configurations**: Default configuration of the `x/gravity` Cosmos module and associated orchestrators.
## Vulnerability Description
Two distinct logical flaws allow an attacker to disrupt bridge operations:
1. **ERC-20 Metadata Invariant Breach**: When a user calls `deployERC20()` to create an Ethereum representation of a Cosmos asset, the orchestrator stores token info locally. If a token is created with a name or symbol exceeding specific character limits, the Cosmos module returns an error. Because the `lastEventNonce` must increase monotonically, this failed event blocks all subsequent events from being processed by the orchestrator, effectively halting Ethereum-to-Cosmos transfers.
2. **Malicious Total Supply Deactivation**: The bridge includes a fail-safe mechanism: if `k.Handle(xCtx, event)` returns an error, the bridge automatically disables itself as a defense. By bridging a custom-made ERC-20 token with a supply intentionally manipulated to exceed 256 bits (uint256 max), the `SendToCosmosEvent` validation fails. This triggers an error return, causing the bridge to set `BridgeActivate = false` and shut down entirely.
## Exploitation
- **Status**: PoC available (documented in research report); reported via Immunefi.
- **Complexity**: Medium (Requires deploying a custom ERC-20 and interacting with bridge contracts).
- **Attack Vector**: Network (Smart contract interaction on Ethereum).
## Impact
- **Confidentiality**: None
- **Integrity**: None
- **Availability**: **High** (The bridge is rendered inoperable, preventing all cross-chain asset transfers).
## Remediation
### Patches
- **Vulnerability 1**: The fix involves ensuring that metadata validation errors do not block the nonce progression or are handled gracefully before the nonce check.
- **Vulnerability 2**: Fixed in commit `ce0216d53d8b35b4c8a509f2ceeb34cf4e2d9c54`. The logic was updated to return `nil` instead of an `error` when an invalid supply is detected, preventing the fail-safe trigger.
### Workarounds
- Manual intervention by validators/governance to reset the bridge state or skip the offending nonce.
## Detection
- **Indicators of Compromise**:
- Log messages: `ERR ethereum event vote record failed` or `BridgeActivate is set to false`.
- Error strings: `malicious supply of ... detected`.
- **Detection methods**: Monitor orchestrator logs for "mismatch between nonces" or "BridgeActivate" status changes.
## References
- Faith's Blog: `https://faith2dxy[.]xyz/2023-12-12/cronos-gravity-bridge-bugs/`
- Cronos Gravity Bridge GitHub: `https://github[.]com/crypto-org-chain/gravity-bridge`
- Fix Commit: `https://github[.]com/crypto-org-chain/gravity-bridge/commit/ce0216d53d8b35b4c8a509f2ceeb34cf4e2d9c54`