# RubiconMarket batchOffer and batchRequote make offers as self; complete loss of funds for some types of tokens, for example WETH

RubiconMarket's batchOffer calls this.offer, making offers as the market itself; with WETH-like tokens, attacker drains market's escrowed WETH for free.

- Payout: Contest reward
- Severity: high
- Program: Rubicon
- Credited: shalaamum
- Disclosed: 2025-02-17
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-04-rubicon-findings/issues/1052
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-rubicon%3AH-05
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** RubiconMarket smart contract (Solidity)
**Root cause:** batchOffer/batchRequote call this.offer, an external message call, so msg.sender inside offer is the RubiconMarket contract itself, not the original caller. Tokens like WETH skip allowance checks when src == msg.sender, letting the market 'transferFrom' its own balance without approval.
**Steps:**
1. Attacker deploys a worthless ERC20 token (attackerCoin) and mints some to themselves.
2. Attacker calls `rubiconMarket.batchOffer([marketWethBalance], [weth.address], [1], [attackerCoin.address])`.
3. Inside batchOffer, `this.offer(...)` executes with msg.sender = RubiconMarket; WETH.transferFrom(RubiconMarket, RubiconMarket, amount) succeeds because src == msg.sender bypasses allowance.
4. An offer is created selling the market's entire WETH balance for 1 unit of attackerCoin.
5. Attacker approves attackerCoin to the market and calls `rubiconMarket.buy(lastOfferId, marketWethBalance*10000/9999)` to take the offer.
6. Attacker receives all WETH; market balance drops to 0.
**How found:** Code review of batchOffer/batchRequote revealed `this.offer` external call changes msg.sender to the contract. The researcher then checked token transferFrom implementations and found WETH's allowance bypass when src == msg.sender, making the self-offer possible.
**Exploitation:** Attacker needs a token whose transferFrom skips allowance when src == msg.sender (e.g., WETH). They deploy such a token, call batchOffer to make the market offer its entire WETH balance for 1 unit of their token, then buy the offer, draining the market's escrowed WETH at near-zero cost.
**Insight:** When a contract calls its own function via `this.fn()`, msg.sender becomes the contract; combined with tokens that skip allowance when src == msg.sender (WETH, some ERC20s), this can let the contract spend its own escrowed funds. Audit for self-calls and token-specific transferFrom quirks.
