Summary
The length of a proper signature is 65. You should confirm that the length of the signature is 65 before allowing withdrawals.
Vulnerability Details
There is no length validation of the signature:
function sendToL1(
uint8 v,
bytes32 r,
bytes32 s,
bytes memory message
) public nonReentrant whenNotPaused {
address signer = ECDSA.recover(
MessageHashUtils.toEthSignedMessageHash(keccak256(message)),
v,
r,
s
);
if (!signers[signer]) {
revert L1BossBridge__Unauthorized();
}
(address target, uint256 value, bytes memory data) = abi.decode(
message,
(address, uint256, bytes)
);
(bool success, ) = target.call{value: value}(data);
if (!success) {
revert L1BossBridge__CallFailed();
}
}
}
Impact
Prevent attackers from feeding input that you know is invalid into your functions, lessening the attack surface. Also, if your own signature production goes awry in a way that results in a signature of invalid length, this check prevents further damage from resulting by reverting upon signatures of an invalid length.
Tools Used
Foundry
Recommendations
Add the following check to the beginning of the sendToL1 function...note that this also reflects the other changes I recommended, including using one combined signature over v, r, and s separately and also adding a timestamp so that signature expire if not used:
function sendToL1(
bytes memory signature,
uint256 timestamp,
bytes memory message
) public nonReentrant whenNotPaused {
if(signature.len != 65) {
revert BossBridge__InvalidSignature}
if(block.timestamp > timestamp + 5 min) {
revert BossBridge__SignatureExpired();}
address signer = ECDSA.recover(
MessageHashUtils.toEthSignedMessageHash(keccak256(message));,
signature
);
if (!signers[signer]) {
revert L1BossBridge__Unauthorized();
}
(address target, uint256 value, bytes memory data) = abi.decode(
message,
(address, uint256, bytes)
);
(bool success, ) = target.call{value: value}(data);
if (!success) {
revert L1BossBridge__CallFailed();
}
}
}