Invalid Validation in _addCalldataCheck
is causing dead bytes in payload
#54
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
duplicate-17
edited-by-warden
🤖_10_group
AI based duplicate group recommendation
satisfactory
satisfies C4 submission criteria; eligible for awards
sufficient quality report
This report is of sufficient quality
Lines of code
https://github.com/code-423n4/2024-10-kleidi/blob/ab89bcb443249e1524496b694ddb19e298dca799/src/Timelock.sol#L1119-L1123
https://github.com/code-423n4/2024-10-kleidi/blob/ab89bcb443249e1524496b694ddb19e298dca799/src/BytesHelper.sol#L50-L55
Vulnerability details
Vulnerability Details
In Kleidi protocol, whitelisting a calldata feature is used for restricting the parameters of the specific functions at specific external contracts. Hot signers only can use whitelisted parameters. The calldata indexes are defined as bytes and the overlapping between the checks are not allowed for security considerations. This check is applied in following line:
This check is prevent overlapping between the calldata checks. But this check doesn't correctly handle the edge case for the
endIndex
. Let say we have a function which takes two parameter as follows:test(uint256 parameter1, bytes32 parameter2)
The calldata payload for this function will be 4 bytes selector + 32 bytes parameter1 + 32 bytes parameter2. As we mentioned before it's bytes type parameter and it slice the bytes data using indexes as follows in BytesHelper.sol contract:
Let's continue with our example, our parameter1's startIndex should be 4 because selector is represented in [0,3] (both inclusive) range. Our length for uint256 parameter will be 32 and the
endIndex
for parameter1 should be 4 + 32 = 36. The range of parameter will be [4,36). The index 36 is exclusive in here because if we make both 4 and 36 inclusive the length will be 33. Basicly, index 36 is not used in here by our first calldata check. When user want to restrict the bytes32 parameter the index range for parameter2 should be [36,68). But we can't use index 36 in here because it is theendIndex
of our first calldata check.This vulnerability will cause dead bytes between the parameters. If we have n amount of parameters and we want to restrict the parameters for all of them the dead bytes amount will be n - 1.
Impact
Those dead bytes will cause wildcarded parameters because if a selector in a contract is restricted by only 1 parameter the all the other parameters is wildcarded by default which is a design choice for the protocol. We can understand that behaviour in following calldata check:
If a function has at least 1 calldata check and if it passes then the payload is whitelisted in here.
Deep dive to the impact
Let say in a function we have 5 parameters and we want to restrict the parameters for the hotsigners and all the parameters are uint256 type. The only way to restrict it is using following ranges [4,36) [37, 68) [69, 100) [101, 132), [133, 164). Following indexes are dead right now: 36, 68, 100, 132 and they are automatically wildcarded. Normally, hot signers can only use that function with that specific parameters but now due to dead bytes ( there are 4 dead bytes ). They can call many other calldata payload by changing the first 8 bits of the parameters. If any hot signer is compromised, the problem becomes much bigger. Multisig wallet can face with huge loses even though only safe calldatas are added to the system.
The following statement in EDGECASES.md file will be wrong. It doesn't guarantee a match the whitelisted calldata and the protocol's main purpose is being ensure about safety even if the user signed a malicious transaction off-chain but it's not possible in current implementation.
Coded PoC
The following add calldata test will fail. In this PoC we restrict both address of deposit and deposit amount by the calldata check. You can add this to
./test/unit/Timelock.t.sol
and call it withforge test --match-test testWhitelistingBatchCalldataSucceeds2 -vvv
command to see where the error occur.Recommended Mitigation
The
endIndex
of previous parameter should be able to equal to the next parameter'sstartIndex
and also the next parameter'sstartIndex
should be able to equal to previous parameters'sendIndex
Assessed type
Invalid Validation
The text was updated successfully, but these errors were encountered: