sherlock-2025-04-burve-h07
[H-07] Attacker can steal user funds via ERC4626 inflation attack on underlying vault
Summary
ERC4626 래퍼 컨트랙트에서 인플레이션 공격이 가능하다. 이를 통해 첫 입금자의 토큰을 탈취할 수 있다.
Keyword
erc4626, inflation attack, crypto theft
Vulnerability
서드파티 ERC4626와 연동하기 위해 서드파티를 감싸는 E4626 볼트 컨트랙트(이하 wrapper 컨트랙트)를 만들었다. wrapper 컨트랙트와 상호작용하기 전 먼저 fetch 함수를 호출하여 이 Vault가 서드파티에게 발행받은 share의 가치를 쿼리해 캐시한다.
wrapper에 입금 또는 출금을 하면 즉시 서드파티에게 전달되지 않고 모든 요청을 합산하여 차액만큼을 마지막에 입금 또는 출금한다. 입출금 함수에서는 실제로 서드파티에게 share를 받는 것이 아니라 요청자의 share 변화를 계산(예측)하여 저장한다.
function fetch(
VaultE4626 storage self,
VaultTemp memory temp
) internal view {
@> temp.vars[0] = self.vault.previewRedeem(self.totalVaultShares); // Total assets
temp.vars[3] = self.vault.previewRedeem(
self.vault.previewDeposit(1 << 128)
); // X128 fee discount factor.
}
function deposit(
VaultE4626 storage self,
VaultTemp memory temp,
ClosureId cid,
uint256 amount
) internal {
uint256 newlyAdding = FullMath.mulX128(
temp.vars[1],
temp.vars[3],
true // Round up to round shares down.
);
@> uint256 totalAssets = temp.vars[0] + newlyAdding - temp.vars[2];
uint256 discountedAmount = FullMath.mulX128(
amount,
temp.vars[3],
false // Round down to round shares down.
);
@> uint256 newShares = totalAssets == 0
? discountedAmount
: FullMath.mulDiv(self.totalShares, discountedAmount, totalAssets);
self.shares[cid] += newShares;
self.totalShares += newShares;
temp.vars[1] += amount;
}발행될 share를 계산하는 부분에 주목하자. FullMath.mulDiv(self.totalShares, discountedAmount, totalAssets) 에서 totalAssets은 서드파티와의 직접 상호작용을 통해 조작될 수 있다. 만약 서드파티가 인플레이션 공격에 취약하게 개발되었다면 wrapper 컨트랙트 역시 인플레이션 공격에 취약하게 된다.
- 공격자가 피해자의 요청을 프론트러닝하여 wrapper를 통해 적은 금액을 입금한다. 또는 연동된 서드파티 ERC4626에 직접 입금한다. 공격자는 최소량의 share를 발행받는다.
- 공격자가 연동된 서드파티 ERC4626에 직접 금액 X를 전송한다. 서드파티의
totalAssets는 증가하지만totalSupply는 그대로이다. - 피해자가 wrapper 컨트랙트의
deposit을 호출하여 금액 Y를 입금 요청한다.wrapper.fetch에서origin.previewRedeem을 호출하여 이 Vault가 서드파티에게 발행받은 전체 share의 가치를 쿼리한다. 이는(temp.vars[0] ≈ X)로 직접 입금액에 의해 부풀려진다.- 따라서
wrapper.deposit함수에서totalAssets역시 부풀려진다. - 피해자의 입금에 의해 새로 발행될 share를 계산한다.
FullMath.mulDiv(self.totalShares, discountedAmount, totalAssets)에서totalAssets가 부풀려졌다.FullMath.mulDiv(self.totalShares, Y, X), X >> Y로 반올림되어 새로운 share는 0이 된다.
wrapper.commit을 호출하여 서드파티 ERC4626과 상호작용한다.
- 공격자는 wrapper 또는 서드파티에서 직접 자신의 예치금을 인출한다. 부풀려진 자산 대 주식 비율로 공격자는 초기 예치금과 공격자금 X, 피해자의 자금 Y를 받게 된다.
Impact
유저의 자금을 탈취한다.
Mitigation
해결 방법은 잘 모르겠다. dead share를 발행하거나 decimals를 조정하더라도 서드파티에 취약점이 있다면 그대로 노출된다. 취약점이 없는 서드파티에 연동한다 정도?
Memo
이 보고서는 이상하게 분류되었다. NoopVault 라는 단순 OZ ERC4626 상속 컨트랙트가 인플레이션 공격에 취약하다는 보고서들과 함께 묶였다. 이 보고서는 NoopVault가 아니라 전체적으로 사용되는 래퍼 컨트랙트에 대한 취약점이다.
tags: bughunting, burve, smart contract, solidity, erc4626, inflation attack, crypto theft, severity high