Skip to content

Commit

Permalink
Uploaded files for judging
Browse files Browse the repository at this point in the history
  • Loading branch information
sherlock-admin4 committed Feb 19, 2025
1 parent bd466e2 commit 4298855
Show file tree
Hide file tree
Showing 676 changed files with 58,034 additions and 10 deletions.
10 changes: 0 additions & 10 deletions .gitignore

This file was deleted.

36 changes: 36 additions & 0 deletions 001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Tangy Ultraviolet Meerkat

Medium

# Lack of Validation on Currency Configuration Updates

### Summary

The `setLaunchGroupCurrency` and `toggleLaunchGroupCurrencyEnabled` functions allow updating currency configurations without sufficient validation, which could lead to misconfigured token pricing or disabled currencies being re-enabled unexpectedly.

### Root Cause

- The function `setLaunchGroupCurrency` allows a manager to update the currency configuration without verifying whether the launch group exists.
- `toggleLaunchGroupCurrencyEnabled` allows enabling/disabling a currency without checking if it has been previously set, which could cause inconsistencies.

https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L726
https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L740

### Internal Pre-conditions

### External Pre-conditions

### Attack Path

### Impact

- Could cause incorrect currency pricing in token sales.
- Could allow unexpected reactivation of disabled currencies.
- May lead to unintended user participation issues.

### PoC

### Mitigation

- Add a check to ensure the launch group exists before setting a currency.
- Require that the currency has been previously configured before allowing it to be enabled/disabled.
45 changes: 45 additions & 0 deletions 002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Custom Champagne Worm

Medium

# Replay Attack Due to Missing userId Validation

### Vulnerability Details
The participate function relies on userId to prevent replay attacks, but it does not validate whether userId is non-zero.
This allows an attacker to submit a request with **userId = 0x0**.
https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L287

https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L237-L239
And here it will effectively bypass the uniqueness check and enable duplicate participations with the same launchParticipationId.

As a result:

Malicious users could replay the same participation request multiple times.
It could lead to unauthorized allocations of tokens.
The system could be exploited to withdraw more funds than intended.

I mark this issue as 'Medium' because there is less possibility that userId is 0 and it might be validated by signers out of the network.
But, we still need to add the validation check for userId in the contract itself.

### Fixes

✅ Pass the entire request struct instead of individual parameters.
✅ Check request.userId != bytes32(0) to ensure it is valid.

```solidity
/// @notice Validates common request parameters
function _validateRequest(ParticipationRequest calldata request) private view {
// Validate launch id, chain id, user address, and launch group is valid
if (
request.userId == bytes32(0) || request.launchId != launchId || request.chainId != block.chainid
|| msg.sender != request.userAddress || !_launchGroups.contains(request.launchGroupId)
) {
revert InvalidRequest();
}
// Validate request has not expired
if (request.requestExpiresAt <= block.timestamp) {
revert ExpiredRequest(request.requestExpiresAt, block.timestamp);
}
}
```
75 changes: 75 additions & 0 deletions 003.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Kind Eggshell Shark

High

# user will steal other user's funds

### Summary

The missing check in `updateParticipation` function will cause a stealing other user's funds.

### Root Cause

On `updateParticipation` function in `Launch.sol`, it only checks if the previous userid is same with current userid in request, does not checks the userAddress.
[link](https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L363)
If new amount is decreased than previous amount, the difference amount will be send to caller.

### Internal Pre-conditions

1. signer needs to sign the `UpdateParticipationRequest`

### External Pre-conditions

none

### Attack Path

1. user calls `updateParticipation` function with other user's info with decreased tokenAmount, changing userAddress to attacker's address

### Impact

The other users loss their funds

### PoC

```solidity
// Based on Launch.UpdateParticipation.t.sol
// [...]
function test_steal_funds() public {
ParticipationRequest memory request = _createParticipationRequest();
bytes memory signature = _signRequest(abi.encode(request));
vm.startPrank(user2);
console.log("user2 balance", currency.balanceOf(user2));
UpdateParticipationRequest memory updateRequest = _createUpdateParticipationRequest2(500);
bytes memory updateSignature = _signRequest(abi.encode(updateRequest));
launch.updateParticipation(updateRequest, updateSignature);
console.log("user2 balance after exploit", currency.balanceOf(user2));
vm.stopPrank();
}
// [...]
function _createUpdateParticipationRequest2(uint256 newTokenAmount)
internal
view
returns (UpdateParticipationRequest memory)
{
uint256 launchTokenDecimals = launch.tokenDecimals();
return UpdateParticipationRequest({
chainId: block.chainid,
launchId: testLaunchId,
launchGroupId: testLaunchGroupId,
prevLaunchParticipationId: testLaunchParticipationId,
newLaunchParticipationId: "newLaunchParticipationId",
userId: testUserId,
userAddress: user2,
tokenAmount: newTokenAmount * 10 ** launchTokenDecimals,
currency: address(currency),
requestExpiresAt: block.timestamp + 1 hours
});
}
```

### Mitigation

add a check that `request.userAddress == prevInfo.userAddress`
97 changes: 97 additions & 0 deletions 004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Kind Eggshell Shark

Medium

# Any user can can other user's participation

### Summary

The missing check for `userAddress` will lead to cancel other users' participation by anyone

### Root Cause

[link](https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L430-L433)
in current implementation of `calcelParticipation` function, it does not verify if userAddress in request is same on info. So anyone can cal other user's partipation. And it sends the funds to `info.userAddress` so can't steal that funds directly.

### Internal Pre-conditions

1. signer needs to sign their request

### External Pre-conditions

none

### Attack Path

1. User calls `cancelParticipation` function with their request

### Impact

The users' participation can be canceled by any users.

### PoC

```solidity
// Based on Launch.CalcelParticipation.sol
// [...]
function test_CancelParticipation() public {
// Prepare cancel participation request
CancelParticipationRequest memory cancelRequest = _createCancelParticipationRequest();
bytes memory cancelSignature = _signRequest(abi.encode(cancelRequest));
ParticipationInfo memory info = launch.getParticipationInfo(cancelRequest.launchParticipationId);
assertEq(info.tokenAmount, 1000 * 10 ** 18);
assertEq(info.currencyAmount, 1000 * 10 ** 18);
uint256 initialUserTokenAmount = launch.getUserTokensByLaunchGroup(testLaunchGroupId, testUserId);
uint256 startingBalance = currency.balanceOf(user1);
vm.startPrank(user2);
// Expect ParticipationCancelled event
vm.expectEmit();
emit ParticipationCancelled(
cancelRequest.launchGroupId,
cancelRequest.launchParticipationId,
cancelRequest.userId,
user2,
info.currencyAmount,
address(currency)
);
// Update participation
launch.cancelParticipation(cancelRequest, cancelSignature);
vm.stopPrank();
// Verify update
ParticipationInfo memory newInfo = launch.getParticipationInfo(cancelRequest.launchParticipationId);
assertEq(newInfo.tokenAmount, 0);
assertEq(newInfo.currencyAmount, 0);
// Verify user balance
assertEq(currency.balanceOf(user1), startingBalance + info.currencyAmount);
// Verify user tokens
uint256 userTokenAmount = launch.getUserTokensByLaunchGroup(testLaunchGroupId, testUserId);
assertEq(userTokenAmount, initialUserTokenAmount - info.tokenAmount);
// Verify user ID is no longer in the launch group
assertEq(launch.getLaunchGroupParticipantUserIds(testLaunchGroupId).length, 0);
}
// LaunchTestBase.t.sol
function _createCancelParticipationRequest() internal view returns (CancelParticipationRequest memory) {
return CancelParticipationRequest({
chainId: block.chainid,
launchId: testLaunchId,
launchGroupId: testLaunchGroupId,
launchParticipationId: testLaunchParticipationId,
userId: testUserId,
userAddress: user2, // change to user2
requestExpiresAt: block.timestamp + 1 hours
});
}
```

### Mitigation

add a check for `info.userAddress == msg.sender`
79 changes: 79 additions & 0 deletions 005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
Kind Eggshell Shark

Medium

# User can loss funds when calling `updateParticipation` function

### Summary

The missing check for `ParticipationId` in `updateParticipation` can loss the user's funds

### Root Cause

[link](https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L380)
It sets `currentAmount` and `tokenAmount` of `newInfo` first and reset those field of `prevInfo` to zero. When user pass the `prevLaunchParticipationId` and `newLaunchParticipation` with same value, the `currentAmount` and `tokenAmount` would be set to zero even if the user deposites the currency

### Internal Pre-conditions

1. signer needs to sign their request

### External Pre-conditions

none

### Attack Path

1. The user calls updateParticipation with same ParticipationId

### Impact

The user can loss their funds

### PoC

```solidity
// based on Launch.UpdateParticipation.t.sol
// [...]
function test_loss_funds() public {
ParticipationRequest memory request = _createParticipationRequest();
bytes memory signature = _signRequest(abi.encode(request));
vm.startPrank(user1);
ParticipationInfo memory iii = launch.getParticipationInfo(testLaunchParticipationId);
console.log("tokenAmount", iii.tokenAmount);
console.log("currencyAmount", iii.currencyAmount);
UpdateParticipationRequest memory updateRequest = _createUpdateParticipationRequest(500);
bytes memory updateSignature = _signRequest(abi.encode(updateRequest));
launch.updateParticipation(updateRequest, updateSignature);
iii = launch.getParticipationInfo(testLaunchParticipationId);
console.log("tokenAmount after", iii.tokenAmount);
console.log("currencyAmount after", iii.currencyAmount);
vm.stopPrank();
}
// [...]
function _createUpdateParticipationRequest(uint256 newTokenAmount)
internal
view
returns (UpdateParticipationRequest memory)
{
uint256 launchTokenDecimals = launch.tokenDecimals();
return UpdateParticipationRequest({
chainId: block.chainid,
launchId: testLaunchId,
launchGroupId: testLaunchGroupId,
prevLaunchParticipationId: testLaunchParticipationId,
newLaunchParticipationId: testLaunchParticipationId, // same id
userId: testUserId,
userAddress: user1,
tokenAmount: newTokenAmount * 10 ** launchTokenDecimals,
currency: address(currency),
requestExpiresAt: block.timestamp + 1 hours
});
}
```

### Mitigation

add a check `prevLaunchParticipationId!=newLaunchParticipationId`
36 changes: 36 additions & 0 deletions 006.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Kind Eggshell Shark

High

# Any user can loss other user's funds

### Summary

The missing check of `newLaunchParticipationId` can overwrite other user's info in `updateParticipation` function.

### Root Cause

On `updateParticipation` function in `Launch.sol`, it does not check anything of `newLaunchParticipationId`.
[link](https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L380)
If user send request including other user's participation id, the all values (currencyAmount, currency, userId, userAddress, tokenAmount) can be overwritten.

### Internal Pre-conditions

1. signer needs to sign the `UpdateParticipationRequest`

### External Pre-conditions

none

### Attack Path

1. user A creates dummy participation with calling `participate` function
2. user A calls `updateParticipation` function with other user's Participation Id.

### Impact

The users funds can be deleted by any users

### Mitigation

add a check for `newLaunchParticipationId`
Loading

0 comments on commit 4298855

Please sign in to comment.