CVE-2026-44335Critical· 9.8▾ MidnightPraisonAI has an SSRF bypass
▾ Midnight zone — Critical, or high with PoC / in-the-wild
impact 53.9 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Exploit-prediction probability, daily snapshots since Jul 13.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via OSV
Last analysed / modified upstream
0.4%
The URL checking logic in PraisonAI has a logical flaw that could be bypassed by attackers, leading to SSRF attacks.
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 as 1.1.1.1 (public)requests treats \ as a path character, connecting to 127.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.
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.
http://127.0.0.1:6666\@1.1.1.1
SSRF
praisonaiagents < 1.6.32Upgrade to a patched release:
praisonaiagents 1.6.32Connected by shared product, vendor, weakness, or advisory.
GHSA-x462-jjpc-q4q4High· 8.1PraisonAI: Cross-Origin Agent Execution via Hardcoded Wildcard CORS and Missing Authentication on AGUI Endpoint
CVE-2026-55528High· 8.2praisonaiagents: AgentServer declares auth_token but never enforces it on any route
CVE-2026-55526High· 8.5praisonaiagents has an SSRF protection bypass in `spider_tools._host_is_blocked()` via DNS-resolved hostnames (`127.0.0.1.nip.io`)
CVE-2026-55524High· 7.5PraisonAI is a multi-agent teams system
CVE-2026-55530Medium· 6.1praisonaiagents: ast_grep_rewrite rewrites arbitrary files without the @require_approval gate enforced on every sibling mutation tool
CVE-2026-55527High· 7.1praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable …