Beginner FriendlyFoundryBridge
100 EXP
View results
Submission Details
Severity: high
Invalid

BossBridge is at risk of signature malleability

Summary

sendToL1 calls ECDSA.recover with the hashed message, v, r, and s as paramters. ECDSA.recover using those parameters in turn calls ECDSA.tryRecover using the same parameters. ECDSA.tryRecover uses EVM's ecrecover function which still allows for signature malleability. Signature malleability attacks are where an attacker takes a valid signature and modifies it to create another valid signature. In this case, that would allow an attacker to withdraw funds that aren't theirs. Even having a function that checks that a signature isn't used more than once can't avoid signature malleability.

In ECDSA.sol, there are some suggestions for what to do to avoid signature malleability attacks, but these aren't implemented in BossBridge.sol.

Vulnerability Details

See the note in the tryRecover function (which is the function ultimately called by the sendToL1 function in BossBridge.sol:

function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}

Impact

An attacker may be able to modify a valid signature and steal funds that don't belong to them.

Tools Used

Manual review

Recommendations

Use this version of the ECDSA.recover function which takes the hashed message and the signature (i.e., v, r, s combined into one signature) instead because it is not vulnerable to signature malleability:

function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.