code4rena-2023-06-angle-protocol-h03
[H-03] Poor detection of disputed trees allows claiming tokens from a disputed tree
Summary
claim에서 이의를 제기한 상태인지 확인하지 않으므로, 빠르게 대처하지 않으면 논란이 있는 머클 트리를 기반으로 claim이 가능하다.
Keyword
logic flaw
Vulnerability
claim 함수를 호출하면 보상을 얻을 수 있다. 머클 트리에서 proof를 확인하고 유효하다면 보상을 준다.
function claim(
address[] calldata users,
address[] calldata tokens,
uint256[] calldata amounts,
bytes32[][] calldata proofs
) external {
...
for (uint256 i; i < usersLength; ) {
address user = users[i];
address token = tokens[i];
uint256 amount = amounts[i];
// Checking if only an approved operator can claim for `user`
if (onlyOperatorCanClaim[user] == 1 && operators[user][msg.sender] == 0) revert NotWhitelisted();
// Verifying proof
bytes32 leaf = keccak256(abi.encode(user, token, amount));
if (!_verifyProof(leaf, proofs[i])) revert InvalidProof();
...
}
}_verifyProof에서 머클 트리를 확인한다. getMerkleRoot 에서 if (block.timestamp >= endOfDisputePeriod) return tree.merkleRoot;를 주목하자. 이의제기가 가능한 시점을 넘었다면 현재 머클 트리 루트를 리턴하고 그렇지 않다면 직전의 머클 트리 루트를 리턴한다.
function _verifyProof(bytes32 leaf, bytes32[] memory proof) internal view returns (bool) {
bytes32 currentHash = leaf;
uint256 proofLength = proof.length;
for (uint256 i; i < proofLength; ) {
if (currentHash < proof[i]) {
currentHash = keccak256(abi.encode(currentHash, proof[i]));
} else {
currentHash = keccak256(abi.encode(proof[i], currentHash));
}
unchecked {
++i;
}
}
bytes32 root = getMerkleRoot();
if (root == bytes32(0)) revert InvalidUninitializedRoot();
return currentHash == root;
}
function getMerkleRoot() public view returns (bytes32) {
if (block.timestamp >= endOfDisputePeriod) return tree.merkleRoot;
else return lastTree.merkleRoot;
}getMerkleRoot는 이의제기 가능 시점이 지났는지만 확인한다. 하지만 이의를 제기하여 분쟁중인 경우에도 시간은 지난다. 분쟁이 아직 해결되지 않았음에도, 이의제기 가능 시점이 지나면 현재 머클트리를 기반으로 보상을 요청할 수 있다.
악성 행위가 발생하고 유저가 이에 빠르게 이의제기했어도, governor/guardian이 빨리 이의를 인정하지 않으면 이를 기반으로 claim이 가능하다.
Impact
이의를 제기했음에도 빠르게 대처하지 않으면 논란이 있는 머클 트리를 기반으로 claim이 가능하다.
Mitigation
이의가 제기되었다면 이것이 해결될 때까지 과거 머클 트리를 이용한다.
diff --git a/contracts/Distributor.sol b/contracts/Distributor.sol
index bc4e49f..8fb6a4c 100644
--- a/contracts/Distributor.sol
+++ b/contracts/Distributor.sol
@@ -197,7 +197,7 @@ contract Distributor is UUPSHelper {
/// @notice Returns the MerkleRoot that is currently live for the contract
function getMerkleRoot() public view returns (bytes32) {
- if (block.timestamp >= endOfDisputePeriod) return tree.merkleRoot;
+ if (block.timestamp >= endOfDisputePeriod && disputer == address(0)) return tree.merkleRoot;
else return lastTree.merkleRoot;
}tags: bughunting, angle protocol, smart contract, solidity, stablecoin, logic flaw, severity high