CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-5HR4-253G-CPX2
4.0

GHSA-5hr4-253g-cpx2: Server-Side Request Forgery in web3.py via CCIP Read

Amit Schendel
Amit Schendel
Senior Security Researcher

Apr 4, 2026·8 min read·2 visits

PoC Available

Executive Summary (TL;DR)

web3.py automatically resolves CCIP Read requests by default, failing to validate destination URLs returned by smart contracts. This allows malicious contracts to force the hosting server to make arbitrary HTTP requests to internal networks or cloud metadata services.

A Server-Side Request Forgery (SSRF) vulnerability exists in the web3.py library due to the default-enabled implementation of EIP-3668 (CCIP Read). The library performs unvalidated HTTP GET and POST requests to URLs supplied by untrusted smart contracts during OffchainLookup revert operations.

Vulnerability Overview

The web3.py library implements EIP-3668, commonly known as CCIP Read, which allows smart contracts to delegate data retrieval to external off-chain HTTP servers. When a contract function call reverts with an OffchainLookup error, it supplies a payload containing an array of URLs. The vulnerability, tracked as GHSA-5hr4-253g-cpx2 (CWE-918), occurs because web3.py natively parses this payload and immediately dispatches HTTP requests to the provided URLs without validating the target destination.

This behavior is highly problematic because the CCIP Read functionality is enabled by default across all provider instances in the library (global_ccip_read_enabled = True). Any Python application using web3.py to interact with untrusted or user-supplied smart contract addresses exposes its underlying infrastructure to Server-Side Request Forgery (SSRF). The automatic execution happens transparently during standard contract interaction methods, most notably the .call() function.

The attack surface extends to any backend system, indexing service, or automated trading bot utilizing the vulnerable library. Because the library processes both synchronous and asynchronous network requests using default configurations in the requests and aiohttp packages, it inherits the default behaviors of those libraries. This includes following HTTP redirects natively, circumventing rudimentary network boundaries.

The impact relies entirely on the network position of the system running the web3.py code. An attacker can direct the library to probe internal subnets, interact with unauthenticated local APIs, or access cloud provider metadata endpoints. The vulnerability scores a CVSS v4.0 rating of 4.0, reflecting the low complexity of exploitation and the direct threat to subsequent internal systems.

Root Cause Analysis

The root cause of this SSRF vulnerability is the absence of programmatic destination policy controls within the CCIP Read exception handling routines. When the eth_call method encounters an OffchainLookup revert, execution is passed to handle_offchain_lookup in web3/utils/exception_handling.py. This function extracts the urls array from the revert payload and iterates through it, performing string substitution for the {sender} and {data} parameters.

Critically, the URL string constructed from the payload undergoes no security validation before being passed to the HTTP client. The implementation fails to enforce a strict URL scheme restriction, allowing the use of http:// instead of requiring https://. Furthermore, it lacks any mechanism to filter out dangerous hostnames or IP addresses. Requests to the local loopback interface (127.0.0.1), link-local addresses used for cloud metadata (169.254.169.254), and private RFC1918 subnets are processed exactly the same as requests to public web servers.

The library dynamically decides between issuing an HTTP GET or an HTTP POST request based on the presence of the {data} and {sender} template literals in the supplied URL. If both templates are present, the library issues a GET request with the parameters formatted into the query string. If either template is missing, the library issues a POST request, placing the parameters into a JSON-formatted HTTP body. This dynamic method selection provides an attacker with the capability to execute state-changing POST requests against internal endpoints.

Additionally, both the requests.Session() object used in the synchronous implementation and the aiohttp.ClientSession() object used in the asynchronous implementation follow HTTP redirects by default. An attacker can bypass potential network-level intrusion detection systems by providing a benign public URL that subsequently responds with an HTTP 302 redirect pointing to an internal target. The library transparently follows the redirect, executing the SSRF payload.

Code Analysis

The vulnerability resides in the core exception handling logic responsible for EIP-3668 resolution. The following code snippet demonstrates the vulnerable synchronous implementation located in web3/utils/exception_handling.py. The aiohttp variant shares the same logical flaws.

session = requests.Session()
for url in offchain_lookup_payload["urls"]:
    formatted_url = URI(
        str(url)
        .replace("{sender}", str(formatted_sender))
        .replace("{data}", str(formatted_data))
    )
    # No scheme or destination validation is performed here
    if "{data}" in url and "{sender}" in url:
        response = session.get(formatted_url, timeout=DEFAULT_HTTP_TIMEOUT)
    else:
        response = session.post(
            formatted_url,
            json={"data": formatted_data, "sender": formatted_sender},
            timeout=DEFAULT_HTTP_TIMEOUT,
        )

In the code above, the session.get() and session.post() functions receive the formatted_url directly. There is no check to determine if the parsed URI maps to an internal IP address or if the scheme is explicitly limited to https. Because requests.Session() natively tracks cookies and follows redirects up to 30 times, an attacker gains significant leverage over the HTTP request lifecycle.

The architectural flaw is the conflation of developer intent with untrusted input. The library assumes the smart contract returning the OffchainLookup error is benign and correctly configured. A complete fix requires implementing an interceptor or custom transport adapter that parses the URL, resolves the hostname to an IP address, verifies the IP against a blocklist of private/reserved ranges, and ensures the scheme is secure prior to initiating the TCP handshake.

Exploitation Methodology

Exploitation requires the target system to query a malicious smart contract using web3.py. The attacker authors a contract that intentionally reverts upon receiving a call to a specific function. The revert payload is formatted to strictly match the OffchainLookup ABI, supplying the target internal URL in the urls array. When the target application executes a read operation (e.g., contract.functions.maliciousFunction().call()), the SSRF is triggered.

The following Python script acts as a proof-of-concept demonstrating how a malicious payload forces the library to target a local listener. In a real-world scenario, the payload originates from the blockchain node's response.

from web3.types import TxParams
from web3.utils.exception_handling import handle_offchain_lookup
 
def reproduce_ssrf():
    target_address = "0x0000000000000000000000000000000000000001"
    payload = {
        "sender": target_address,
        "callData": "0x1234",
        "callbackFunction": "0x12345678",
        "extraData": "0x90ab",
        "urls": [
            "http://127.0.0.1:9999/SSRF_DETECTION?sender={sender}&data={data}"
        ],
    }
    transaction: TxParams = {"to": target_address}
    print(f"Triggering CCIP Read handler with URL: {payload['urls'][0]}")
    try:
        handle_offchain_lookup(payload, transaction)
    except Exception as e:
        print(f"Expected failure after request was sent: {e}")
 
if __name__ == "__main__":
    reproduce_ssrf()

Attackers can leverage this vulnerability in several distinct ways. For cloud environments, the attacker targets the Instance Metadata Service (IMDS). A GET request directed at http://169.254.169.254/latest/meta-data/iam/security-credentials/ on AWS can retrieve temporary IAM credentials if IMDSv1 is enabled. For internal network probing, attackers execute a blind SSRF technique. By measuring the response time or analyzing subsequent transaction failures, the attacker can infer the state of internal ports and map the internal network topology.

The ability to issue POST requests significantly elevates the risk. If the target internal service expects JSON payloads and lacks authentication, the attacker can manipulate the service state. Since the POST body is strictly formatted as {"data": formatted_data, "sender": formatted_sender}, the attacker must find an endpoint where this specific JSON structure triggers an exploitable parsing behavior or state change.

Impact Assessment

The CVSS v4.0 vector assigned to this vulnerability is CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N, resulting in a base score of 4.0. The exploit requires no privileges (PR:N) and operates over the network (AV:N) with low complexity (AC:L). The vulnerability does not directly compromise the host application's confidentiality, integrity, or availability (VC:N/VI:N/VA:N), as the SSRF response is consumed by the library and not directly echoed back to the user.

The impact is concentrated on subsequent systems (SC:L/SI:L). The severity of the compromise depends strictly on the sensitivity of the internal services reachable from the host. If the application is deployed in a modern cloud environment with IMDSv2 strictly enforced, the risk of credential theft is mitigated because IMDSv2 requires a specific PUT request to obtain a session token, which this vulnerability cannot generate. However, legacy deployments utilizing IMDSv1 remain highly susceptible to complete IAM role compromise.

Beyond cloud metadata, the SSRF allows interaction with unauthenticated management interfaces, internal monitoring endpoints, and developer tools. Services like Redis, Elasticsearch, or internal administrative APIs exposed on the same VPC or localhost become accessible to the attacker. Even in cases where direct data exfiltration is impossible due to the lack of an immediate response channel (Blind SSRF), the integrity of internal application states can be compromised via crafted POST requests.

The default enablement of CCIP Read across all web3.py installations makes this a pervasive risk. Security teams must assume that any application version within the affected range that interacts with external smart contracts is actively exposing its internal network boundary.

Remediation and Mitigation

The definitive remediation for GHSA-5hr4-253g-cpx2 is upgrading the web3.py library to version 7.15.0 or 8.0.0b2. These versions introduce comprehensive destination validation within the CCIP Read implementation, restricting the types of URLs that the library will resolve and adding security bounds to the underlying HTTP clients.

For environments where immediate patching is not technically feasible, developers must disable the EIP-3668 functionality globally. This is achieved by setting the global_ccip_read_enabled property to False on the Web3 class. Implementing this configuration change completely nullifies the vulnerability by preventing the library from automatically parsing OffchainLookup revert payloads.

from web3 import Web3
 
# Disable CCIP Read globally to prevent SSRF
Web3.global_ccip_read_enabled = False

In addition to application-level fixes, infrastructure-level defenses should be implemented. Execution environments should operate with strict egress network filtering. Applications using web3.py rarely require access to link-local cloud metadata endpoints or internal VPC subnets. Configuring VPC security groups or local firewalls to deny outbound traffic to 169.254.169.254 and RFC1918 ranges prevents exploitation even if the library remains vulnerable. Security teams should monitor outbound network traffic originating from the application servers for anomalous requests targeting internal subnets.

Technical Appendix

CVSS Score
4.0/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N

Affected Systems

web3 >= 6.0.0b3, < 7.15.0web3 == 8.0.0b1

Affected Versions Detail

Product
Affected Versions
Fixed Version
web3
Ethereum
>= 6.0.0b3, < 7.15.07.15.0
web3
Ethereum
== 8.0.0b18.0.0b2
AttributeDetail
Vulnerability TypeServer-Side Request Forgery (SSRF)
CWE IDCWE-918
CVSS v4.0 Score4.0 (Medium)
Attack VectorNetwork
Exploitation StatusProof of Concept Available
Primary ImpactInternal network probing and potential cloud metadata exposure

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1552.005Cloud Instance Metadata API
Credential Access
CWE-918
Server-Side Request Forgery (SSRF)

The application issues network requests on behalf of a user or system but does not sufficiently restrict the destination of those requests.

Known Exploits & Detection

GitHub Security AdvisoryProof of concept demonstrates blind SSRF capabilities against a local port via malicious transaction payloads.

Vulnerability Timeline

Vulnerability published via GitHub Security Advisory
2026-04-04

References & Sources

  • [1]GitHub Security Advisory GHSA-5hr4-253g-cpx2
  • [2]OSV Vulnerability Record
  • [3]Official web3.py Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.