CVE-2025-21607 – vyper
Package
Manager: pip
Name: vyper
Vulnerable Version: >=0 <0.4.1
Severity
Level: Low
CVSS v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N/E:P/RL:O/RC:R
CVSS v4.0: CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
EPSS: 0.00071 pctl0.22193
Details
Vyper Does Not Check the Success of Certain Precompile Calls ### Summary When the Vyper Compiler uses the precompiles EcRecover (0x1) and Identity (0x4), the success flag of the call is not checked. As a consequence an attacker can provide a specific amount of gas to make these calls fail but let the overall execution continue. Then the execution result can be incorrect. Based on EVM's rules, after the failed precompile the remaining code has only 1/64 of the pre-call-gas left (as 63/64 were forwarded and spent). Hence, only fairly simple executions can follow the failed precompile calls. Therefore, we found no significantly impacted real-world contracts. The fix is tracked in https://github.com/vyperlang/vyper/pull/4451. ### Details #### The relevant precompiles ##### EcRecover EcRecover is used in vyper's `ecrecover` built-in. As the precompile consumes 3000 gas, any execution after an out-of-gas EcRecover call has at most 47 gas left. ##### Identity - The Identity precompile is used in vyper to perform memory copy operations. As its cost is variable, a variable amount of gas might be left after a failed call. The bigger the copy operation, the more gas can be left. Hence, a failed call to Identity could theoretically be followed by successful storage changes or emitted events. - Identity is no longer used when `evm-version` `cancun` is used (because `MCOPY` is used instead). In 0.4.0 `cancun` is default, in 0.3.10 `cancun` is an option, otherwise `cancun` is not available. As only pre-`cancun` versions are relevant, we don't have to consider transient storage operations succeeding a failed call to Identity. #### The other precompiles - Calls to `Sha2`, `ecAdd`, and `ecMul` have success checks and have had them for a long time. - The precompiles `modexp`, `ripe`, `blake`, `ecPairing`, and `Point Evaluation` have no builtins in vyper. ### PoC In the following we provide concrete examples of incorrectly generated bytecode. These examples are not optimized, but rather Proof-of-Concepts. The list is also not exhaustive. #### `ecrecover` use - Affected versions: 0.2.0 - 0.4.0 - For older compiler versions (<=0.3.9) it behaves similarly to this [older advisory](https://github.com/vyperlang/vyper/security/advisories/GHSA-f5x6-7qgp-jhf3). As no data is returned, the previous value of the memory word is returned to the user. Hence, any dirty bytes might be returned. Contracts with older compiler versions and `ecrecover` were checked. - For new vyper versions, the output buffer is zeroed, so when the call fails zero is returned. This is an incorrect result, but developers should anyway check for 0 as a failure case. Hence, this is unlikely to result in issues. However, we did search for such cases. - As mentioned above at most 47 gas is left after the failed call, hence a `return` is the most realistic scenario to be attacked. Vulnerable Code: ```py @external @view def foo(hash: bytes32, v: uint256, r:uint256, s:uint256) -> address: return ecrecover(hash, v, r, s) ``` Problematic Call: ```py print( c.foo( binascii.unhexlify( "6c9c5e133b8aafb2ea74f524a5263495e7ae5701c7248805f7b511d973dc7055" ), 28, 78616903610408968922803823221221116251138855211764625814919875002740131251724, 37668412420813231458864536126575229553064045345107737433087067088194345044408, ) ) # Returns 0x9eE53ad38Bb67d745223a4257D7d48cE973FeB7A print( c.foo( binascii.unhexlify( "6c9c5e133b8aafb2ea74f524a5263495e7ae5701c7248805f7b511d973dc7055" ), 28, 78616903610408968922803823221221116251138855211764625814919875002740131251724, 37668412420813231458864536126575229553064045345107737433087067088194345044408, gas=3000, ) ) # Returns 0x0000000000000000000000000000000000000000 ``` #### Identity to copy Dynamic Arrays - Affected versions: 0.3.2 - 0.3.9 - Dynamic Arrays might be copied on different occasions - That copy operation can fail leading to incorrect accesses afterwards Vulnerable Code: ```py @external def foo() -> uint256: a: DynArray[uint256, 4000] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Metadata
Created: 2025-01-14T16:34:20Z
Modified: 2025-04-24T14:39:58Z
Source: https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/01/GHSA-vgf2-gvx8-xwc3/GHSA-vgf2-gvx8-xwc3.json
CWE IDs: ["CWE-670", "CWE-703"]
Alternative ID: GHSA-vgf2-gvx8-xwc3
Finding: F164
Auto approve: 1