code4rena-2023-12-shell-protocol-l09
[L-09] Owner has to perpetually pay fees to withdraw fees, resulting in dust amount left in the contract
Summary
관리자가 요청했을 때도 수수료를 뗀다. 따라서 관리자는 수수료로 받은 토큰을 unwrap할 때, 일부를 unwrap할 수 없다.
Keyword
asset lock
Vulnerability
모든 wrap/unwrap 시에 수수료를 뗀다. 수수료는 Ocean 토큰을 관리자에게 민팅하는 형태로 전달된다.
function _grantFeeToOcean(uint256 oceanId, uint256 amount) private {
if (amount > 0) {
// since uint, same as (amount != 0)
_mintWithoutSafeTransferAcceptanceCheck(owner(), oceanId, amount);
}
}관리자가 수수료를 unwrap할 때도 마찬가지로 수수료를 뗀다. 따라서 관리자가 소지한 모든 토큰을 unwrap하더라도 항상 아주 약간의 토큰이 수수료로 새로 발행되어 wrap된 상태로 남아있다.
function _erc20Unwrap(address tokenAddress, uint256 amount, address userAddress, uint256 inputToken) private {
try IERC20Metadata(tokenAddress).decimals() returns (uint8 decimals) {
@> uint256 feeCharged = _calculateUnwrapFee(amount);
uint256 amountRemaining = amount - feeCharged;
(uint256 transferAmount, uint256 truncated) =
_convertDecimals(NORMALIZED_DECIMALS, decimals, amountRemaining);
feeCharged += truncated;
@> _grantFeeToOcean(inputToken, feeCharged);
SafeERC20.safeTransfer(IERC20(tokenAddress), userAddress, transferAmount);
emit Erc20Unwrap(tokenAddress, transferAmount, amount, feeCharged, userAddress, inputToken);
} catch {
revert NO_DECIMAL_METHOD();
}
}Impact
수수료 토큰의 일부를 unwrap할 수 없다.
Mitigation
관리자가 작업을 요청했을 때에는 수수료를 떼지 않는다.
function _grantFeeToOcean(uint256 oceanId, uint256 amount) private {
- if (amount > 0) {
+ if (amount > 0 && msg.sender != owner()) {
// since uint, same as (amount != 0)
_mintWithoutSafeTransferAcceptanceCheck(owner(), oceanId, amount);
}
}Memo
Medium으로 제출했는데 Low로 내려간 이슈이다. 남는 토큰의 양이 아주 작기 때문에 Low로 쳤다.
tags: bughunting, shell protocol, smart contract, solidity, asset lock, severity low