code4rena-2022-08-nounsdao-m01
[M-01] Voters can burn large amounts of Ether by submitting votes with long reason strings
Summary
투표시 가스비용을 일부 환급해준다. 함수 파라미터로 긴 문자열을 넣으면 가스를 더 많이 소모하여 더 많은 ETH를 환급받을 수 있다.
Keyword
gas refund, DoS, input validation
Vulnerability
- contracts/governance/NounsDAOLogicV2.sol#L518-L524
- contracts/governance/NounsDAOLogicV2.sol#L533-L544
- contracts/governance/NounsDAOLogicV2.sol#L98
uint256 public constant REFUND_BASE_GAS = 36000;
...
function castRefundableVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) external {
castRefundableVoteInternal(proposalId, support, reason);
}
function castRefundableVoteInternal(
uint256 proposalId,
uint8 support,
string memory reason
) internal {
uint256 startGas = gasleft();
uint96 votes = castVoteInternal(msg.sender, proposalId, support);
emit VoteCast(msg.sender, proposalId, support, votes, reason);
if (votes > 0) {
_refundGas(startGas);
}
}
function _refundGas(uint256 startGas) internal {
unchecked {
uint256 balance = address(this).balance;
if (balance == 0) {
return;
}
uint256 gasPrice = min(tx.gasprice, block.basefee + MAX_REFUND_PRIORITY_FEE);
uint256 gasUsed = startGas - gasleft() + REFUND_BASE_GAS;
uint256 refundAmount = min(gasPrice * gasUsed, balance);
(bool refundSent, ) = msg.sender.call{ value: refundAmount }('');
emit RefundableVote(msg.sender, refundAmount, refundSent);
}
}투표를 하면 투표에 사용된 가스비를 일부 환급해주는 기능이 있다. 이 때, reason 파라미터에 임의의 문자열을 넣어 이벤트에 찍을 수 있다.
reason 파라미터는 길이 제한이 없다. 따라서 공격자가 임의로 긴 문자열을 넣을 수 있다. 긴 문자열을 파라미터로 넘긴다면 짧은 것보다 많은 가스를 소비하고, 그리하면 더 많은 가스비를 환급받을 수 있다. 가스 환급은 컨트랙트에 예치된 ETH를 이용하므로 예치된 ETH를 기대보다 더 소비할 수 있게 된다.
환급을 받더라도 파라미터를 넘기는 가스비를 전부 받아내지는 못하기때문에, 공격을 통해 공격자가 경제적으로 이득을 보지는 못 한다.따라서 경제적 이득을 바라고 공격을 하지는 않을 것이다. 하지만 이러한 경제적 보호에만 의존하는 것보다는 이를 막는 것이 낫다.
Impact
- 미래에 calldata 가격이 낮아질 가능성이 높다. calldata를 넣는 가격이 낮아진다면 공격자가 경제적으로 손해를 보는 부분이 완화되어 좀 더 공격하기 좋은 상황이 올 것이다.
- 공격자가 임의의 텍스트를 이벤트로 쓰고자 하는 동기가 있을 수 있으며, 단순히 이를 위해 이 시스템을 악용할 수 있다.
- 단순히 프로토콜의 명성을 손상시키기 위해 악의적으로 ETH를 고갈시킬 수 있다.
- 예치된 ETH를 고갈시켜 다른 유권자들이 가스비용을 환급받지 못하게 할 수 있다.
Mitigation
- 해당 문자열 파라미터를 사용하는
emit VoteCast코드를 가스 환급 대상 코드에서 제외한다. (가스비를 먼저 환급해준 후 이벤트 생성) 이벤트 생성에 대한 가스비를 환급하고 싶다면REFUND_BASE_GAS를 늘려 합리적인 양(적절한 reason 길이)까지만 환급해준다. reason의 타입을bytes로 변경하고 길이를 제한한다.castRefundableVoteWithReason()에서 길이를 체크하고, 너무 긴reason을 넣은 경우 revert 시킨다.
tags: bughunting, nouns dao, smart contract, solidity, gas refund, dos, lack-of-input-validation-vul, severity medium