sherlock-2025-07-mellow-m07
[M-07] DoS in Redemption Due to Unchecked Asset Support in Subvaults
Summary
출금을 처리할 때 모자란 토큰을 서브볼트에서 꺼낸다. 이 때 토큰을 출금할 대상 서브볼트는 balanceOf 함수 호출 결과로 지정한다. 서브볼트는 등록된 토큰만 처리할 수 있으며 등록되지 않은 토큰을 출금하려 할 때는 트랜잭션이 취소된다. 공격자는 서브볼트가 지원하지 않는 소액의 토큰을 직접 전송하여 DoS 공격을 할 수 있다.
Keyword
donation attack, dos, allowlist
Vulnerability
- flexible-vaults/src/queues/SignatureRedeemQueue.sol#L13-L27
- flexible-vaults/src/hooks/BasicRedeemHook.sol#L33-L41
- flexible-vaults/src/managers/RiskManager.sol#L236-L253
유저는 시그니처 출금 큐의 redeem을 호출하여 출금을 요청한다. 볼트에 예치된 토큰만으로는 출금을 처리할 수 없을 시 callHook을 호출하여 서브볼트에 예치된 토큰을 꺼낸다. callHook에서는 서브볼트를 순회하며 출금하고자 하는 토큰(이하 토큰 X)의 양을 조회한다. 서브볼트가 토큰 X를 가지고 있다면 hookPullAssets를 호출하여 해당 서브볼트에서 토큰 X를 꺼낸다. hookPullAssets의 내부에서는 modifySubvaultBalance 함수를 호출한다.
function redeem(Order calldata order, IConsensus.Signature[] calldata signatures) external payable nonReentrant {
...
@> vault_.callHook(order.requested);
...
}
function callHook(address asset, uint256 assets) public virtual {
IVaultModule vault = IVaultModule(address(this));
@> uint256 liquid = IERC20(asset).balanceOf(address(vault));
if (liquid >= assets) {
return;
}
uint256 requiredAssets = assets - liquid;
uint256 subvaults = vault.subvaults();
for (uint256 i = 0; i < subvaults; i++) {
address subvault = vault.subvaultAt(i);
@> uint256 balance = IERC20(asset).balanceOf(subvault);
if (balance == 0) {
continue;
}
if (balance >= requiredAssets) {
@> vault.hookPullAssets(subvault, asset, requiredAssets);
break;
} else {
@> vault.hookPullAssets(subvault, asset, balance);
requiredAssets -= balance;
}
}
}서브볼트는 지원하는 토큰을 등록해둔다. 등록되지 않은 토큰을 출금하기 위해 modifySubvaultBalance 함수를 호출하면 NotAllowedAsset 에러를 발생시켜 트랜잭션을 취소시킨다.
function modifySubvaultBalance(address subvault, address asset, int256 change)
external
onlyVaultOrRole(MODIFY_SUBVAULT_BALANCE_ROLE)
{
RiskManagerStorage storage $ = _riskManagerStorage();
requireValidSubvault($.vault, subvault);
@> if (!$.allowedAssets[subvault].contains(asset)) {
@> revert NotAllowedAsset(asset);
}
...
}이는 DoS 공격으로 사용될 수 있다. 공격자가 극소량의 토큰을 전송하면 redeem 호출시 트랜잭션이 취소된다.
Impact
공격자가 토큰을 컨트랙트에 직접 전송하여 DoS 공격을 할 수 있음. redeem 호출이 불가하게 함.
Mitigation
서브볼트의 modifySubvaultBalance를 호출하기 전 먼저 서브볼트가 해당 토큰을 지원하는 지 확인한다. 지원하지 않는 토큰이라면 호출하지 않는다.
tags: bughunting, mellow, smart contract, solidity, severity medium, dos, allowlist, donation attack