Signature-related Attacks¶
In smart contract systems, signatures are powerful tools that serve critical functions. The EVM features the ecrecover
precompile, allowing for native signature validity checks and recovery. This function is predominantly used in the context of authorization, data validity checks, and facilitating gas-less transactions. However, signature systems in smart contracts can malfunction in various ways, often leading to severe consequences.
Missing Validation¶
One of the most common vulnerabilities is missing validation when ecrecover
encounters errors and returns an invalid address.
1 2 3 4 |
|
A crucial check for address(0)
is absent in this instance. This omission allows an attacker to submit invalid signatures with arbitrary payloads yet pass as valid. A simple yet effective solution to this issue would be to include a check like the following:
1 |
|
Even better, OpenZeppelin's ECDSA
library should be used because it automatically reverts when invalid signatures are encountered.
Replay Attacks¶
Replay attacks occur when a signature and the system consuming it have no deduplication mechanism. A cause for replay attack vulnerabilities is when signatures are not properly invalidated or a nonce is absent from the system. The following examples underline the different attack angles on a smart contract signature system and its iterations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
In this scenario, an attacker possessing the owner's signature can perform the same action multiple times. For instance, if the owner signed a transaction authorizing a transfer of funds, the attacker could replay the signature and drain the contract by transferring funds repeatedly. To mitigate this issue, a mapping can invalidate each submitted signature after its first execution. However, a nonce must be encoded into the signed payload to allow the owner to sign the same action multiple times. The sole purpose of the nonce value is to change the final signature when the payload data remains the same.
The following code adds the signature invalidation mechanism as well as the nonce-related business logic.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Even this enhanced contract is not entirely secure. If the system is deployed on multiple chains or if the signer address is used in other contexts on different chains, signature replay attacks are still a potential threat.
Cross-chain Replay Attacks¶
Cross-chain replay attacks arise when signatures can be reused across different blockchain systems. Once a signature has been used and invalidated on one chain, an attacker can still copy it, use it on another, and trigger an unwanted state change. This poses a significant threat to smart contract systems deployed across chains with identical code.
To mitigate this risk, the chain ID should be encoded in the signature payload and validated against the current chain ID where the action is executed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
It's worth noting that when signatures are used with EIP712 typed data payloads, the domain separator value already includes the chain ID.
Frontrunning¶
Another common issue is frontrunning. Attackers can monitor the mempool for transactions using ECDSA signatures in certain systems, such as those where a reward is paid out for third parties executing a payload. Depending on the information in the signature payload, an attacker can frontrun the original transaction, manipulate specific parameters, and exploit the system.
Regarding the example above, a signature vulnerable to frontrunning attacks would emerge if the valid signature hash were to be calculated as follows:
1 |
|
With the param1
parameter missing, a frontrunning attacker can arbitrarily set the value of param1
and potentially exploit the system. It is paramount that all parameters participating in the execution of business logic triggered by the signature are included therein.
Signature Malleability¶
Signature malleability is a characteristic of digital signatures. In Ethereum, an ECDSA signature is represented by two 32-byte sized, r
and s
values, and a one-byte recovery value, v
. The symmetric structure of elliptic curves implies that no signature is unique. A consequence of these "malleable" signatures is that they can be altered without being invalidated.
For every set of parameters {r, s, v}
used to create a signature, another distinct set {r', s', v'}
results in an equivalent signature. Therefore, when a smart contract system uses ecrecover
directly instead of a well-known library like OpenZeppelin's ECDSA
, detecting and discarding malleable signatures is essential.
OpenZeppelin's ECDSA library contains the following code to prevent forged signatures:
1 2 3 |
|
This measure stops signature malleability attacks since most signatures from current libraries yield a unique signature with an s-value in the lower half order. It is vital to the signature validation library that this check is in place.
EIP-2098 Compact Signatures¶
The ECDSA.recover
and ECDSA.tryRecover
methods are susceptible to a specific form of signature malleability, owing to their ability to process both EIP-2098 compact signatures and the conventional 65-byte signature format. However, this issue is relevant only to the functions which accept a single byte argument and does not impact the ones that take {r, v, s}
or {r, vs}
as separate arguments.
The contracts that could be affected are those that implement strategies of signature reuse or replay protection by marking the signature itself as 'used' instead of the signed message. In this case, a user might take an already submitted signature, re-submit it in a different format, such as a compact signature, and circumvent the established protection mechanism.
This issue only affects the OpenZeppelin contracts below and not including version 4.7.3
. The related security advisory can be found here.