xanoutas/solidity-security-findings huggingface dataset:
Operator can become  one of the trusted roles
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The operator can have the ability to change the implementation of the contract, potentially to a malicious one where assets can be drained.

The operator can also denied withdrawal by pausing the [withdraw](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L282) function of the [nativeNode](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeNode.sol#L39)

## Bug Description
As per the karak [implementation](https://docs.karak.network/security/governance#pauser) and according to the readMe, one of the trusted roles is the `MANAGER` role, where it has the ability to pause some certain functions in case of an emergency
> `To ensure pauses are prompt, 4 team wallets are given a manager role which strictly enables them to pause the Karak contracts in an emergency situation, but hold no other privileges.` 

and as per as the conversation i had with the sponsor, the operator should just  have the ability to stake and unstake its vault

> So there's no manager of vault, only owner, and it's the core not operator.
Operator just has the functionality to stake/unstake it's vault.


But due to the way the vault deployment has been implemented,
```solidity
    /// @notice Deploys a new restaking vault with the given configuration to an operator
    /// @dev Emits a DeployedVault event for each vault deployed
    /// @dev It is meant to be permissionless since the setting of the standard vault implementation is permissioned
    /// @param vaultConfigs: The configurations of the vaults to deploy
    /// @param vaultImpl: The address of the vault implementation to use
    /// @return vaults {IKarakBaseVault[]} The addresses of the deployed vaults
    function deployVaults(VaultLib.Config[] calldata vaultConfigs, address vaultImpl)
        external
        whenFunctionNotPaused(Constants.PAUSE_CORE_DEPLOY_VAULTS)
        nonReentrant
        returns (IKarakBaseVault[] memory vaults)
    {
        vaults = _self().deployVaults(msg.sender, vaultImpl, vaultConfigs);
    }
```
 an operator can set the `MANAGER` of his vault during [deployments](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L161) by utilizing the [extraData](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/VaultLib.sol#L16) of the [config struct](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/VaultLib.sol#L9) which would later be decoded and used during the [initialization](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L59-L60) to set up the vault, this can be native or the normal vault.

This would
Operator can evade slashing by using unregistered slashHandler address 
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
An operator can evade slashing from the `core` and make the call to revert whenever that happens. This is because , the operator has put an address that should not have been allowed there.

## Bug Description
During the vault [deployments](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L167) the [vaultConfigs's](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/VaultLib.sol#L9-L17)  `extraData` was not sanitized enough.

```solidity
function deployVaults(VaultLib.Config[] calldata vaultConfigs, address vaultImpl)
        external
        whenFunctionNotPaused(Constants.PAUSE_CORE_DEPLOY_VAULTS)
        nonReentrant
        returns (IKarakBaseVault[] memory vaults)
    {
>        vaults = _self().deployVaults(msg.sender, vaultImpl, vaultConfigs);
    }
```
This oversight allows the `operator` to be able to set up the [slashHandler](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L75) or the `slashStore` that when an operator is slashed gets to hold the slashed assets and to decide what to do with it as per the protocol.

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L135
```solidity
   for (uint256 i = 0; i < queuedSlashing.vaults.length; i++) {
135            IKarakBaseVault(queuedSlashing.vaults[i]).slashAssets(
                  queuedSlashing.earmarkedStakes[i],
             
   self.assetSlashingHandlers[IKarakBaseVault(queuedSlashing.vaults[i]).asset()]
            );
        }
```

This will now allow the operator to evade slashing, because whenever the [finalizeSlashing](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/SlasherLib.sol#L126) is called, the function will revert.

This is because, the `operator` has set up an unrecognized address as the handler/slashStore.

This is where the call from the core will revert

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/NativeVault.sol#L308C9-L308C72
```solidity
if (slashingHandler != self.slashStore) revert NotSlashStore();
```

## Tools Used

Manual review

## Recommended Mitigation Steps

I recommend that the slash handler be sanitized or supplied independently thats sourced from the the [AllowListed](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/CoreLib.sol#L27) where the `slashHandler` is the one that has been verified already through the [allowlistAssets](https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/Core.sol#L85C14-L85C29) function.
```solidity
        mapping(address asset => addr
NodeOwners can evade DSS slash in NativeVault.
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Vaults are liable to slashing once the vault operator acts maliciously. DSS initiates a slash process by first calling ```Core.requestSlashing()```, which must be followed by a veto window period to allow veto committee justify slash request or cancel. 

If veto committee finds the request justifiable, anyone can call ```Core.finalizeSlashing()``` to finalize the process.

However, it is worth noting that slashing works differently in NativeVault. In a regular vault, if a user tries to escape slash requires by redeeming, it takes 7 days for the user to finalise withdraw(also, shares would have been transferred to vault), if slashing is justified, user cannot escape the slash.

In NativeVault, since funds are on Beacon, finalizing a slash process involves reducing totalAsset by the slash amount, so that node owners' share valuations are reduced in the ratio of slash. (32e18 shares now worth 16e18_ETH after a 50% slash).

The issue with this method is the validation check done in ```NativeVault.slashAssets()``` to prevent negative ```totalAsset```.
```solidity
  function slashAssets(uint256 totalAssetsToSlash, address slashingHandler)
        external
        onlyOwner
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_NATIVEVAULT_SLASHER)
        returns (uint256 transferAmount)
    {
        NativeVaultLib.Storage storage self = _state();

        if (slashingHandler != self.slashStore) revert NotSlashStore();

        // avoid negative totalAssets if slashing amount is greater than totalAssets
@>>>      if (totalAssetsToSlash > self.totalAssets) {
@>>>          totalAssetsToSlash = self.totalAssets;
        }

        self.totalAssets -= totalAssetsToSlash;
        emit Slashed(totalAssetsToSlash);
        return totalAssetsToSlash;
    }
```
NativeVault uses a virtual asset, it increases when node owners validate a withdrawal credential to the native node address and decreases in any snapshot update that returns a negative change in the validator beacon balance.

This means a validator can monitor a slash request on NativeVault and instantly initiate exit on Beacon before calling ```validateSnapshotProofs ()``` to burn shares and reduce total assets.

Now, node owner shares have been burnt before the arrival of Eth, and total assets have also been reduced, the node owner then calls ```startSnapshot()``` to record the node balance difference and mint shares which can then be used to initiate withdrawal.

## Proof of Concept
1. DSS initiates a slash request on a NativeVault.(48hrs wait!)
2. Validator(s) instantly initiated exit on Beacon(27hrs wait before processing withdrawal to node).
3. Node owner(s) calls ```startSnapshot()``` before any incre
operator customizes the slashStore address, it results in slashAssets always reverting
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L75
https://github.com/code-423n4/2024-07-karak/blob/main/src/entities/CoreLib.sol#L89-L116


# Vulnerability details



## Impact
operator can customizes the `slashStore` address when create new vault, it results in slashAssets always reverting , if the `slashStore` address is different with `slashingHandler`.

## Proof of Concept
DSSs have the right to slash vaults staked to it if it feels that an operator had failed to perform its tasks adequately.
From the [NativeVault.sol::slashAssets](https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L299-L318)
```solidity
function slashAssets(uint256 totalAssetsToSlash, address slashingHandler)
    external
    onlyOwner
    nonReentrant
    whenFunctionNotPaused(Constants.PAUSE_NATIVEVAULT_SLASHER)
    returns (uint256 transferAmount)
{
    NativeVaultLib.Storage storage self = _state();

    if (slashingHandler != self.slashStore) revert NotSlashStore();   <@
```
there is a `slashingHandler != self.slashStore` check.

The `slashingHandler` is comes from `assetSlashingHandlers` setting. It's a mapping data structure, where each asset corresponds to a specific slashingHandler address. `MANAGER_ROLE` in core.sol can set above mapping via [Core.sol::allowlistAssets](https://github.com/code-423n4/2024-07-karak/blob/main/src/Core.sol#L85-L91)
```solidity
function allowlistAssets(address[] memory assets, address[] memory slashingHandlers)
    external
    onlyRolesOrOwner(Constants.MANAGER_ROLE)
{
    _self().allowlistAssets(assets, slashingHandlers);
    emit AllowlistedAssets(assets);
}
```

While the `slashStore` is an operator custom address pass in via extraData when creating new vault [CoreLib.sol::createVault](https://github.com/code-423n4/2024-07-karak/blob/main/src/entities/CoreLib.sol#L89-L116)
```solidity
function createVault(
    Storage storage self,
    address operator,
    address depositToken,
    string memory name,
    string memory symbol,
    bytes memory extraData,
    address implementation
) internal returns (IKarakBaseVault) {
    // Use Create2 to determine the address before hand
    bytes32 salt = keccak256(abi.encodePacked(operator, depositToken, self.vaultNonce++));

    address expectedNewVaultAddr =
        LibClone.predictDeterministicAddressERC1967BeaconProxy(address(this), salt, address(this));

    self.vaultToImplMap[address(expectedNewVaultAddr)] = implementation;

    IKarakBaseVault vault = cloneVault(salt);
    vault.initialize(address(this), operator, depositToken, name, symbol, extraData);  <@
```

[NativeVault.sol::initialize](https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L46-L79)
```solidity
(address manager, address slashStore, address nodeImplementation) =
    abi.decode(_extraData, (address, address, address));    <@

if (manager == address(0) || slashStore == address(0) || nodeImplementation == address(0)) revert ZeroAddre
The operator can use any arbitrary nodeImplementation and grant themselves the manager role
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L53-L79


# Vulnerability details

## Impact
The operator can use any arbitrary nodeImplementation and grant themselves the manager role.
- operator can use a malicious `nodeImplementation` address
- operator can change current `nodeImplementation` to a malicious address

## Proof of Concept
The operator can utilize arbitrary extraData, which can subsequently be decoded to reveal the manager address and nodeImplementation address. The system then grants the MANAGER_ROLE to the specified manager address and sets the provided nodeImplementation as the default nodeImpl address [NativeNode.sol::initialize](https://github.com/code-423n4/2024-07-karak/blob/main/src/NativeVault.sol#L53-L79)
```solidity
function initialize(
    address _owner,
    address _operator,
    address _depositToken,
    string memory _name,
    string memory _symbol,
    bytes memory _extraData
) external initializer {
    _initializeOwner(_owner);
    __Pauser_init();

    if (_depositToken != Constants.DEAD_BEEF) revert DepositTokenNotAccepted();

    (address manager, address slashStore, address nodeImplementation) =
        abi.decode(_extraData, (address, address, address));

    if (manager == address(0) || slashStore == address(0) || nodeImplementation == address(0)) revert ZeroAddress();
    _grantRoles(manager, Constants.MANAGER_ROLE);    <@

    NativeVaultLib.Storage storage self = _state();
    VaultLib.Config storage config = _config();

    config.asset = _depositToken;
    config.name = _name;
    config.symbol = _symbol;
    config.decimals = 18;
    config.operator = _operator;
    config.extraData = _extraData;

    self.slashStore = slashStore;
    self.nodeImpl = nodeImplementation;   <@

    emit NativeVaultInitialized(_owner, manager, _operator, slashStore);
}
```


test:

```solidity
    function testOperatorCanChangeTheImplementation() public {
        // use a random address
        address nativeNodeImpl = makeAddr('nativeNodeImpl');

        //operator can specific a slashStore address.
        address slashStoreRandom = makeAddr('randomAddress');

        // Deploy Vaults
        VaultLib.Config[] memory vaultConfigs = new VaultLib.Config[](1);
        vaultConfigs[0] = VaultLib.Config({
            asset: Constants.DEAD_BEEF,
            decimals: 18,
            operator: operator,
            name: "NativeTestVault",
            symbol: "NTV",
            extraData: abi.encode(address(operator), slashStoreRandom, address(nativeNodeImpl))
        });

        vm.prank(operator);
        IKarakBaseVault[] memory vaults = core.deployVaults(vaultConfigs, address(0));

        nativeVault = NativeVault(address(vaults[0]));

        //operator has manager role.
        assertTrue(nativeVault.hasAnyRole(operator, Constants.MANAGER_ROLE));
    }
```

out:
```shell
Ran 1 test for test/nativeRestaking/nativeVault.t.sol:NativeVaultTest
[PASS] testOperatorCanChangeTheIm
NativeVault's `MANAGER_ROLE` is untrusted and allows malicious operators to steal all funds from the native nodes
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
`NativeVault.sol`'s `changeNodeImplementation` function can only be called by the `Core` contract or the `MANAGER_ROLE`. As this function is never called in the `Core` contract, practically only the manager of the Native Vault can call it. 

As a result, through the `changeNodeImplementation` function malicious Native Vault managers can steal all funds from users' nodes or may prevent vaults from being slashed.

It is important to note that even though Native Vault managers are granted a role with the same name as the trusted Core manager's role, the two roles are completely different, as the Native Vault manager's role is granted by untrusted addresses.

The issue is that the manager is specified by the operator of the Native Vault. As operators are not trusted, Native Vault managers can also not be trusted and may be malicious contracts.

## Proof of Concept
As we can see `changeNodeImplementation` allows the untrusted manager to change the implementation contract of the Native node:
```solidity
    function changeNodeImplementation(address newNodeImplementation)
        external
        onlyOwnerOrRoles(Constants.MANAGER_ROLE)
        whenFunctionNotPaused(Constants.PAUSE_NATIVEVAULT_NODE_IMPLEMENTATION)
    {
        if (newNodeImplementation == address(0)) revert ZeroAddress();

        _state().nodeImpl = newNodeImplementation;
        emit UpgradedAllNodes(newNodeImplementation);
    }
```

Therefore, any funds stored in the Native node contracts may be stolen by simply changing the implementation to a malicious contract. Furthermore, this can prevent the slash store from receiving any slashed funds.

## Tools Used
Manual review

## Recommended Mitigation Steps
Perhaps, Native Vault managers should be the same as the manager of the Core contract.


## Assessed type

Access Control
Operators can steal slashed funds
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/f5e52fdcb4c20c4318d532a9f08f7876e9afb321/src/entities/CoreLib.sol#L107


# Vulnerability details

## Impact
Operators need to specify the address of the `SlashStore` which will receive any funds that are slashed from Native Vaults. The issue is that as operators are not trusted (they are not included in the `All trusted roles in the protocol` section of the contest page) there is a high danger of malicious operators setting the slash store to an address they control in order to be able to steal all assets that are slashed from their vaults.

## Proof of Concept
When Native Vaults are deployed in `Core.sol` the caller/operator specifies the address of the `SlashStore` in the `extraData` field. Currently, there is no validation on what that address could be, allowing malicious operators to specify a contract that they have full control over. Thus, when the DSS slashes their Native Vaults they will retain possession of the slashed funds.

## Tools Used
Manual review

## Recommended Mitigation Steps
Perhaps, making validations that the `SlashStore` address, specified in the `extraData` field, is a secure contract would be a sufficient fix. This can be achieved by implementing a whitelist of secure `SlashStore` addresses.


## Assessed type

Invalid Validation
DSS can deposit assets into the vault by oneself, which can lead to vault users losing more slashed assets
Audit: 2024-07-karak-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-karak/blob/main/src/Core.sol#L220-L231


# Vulnerability details

## Impact
DSS can deposit assets into the vault by oneself, which can lead to vault users losing more slashed assets.

## Proof of Concept
The protocol implements two distinct time windows to manage asset withdrawals and slashing:
- Users have a 9-day period to redeem assets from the vault.
- DSS has a 2-day window to execute slashing on the vault.

These time windows are designed to prevent users from circumventing slashing penalties by withdrawing assets prematurely. 

However, a potential vulnerability exists in this mechanism:
DSS, having control over the slashing timestamp and the ability to deposit assets into the vault, could exploit this system. The exploit would proceed as follows:

- DSS deposits a significant amount of assets into the target vault.
- DSS initiates the slashing process, triggering the 2-day slashing window.

Just before calling finalizeSlashing at the end of the 2-day window, DSS withdraws the previously deposited assets.

test:
```solidity
function testDSSCanSlashMoreAssets() public {
    address alice = makeAddr('alice');
    address bob = makeAddr('bob');
    address dssUser = makeAddr('dssUser');
    address dssContract = address(this);

    core.registerDSS(10e18);
    vm.prank(operator);
    core.registerOperatorToDSS(IDSS(dssContract),'');

    depositToken.mint(alice, 1200);
    depositToken.mint(bob, 1200);
    depositToken.mint(dssUser, 1200);

    vm.startPrank(alice);
    depositToken.approve(address(vault), 1200);
    vault.deposit(1200, alice);
    vm.stopPrank();

    vm.startPrank(bob);
    depositToken.approve(address(vault), 1200);
    vault.deposit(1200, bob);
    vm.stopPrank();

    vm.startPrank(dssUser);
    depositToken.approve(address(vault), type(uint256).max);
    vault.approve(address(vault), type(uint256).max);
    vault.deposit(1200, dssUser);
    vm.stopPrank();

    //register vault to dss.
    Operator.StakeUpdateRequest memory stakeVault = Operator.StakeUpdateRequest({
        vault:address(vault),
        dss:IDSS(dssContract),
        toStake:true
    });
    vm.prank(operator);
    Operator.QueuedStakeUpdate memory updatedStake = core.requestUpdateVaultStakeInDSS(stakeVault);
    skip(9 days + 1);

    core.finalizeUpdateVaultStakeInDSS(updatedStake);

    //dss startRedeem.
    vm.startPrank(dssUser);
    bytes32 withdrawalKey = vault.startRedeem(vault.balanceOf(dssUser), dssUser);
    vm.stopPrank();

    skip(7 days + 1);
    
    //dss request slash specific vault.
    uint96[] memory percentages = new uint96[](1);
    address[] memory vaultAddresses = new address[](1);
    percentages[0] = 10e18; //10% percent.
    vaultAddresses[0] = address(vault);
    SlasherLib.SlashRequest memory slashingRequest = SlasherLib.SlashRequest({
        operator: operator,
        slashPercentagesWad: percentages,
        vaults: vaultAddresses
    });
    SlasherLib.QueuedSlas