xanoutas/solidity-security-findings huggingface dataset:
public accrueBribes
Audit: 2023-05-maia-findings
Severity: MEDIUM

# Lines of code

https://github.com/codeinking11/maia/blob/c37f1f8f0fb924e01bc99705c49c1a00ced56c28/src/gauges/BaseV2Gauge.sol#L111


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The find affects the detail are in the proof of concept   function accrueBribes(address user) external onlyStrategy 
      



## Proof of Concept
 If the bribes are not distributed fairly or transparently, it can lead to a concentration of power and influence in the hands of a few individuals or entities. 
 This can undermine the principles of decentralization and fairness in the governance or reward distribution mechanism.

The accrual of bribes can create incentives for manipulation and collusion among participants. 
Because the accrueBribes the system by coordinating their actions to gain an unfair advantage or manipulate the rewards in their favor in a sort of swam Denial Of Service attack the artificially accrues values can affect 
the calculation of these functions
  function _decrementGaugeWeight(address user, address gauge, uint112 weight, uint32 cycle) internal {
        if (!_gauges.contains(gauge)) revert InvalidGaugeError();

        uint112 oldWeight = getUserGaugeWeight[user][gauge];

        IBaseV2Gauge(gauge).accrueBribes(user);

        getUserGaugeWeight[user][gauge] = oldWeight - weight;
        if (oldWeight == weight) {
            // If removing all weight, remove gauge from user list.
            require(_userGauges[user].remove(gauge));
        }

        _writeGaugeWeight(_getGaugeWeight[gauge], _subtract112, weight, cycle);

        emit DecrementGaugeWeight(user, gauge, weight, cycle);
    }

      function _incrementGaugeWeight(address user, address gauge, uint112 weight, uint32 cycle) internal {
        if (!_gauges.contains(gauge) || _deprecatedGauges.contains(gauge)) revert InvalidGaugeError();
        unchecked {
            if (cycle - block.timestamp <= incrementFreezeWindow) revert IncrementFreezeError();
        }

        IBaseV2Gauge(gauge).accrueBribes(user);

        bool added = _userGauges[user].add(gauge); // idempotent add
        if (added && _userGauges[user].length() > maxGauges && !canContractExceedMaxGauges[user]) {
            revert MaxGaugeError();
        }

        getUserGaugeWeight[user][gauge] += weight;

        _writeGaugeWeight(_getGaugeWeight[gauge], _add112, weight, cycle);

        emit IncrementGaugeWeight(user, gauge, weight, cycle);
    }


 This can undermine the integrity of the gauge system and harm the interests of other participants.
 Manipulation of Accrual Process: If the accrual process is manipulated, a malicious actor could artificially inflate or reduce the amount of bribes allocated to certain users.
This manipulation could favor specific individuals or groups, undermining the integrity and fairness of the distribution mechanism.It also affect the rewards of guages.It is medium risk because it requires functions in the project which are well protects
Error in Slashing calculation causes users to lose double the intended amount
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L425-L446
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L482-L484
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L512-L513
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/NativeVaultLib.sol#L127-L129


# Vulnerability details

## Impact
A slashing scenario in which users would lose more(double) than the intended slashed amount.

## Proof of Concept
Assume the following scenario (for mathematical simplicity assuming that BOB is the first and the only user of the vault):
1. ***Bob creates a node, and adds 2 validators each with 32ETH as balance***
(Bob.totalRestakedEth = 64ETH, self.totalAssets = 64ETH)
2. ***The NativeVault is slashed for 30ETH***
(self.totalAssets = 64 - 34 = 34ETH)
3. ***Bob withdraws from the validators and thus the funds are transferred to the native node***
(bobs_node.balance = 64ETH)
4. ***Bob calls the `startSnapshot()` function, here the following things are calculated:***
***In the _transferToSlashStore() function:***(https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L425-L446)
(30ETH is transferred from Bob's native node to slashstore,
Bobs totalRestakedETH = 34ETH)

5. ***Bob calls the `validateSnapshotProofs()` function, here the following things are calculated:***
***In the `_updateSnapshot()` function:*** (https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L482-L484)
(totalDeltaWei = 34 + (-64) = -30ETH)

***In the `_decreaseBalance()` function called thereafter:***(https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L512-L513)
(self.totalAssets = 34 - 30 = 4ETH,
self.ownerToNode[_of].totalRestakedETH = 34 - 30 = 4ETH)

This is wrong since, only 30ETH should have been reduced from the initial 64ETH, but here we can see that 30*2 = 60ETH is being reduced from the user.

## Tools Used
Manual

## Recommended Mitigation Steps
A different approach may have to be implemented for the fully withdrawal condition.(Many changes would have to be brought here: https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/NativeVaultLib.sol#L127-L129)


## Assessed type

Math
The DSS can slash an operator even after they change their vault to be staked to another DSS
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L94-L124
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L126-L151
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L220-L231
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L248-L256


# Vulnerability details

When the operator stakes their vault to the DSS the DSS gains the ability to slash the operator if they are not performing the tasks as it is needed. That ability is used to punish bad operators and the DSS should only be able to slash vaults that are assigned to them and no other vault. However due to the way that slashing is being finalized the DSS can slash the operator even after they have unstaked their vault from the DSS. That issue happens due to the following flow missing an important validation:
The DSS decides to request a slashing of the operator's vault by calling the `requestSlashing` function in the `Core.sol` which checks if the operator is registered to the DSS and their vault is staked to that DSS:
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L220-L231
```
    function requestSlashing(SlasherLib.SlashRequest calldata slashingRequest)
        external
        whenFunctionNotPaused(Constants.PAUSE_CORE_REQUEST_SLASHING)
        nonReentrant
        returns (SlasherLib.QueuedSlashing memory queuedSlashing)
    {
        IDSS dss = IDSS(msg.sender);
        CoreLib.Storage storage self = _self();
        self.checkIfOperatorIsRegInRegDSS(slashingRequest.operator, dss);
        queuedSlashing = self.requestSlashing(dss, slashingRequest, self.nonce++);
        emit RequestedSlashing(address(dss), slashingRequest);
    }
```
then the request is decoded in the `SlasherLib.sol` and stored in the `slashingRequests` for future use:
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L94-L124
```
    function requestSlashing(
        CoreLib.Storage storage self,
        IDSS dss,
        SlashRequest memory slashingMetadata,
        uint256 nonce
    ) external returns (QueuedSlashing memory queuedSlashing) {
        validateRequestSlashingParams(self, slashingMetadata, dss);
        uint256[] memory earmarkedStakes = fetchEarmarkedStakes(slashingMetadata);
        queuedSlashing = QueuedSlashing({
            dss: dss,
            timestamp: uint96(block.timestamp),
            operator: slashingMetadata.operator,
            vaults: slashingMetadata.vaults,
            earmarkedStakes: earmarkedStakes,
            nonce: nonce
        });
        self.slashingRequests[calculateRoot(queuedSlashing)] = true;
        self.operatorState[slashingMetadata.operator].nextSlashableTimestamp[dss] =
            b
Slashing NativeVault will lead to locked ETH for the users
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L238
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L277
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L348
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L512


# Vulnerability details

## Impact
The Karak protocol includes a slashing mechanism that allows the contract owner to penalize stakers by reducing their staked assets in the event of malicious activity by the operator. If a user with a staked balance of 32 ETH is subject to a 3 ETH slashing, they should ideally be able to withdraw the remaining 29 ETH. However, due to a flaw in the implementation, when the user attempts to fully withdraw their ETH, they are only able to withdraw less than the actual remaining amount, with some ETH becoming permanently locked in the protocol.

Specifically, if all share tokens are burned during the withdrawal process, the user cannot access the remaining locked ETH. This results in users receiving fewer ETH than they are entitled to, with the excess ETH becoming inaccessible and permanently locked within the protocol. 
## Proof of Concept
Consider the following simplified scenario, where Alice want to restake her 32 ETH into Karak protocol, using 1 validator and no rewards acumulated for simplicity. In this example, although Alice initially had 32 ETH staked and should be able to withdraw 29 ETH (after a 3 ETH slashing), she ends up receiving only 26 ETH. The remaining 3 ETH becomes permanently locked in the contract. 
The steps she need to perform are the following:
1. Staking and Initial Setup: 
   * Alice call `createNode` to point her withdrawal credentials to it
    * She then calls `validateWithdrawalCredentials` followed by `startSnapshot` and `validateSnapshotProofs`.  
    *After these actions, the state will look like this:  
    `totalAssets = 32e18`  
    `totalSupply = 32e18`  
    `totalRestakedETH = 32e18` (in her Node struct)*
2. Slashing:
   * a slashing event occurs for 3 ETH, redusing the `totalAssets` to 29e18.  
  
3. Withdraw from Beacon Chain:  
   * Alice decides to withdraw all her ETH from the Beacon Chain, resulting in her node balance being 32 ETH.
   * To withdraw her ETH from the node, she performs the following operations: `startSnapshot`, `validateSnapshotProofs`, `startWithdraw`, and `finishWithdraw`.
4. Starting Snapshot
   * When `startSnapshot` is executed, `_transferToSlashStore` is called, slashing 3 ETH from her node and transferring them to `SlashStore`. This reduces `totalRestakedETH` to `29e18` in her Node struct and saves the new snapshot with `nodeBalanceWei = 29e18`.
5. Validate snapshot proofs 
   * Alice calls `validateSnapshotProofs` to validate her balance proofs:
```solidity
            int256 b
NodeOwners lose partial/full remaining stake after NativeVault suffers a slash request
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L456-L459


# Vulnerability details

## Impact
NodeOwners lose partial/full remaining stake after NativeVault suffers a slash request

## Proof of Concept
1. Native node is deployed on a NativeVault
2. One withdraw credentials was verified with the node.
3. ```node.lastSnapshotTimestamp``` is updated to the current time after ```validateSnapshotProofs()``` is called.
4. Few days after ```node.lastSnapshotTimestamp```, Node owner observed a slash request of 50% on the vault and decided to exit the validator on beacon.
5. After exiting on beacon, stakes are locked for about 27hrs before being pushed to withdraw queue.
6. Node owner waited to call ```startSnapshot()``` after withdrawal arrived, so that slash funds can be transferred to slash store.
7. Funds arrive to native node, node owner calls ```startSnapshot()``` and ``` _startSnapshot()``` is triggered.
8. In ``` _startSnapshot()```, ```_transferToSlashStore(nodeOwner)``` is triggered.
9. In ```_transferToSlashStore(nodeOwner)```, slash assets of node is computed to be the difference between the original restakedEth of the node which is 32_ETH, and what the shares now worth, which is 16_ETH ```32 - 16 = 16```
10. ```slashedWithdrawable``` is then computed to be the minimum between ```slashedAsset``` and node balance(which is 32_ETH atm!) ```min(32, 16) = 16```
11. ```slashedWithdrawable``` is then withdrawn from node to slash store
12. By now, node.balance is now 16_ETH
13. When flow is returned to ``` _startSnapshot()``` where ``` nodeBalanceWei``` is computed to be the difference between ```node.nodeAddress.balance``` which is now 16_ETH and ``` node.withdrawableCreditedNodeETH```(which could be assume to be zero considering that no previous reward update yet) ```16 - 0 = 16```
14. ```node.snapshot``` was then updated with 16_ETH as ``` nodeBalanceWei```
```solidity
        _transferToSlashStore(nodeOwner);

        // Calculate unattributed node balance
        uint256 nodeBalanceWei = node.nodeAddress.balance - node.withdrawableCreditedNodeETH;

        if (revertIfNoBalanceChange && nodeBalanceWei == 0) revert NoBalanceUpdateToSnapshot();

        NativeVaultLib.Snapshot memory snapshot = NativeVaultLib.Snapshot({
            parentBeaconBlockRoot: _getParentBlockRoot(uint64(block.timestamp)),
 @>         nodeBalanceWei: nodeBalanceWei, //@audit 16_ETH
            balanceDeltaWei: 0,
            remainingProofs: node.activeValidatorCount
        });

        node.currentSnapshotTimestamp = uint64(block.timestamp);
        _updateSnapshot(node, snapshot, nodeOwner); //@audit here snapshot is stored since remaingProofs > 0
```
15. Node owner then calls ```validateSnapshotProofs()``` where ```snapshot.balanceDeltaWe``` is updated to -32_ETH(withdrawn!)
https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/NativeVaultLi
Incorrect order of operations during a snapshot validation can result in stuck ETH due to an underflow.
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L456


# Vulnerability details

## Bug description
Karak native restaking utilizes a balance snapshot system to keep the `NativeNode` aware of the balance changes on the beacon chain. `NativeVault` updates the node owner's shares based on the balance changes on their `NativeNode` and the beacon chain balance of the validators.

There is a potential underflow of the `NativeVault.totalAssets()` and `node.totalRestakedETH` that can occur because an increase of the `NativeNode's` balance is not snapshotted before the slashing event.

Assume a scenario where a `NativeVault` has one staker (Alice) with 32 ETH restaked from the BeaconChain.
* NativeVault's totalAssets = 32 ETH.
* Alice.totalRestakedETH = 32 ETH.
* Current balance of the Alice's NativeNode = 0 ETH.
* 2 ETH accrues on Alice's NativeNode balance due to rewards from BeaconChain. (not yet snapshotted)
* A vault is slashed for 2 ETH.
* NativeVault's totalAssets becomes 30 ETH.
* startSnapshot() is called for Alice.

When `startSnapshot()` is called for Alice, the internal `_startSnapshot()` function is invoked, where `_transferToSlashStore(Alice)` is called.

[NativeVault.sol#L448-L456](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L448-L456)
```solidity
function _startSnapshot(
    NativeVaultLib.NativeNode storage node,
    bool revertIfNoBalanceChange,
    address nodeOwner
) internal {
    if (node.currentSnapshotTimestamp != 0)
        revert PendingIncompleteSnapshot();
    // Sweep slashed ETH from node balance before locking in snapshot node balance.
    // This allows all the ETH that was theoritically slashed in `slashAssets` function call can be moved to a slash store,
    // which can decide what to do with this slashed ETH.
    _transferToSlashStore(nodeOwner);
```

`_transferToSlashStore()` will sweep all of the slashed ETH from the NativeNode's balance.

[NativeVault.sol#L430-L439](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L430-L439)
```solidity
// slashed ETH = total restaked ETH (node + beacon) - share price equivalent ETH
uint256 slashedAssets = node.totalRestakedETH -
    convertToAssets(balanceOf(nodeOwner));
// sweepable ETH = min(ETH available on node address, total slashed ETH)
uint256 slashedWithdrawable = Math.min(
    node.nodeAddress.balance,
    slashedAssets
);
// withdraw sweepable ETH to slashStore
INativeNode(node.nodeAddress).withdraw(
    self.slashStore,
    slashedWithdrawable
);
// update total restaked ETH available (node + beacon)
node.totalRestakedETH -= slashedWithdrawable;
```

In the above code snippet `slashedAssets` will equal 2 ETH and `node.totalRestakedETH` will be decremented to 30 ETH.

After this, `_startSnapshot()` will proceed to create a snapshot that would need to be validated later.
An Operator can deploy a vault with a controlled slash store to receive slashed Eth from NativeVault 
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L59-L75


# Vulnerability details

## Impact
Due to a missing check on the slashStore address that is included in the encoded ```extraData``` in the configuration struct passed into ```Core.deployVaults()```, an Operator can easily pass in slash store contract that is not that of the Protocol and move all slashed assets away.

## Proof of Concept
Let us consider a Vault(one deployed with erc20s tokens as asset)
1. When an operator calls ```Core.deployVaults()``` with any token passed in as asset, flow is passed to ```_self().deployVaults(msg.sender, vaultImpl, vaultConfigs)```, where ```validateVaultConfigs()``` validates that each token to be used as an asset in each vault has a corresponding slashHandler contract.
```solidity
    function validateVaultConfigs(Storage storage self, VaultLib.Config[] calldata vaultConfigs, address implementation)
        public
        view
    {
        if (!(implementation == address(0) || isVaultImplAllowlisted(self, implementation))) {
            revert VaultImplNotAllowlisted();
        }
        for (uint256 i = 0; i < vaultConfigs.length; i++) {
 @>       if (self.assetSlashingHandlers[vaultConfigs[i].asset] == address(0)) revert AssetNotAllowlisted();
        }
    }
```

Also, when we check the finalization logic, it was observed that, the slashHandler address that was utilised is the one set by protocol admin in storage state
```solidity
    function finalizeSlashing(CoreLib.Storage storage self, QueuedSlashing memory queuedSlashing) external {
        bytes32 slashRoot = calculateRoot(queuedSlashing);
        if (!self.slashingRequests[slashRoot]) revert InvalidSlashingParams();
        if (queuedSlashing.timestamp + Constants.SLASHING_VETO_WINDOW > block.timestamp) {
            revert MinSlashingDelayNotPassed();
        }
        delete self.slashingRequests[slashRoot];

        for (uint256 i = 0; i < queuedSlashing.vaults.length; i++) {
            IKarakBaseVault(queuedSlashing.vaults[i]).slashAssets(
                queuedSlashing.earmarkedStakes[i],
@> self.assetSlashingHandlers[IKarakBaseVault(queuedSlashing.vaults[i]).asset()]
            );
        }
...code
    }
```
Set by protocol admin
```solidity
  function allowlistAssets(address[] memory assets, address[] memory slashingHandlers)
        external
   @>     onlyRolesOrOwner(Constants.MANAGER_ROLE) //PROTOCOL_ADMIN
    {
        _self().allowlistAssets(assets, slashingHandlers);
        emit AllowlistedAssets(assets);
    }
```
```solidity
      function allowlistAssets(Storage storage self, address[] memory assets, address[] memory slashingHandlers)
        external
    {
        if (assets.length != slashingHandlers.length) revert LengthsDontMatch();
        for (uint256 i = 0; i < assets.length; i++) {
            if (slashingHandlers[i] == address(0) || assets[i] == address(0)) revert ZeroAddress();
    @>