PraisonAI CVE-2026-44335
HIGHSeverity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Network-reachable, low-complexity, unauthenticated URL-parser bypass with no user interaction; impact bounded to integrity (forged internal requests) with confidentiality/availability not clearly established, matching the vendor's integrity-focused scoring.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
Lifecycle Timeline
5DescriptionGitHub Advisory
Summary
The URL checking logic in PraisonAI has a logical flaw that could be bypassed by attackers, leading to SSRF attacks.
Details
The current PraisonAI project uses _validate_url to validate the input URL. The main logic is to perform security checks on the host portion of the URL extracted by urlparse to prevent SSRF attacks.
<img width="1290" height="1145" alt="QQ20260424-151256-24-1" src="https://github.com/user-attachments/assets/d5f16b74-5ad2-444f-8600-b05f78a4b769" />
However, there are indeed differences in parsing between urlparse and the library that actually sends the request. Currently, almost all application scenarios in this project involve first using _validate_url for URL validation, and then using _get_session().get to send the request.
<img width="1143" height="740" alt="QQ20260424-151437-24-2" src="https://github.com/user-attachments/assets/b1bf6ec2-d32a-4dac-b814-da819e8d3c83" />
In reality, its underlying mechanism is requests.get.
<img width="1042" height="576" alt="QQ20260424-151645-24-3" src="https://github.com/user-attachments/assets/e17352c3-4205-44d6-ab6e-75566480215b" />
The core issue: urlparse() and requests disagree on which host a URL like http://127.0.0.1:6666\@1.1.1.1 points to:
urlparse()treats\as a regular character and@as the userinfo-host delimiter, so it extracts hostname as1.1.1.1(public)requeststreats\as a path character, connecting to127.0.0.1(internal)
Below is a test code I wrote following the code.
import sys
from pathlib import Path
from pprint import pprint
sys.path.insert(0, str(Path(r"D:/BaiduNetdiskDownload/PraisonAI-main/PraisonAI-main/src/praisonai-agents")))
from praisonaiagents.tools import spider_tools
# url = "http://127.0.0.1:6666\@1.1.1.1"
url = "http://127.0.0.1:6666"
result = spider_tools.scrape_page(url)
if isinstance(result, dict) and "error" in result:
print("scrape failed:", result["error"])
else:
pprint(result)When an attacker uses http://127.0.0.1:6666/, the existing detection logic can detect that this is an internal network address and block it.
<img width="1068" height="128" alt="QQ20260424-152007-24-4" src="https://github.com/user-attachments/assets/294bff10-2af6-4960-bf69-dbf3340b1e9b" />
However, when an attacker uses http://127.0.0.1:6666\@1.1.1.1, the detection logic resolves the host to 1.1.1.1, which is a public IP address, thus passing the verification. But in the actual request process, this URL is forwarded by requests.get to http://127.0.0.1:6666, bypassing the detection and achieving an SSRF attack.
<img width="2089" height="324" alt="QQ20260424-152123-24-5" src="https://github.com/user-attachments/assets/4421ce42-e47b-48de-a97a-56ce56a2bbc9" />
PoC
http://127.0.0.1:6666\@1.1.1.1Impact
SSRF
AnalysisAI
Server-side request forgery in PraisonAI (praisonaiagents Python package, versions <= 1.6.31) lets remote attackers bypass the framework's SSRF protections by exploiting a URL-parsing differential between Python's urlparse and the requests library. The _validate_url anti-SSRF check resolves a payload like http://127.0.0.1:6666\@1.1.1.1 to the public host 1.1.1.1 and allows it, but requests.get actually connects to the internal 127.0.0.1, so the agent's scraping tools can be coerced into reaching internal services. Publicly available exploit code exists (a working PoC is published in the GHSA advisory), though EPSS is very low (0.04%, 13th percentile) and there is no evidence of active exploitation.
Technical ContextAI
The flaw is a classic CWE-918 SSRF caused by a parser-confusion vulnerability. PraisonAI attempts to block requests to internal addresses by extracting the host with urlparse().hostname and validating it, then issuing the outbound request through _get_session().get, which is backed by the requests library. urlparse and requests apply different URL grammar to the same string: urlparse treats the backslash as an ordinary character and the '@' as the userinfo/host delimiter, extracting 1.1.1.1 as the host, while requests (following WHATWG/curl-style normalization) treats the backslash as a path separator and connects to the authority before it, 127.0.0.1:6666. Because validation and request-sending use two libraries that disagree on the effective host, any allow-list/deny-list check on the parsed host can be desynchronized from the real connection target. The affected component is the praisonaiagents pip package (pkg:pip/praisonaiagents), specifically its spider_tools/scrape_page and other tools that fetch attacker-influenced URLs.
RemediationAI
Upgrade the praisonaiagents package to the patched release - Vendor-released patch: 1.6.32 (pip install --upgrade 'praisonaiagents>=1.6.32'), per GHSA-q9pw-vmhh-384g. If you cannot upgrade immediately, reduce exposure by not passing user-controlled URLs into the scraping/fetch tools (e.g. spider_tools.scrape_page), or place the tool behind validation that resolves and pins the host with the same library that sends the request (requests) rather than urlparse, rejecting URLs whose resolved connection target is loopback, link-local (169.254.0.0/16 including 169.254.169.254 metadata), or RFC1918 space; note this changes behavior for any legitimate internal-URL use cases. Additionally, restrict outbound egress from the host running PraisonAI via a network egress allow-list or an outbound proxy so that even a successful bypass cannot reach internal services or cloud metadata endpoints - the trade-off is that legitimate outbound fetches must be explicitly permitted. Reject URLs containing backslashes or embedded userinfo ('@') as a targeted stopgap for this specific payload class. Advisory: https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-q9pw-vmhh-384g.
Same weakness CWE-918 – Server-Side Request Forgery (SSRF)
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-q9pw-vmhh-384g