sherlock-2025-04-burve-h03
[H-03] Incorrect netting logic leads to excessive withdrawal amounts
Summary
서드파티 볼트에 대한 입출금을 모아서 한 번에 처리할 때, 출금액을 잘못 계산하여 실제로 출금해야 하는 것보다 많은 양을 출금한다.
Keyword
bug, arithmetic error
Vulnerability
서드파티 볼트에 예치금을 이동할 때, 가스 비용이나 수수료 비용을 아끼기 위해 여러가지 작업에서 발생한 입/출금액을 합산하여 최종적으로 한 번만 상호작용을 한다. 예를 들어 서드파티 볼트에서 토큰 100개를 입금하고, 50개를 출금, 다시 10개을 입금하는 요청이 발생하면 3번 상호작용하는 대신 60개만 한 번에 입금한다.
다음은 VaultE4626Impl.commit 함수이다. 입출금 요청을 모두 합산한 뒤 commit 함수를 호출하여 최종적으로 남은 금액을 확인, 서드파티와 상호작용을 한다. 입금 요청액과 출금 요청액을 비교하고 입금액이 더 많다면 입금액과 출금액의 차액만큼을 입금, 출금액이 더 많다면 출금액과 입금액의 차액만큼을 출금해야 한다.
그런데 assetsToWithdraw > assetsToDeposit 일 때, 즉 출금 요청액이 더 많을 때 이를 잘못 핸들링한다. assetsToDeposit을 먼저 0으로 초기화한 후 이를 assetsToWithdraw 에서 빼므로 결과적으로 assetsToWithdraw - 0 만큼 출금을 요청하게 된다. 즉, 서드파티에서 실제로 출금해야 하는 것보다 많이 출금하게 된다.
function commit(VaultE4626 storage self, VaultTemp memory temp) internal {
uint256 assetsToDeposit = temp.vars[1];
uint256 assetsToWithdraw = temp.vars[2];
if (assetsToDeposit > 0 && assetsToWithdraw > 0) {
// We can net out and save ourselves some fees.
if (assetsToDeposit > assetsToWithdraw) {
assetsToDeposit -= assetsToWithdraw;
assetsToWithdraw = 0;
} else if (assetsToWithdraw > assetsToDeposit) {
@> assetsToDeposit = 0;
@> assetsToWithdraw -= assetsToDeposit;
} else {
// Perfect net!
return;
}
}
if (assetsToDeposit > 0) {
// Temporary approve the deposit.
SafeERC20.forceApprove(
self.token,
address(self.vault),
assetsToDeposit
);
self.totalVaultShares += self.vault.deposit(
assetsToDeposit,
address(this)
);
SafeERC20.forceApprove(self.token, address(self.vault), 0);
@> } else if (assetsToWithdraw > 0) {
// We don't need to hyper-optimize the receiver.
@> self.totalVaultShares -= self.vault.withdraw(
assetsToWithdraw,
address(this),
address(this)
);
}
}Impact
실제로 출금해야 하는 것보다 많은 양을 출금하였으므로 프로토콜 내부 회계 금액과 일치하지 않는다. ERC4626 볼트의 share를 실제보다 적게 가지고 있기에(출금으로 인해) 추후 출금 요청에 실패한다.
Mitigation
assetsToWithdraw 를 먼저 계산한 뒤 assetsToDeposit를 0으로 설정한다.
tags: bughunting, burve, smart contract, solidity, arithmetic error, severity high