Closed code423n4 closed 2 years ago
Always checking isContract
adds significant overhead in the standard case where it's not 1271 — this is a known limitation and would be a highly unusual application of eip-1271 (i.e. passing in something that isn't a signature, but has the exact length as a signature). This should be documented but is not a significant issue in our view.
EIP-1271 is pretty open ended in terms of what may be considered acceptable, supporting signature schemes other than just ECDSA. The current implementation assumes that the signature is a valid ECDSA signature so when the length happens to match but a different scheme was used, it's possible that verification will fail before the call to _assertValidEIP1271Signature
.
Lowering to a Low severity since the impact is presumably rare. It does have the potential to prevent the contract from interacting with seaport but doesn't lead to other negative consequences.
Grouping with the warden's QA report #188
Lines of code
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/SignatureVerification.sol#L39-L100
Vulnerability details
Impact
EIP-1271 Signature may contains 64 or 65 bytes but isn't EIP-2098 signature. But it may be reverted if it isn't EIP-2098 signature.
EIP-1271 Signature that contains 64 or 65 bytes may be custom kind of signature for example it may be keccak256 of some value or merkle tree root.
In the case that 64 or 65 bytes EIP-1271 Signature is not EIP-2098 signature. It will be reverted with
InvalidSignature()
orBadSignatureV(v)
because it is checked with EIP-2098 verification algorithm first and recoveredSigner may be address(0)Let assume signer contract accept this signature (return correct magicValue)
In reality, this signature should be valid as calling
isValidSignature
return correct magicValue due to assumption above.Proof of Concept
Assumption
Assume current signature is a valid EIP-1271 signature (calling
isValidSignature
on signer return correct magicValue).Case signature length 64
This code block will be passed. But ecrecover may return 0
If ecrecover return 0
It will be reverted with
InvalidSignature()
.But in fact it should be valid due to assumption above. ( must perform
_assertValidEIP1271Signature(signer, digest, signature);
)If ecrecover return non-zero address
This case is valid as it is calling
_assertValidEIP1271Signature(signer, digest, signature);
as expectedCase signature length 65
In this case, It may be reverted with revert
BadSignatureV(v);
if length is 65 but uint8(signature[64]) is not 27 or 28.But in fact it should be valid due to assumption above. ( must perform
_assertValidEIP1271Signature(signer, digest, signature);
)Other length
Signature of other length is verified directly into signer contract by calling
_assertValidEIP1271Signature(signer, digest, signature);
, so it will be passed.Tools Used
Manual code review
Recommended Mitigation Steps
Check isContract on signer first, if it is contract only verify EIP-1271 as contract never sign EIP-2098 under his address.
If signer is an EOA, only check for EIP-2098 as EOA never use EIP-1271.
If signer is an EOA but signature is malformed or recovered to different signer, revert with
InvalidSignature()
orBadSignatureV(v)
.Change code to be like this