🛡️ CVE-2026-57126 — praisonaiagents

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-918 OSV
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

praisonaiagents: SSRF guard validates literal IPs only and never resolves DNS

# praisonaiagents: SSRF guard validates literal IPs only and never resolves DNS

Researcher: Kai Aizen — SnailSploit (@SnailSploit), Adversarial & Offensive Security Research

Target: https://github.com/MervinPraison/PraisonAI

Weakness: CWE-918 Server-Side Request Forgery (SSRF).

Summary

The SSRF guard shared by PraisonAI's web tools (SpiderTools._validate_url_host_is_blocked in praisonaiagents/tools/spider_tools.py) inspects only literal IP-address encodings of the URL host. It never resolves DNS names. Any hostname whose A/AAAA record points at an internal, loopback, link-local, or cloud-metadata address passes validation and the request is issued to that target. A static internal A record is sufficient — no DNS-rebinding race is required.

The guard's own docstring claims it returns True "when hostname resolves to loopback/private/internal targets," but no resolution is performed. The fix for CVE-2026-47390 added more *encodings of literal IPs* (decimal integer, 0x hex, inet_aton); it did not address the *class* "host is a name that resolves to a forbidden address."

The same guard is reached through two tool surfaces:

  • scrape_page / crawl / extract_links / extract_text (spider tools)
  • the @url mention fetch in praisonaiagents/tools/mentions.py (which calls the identical SpiderTools._validate_url then urllib.request.urlopen)

The correct pattern already exists in the same package: file_tools.py resolves the host with socket.getaddrinfo and checks each resolved address before fetching. spider_tools / mentions do not.

Affected packages

  • pip/praisonaiagents <= 1.6.39
  • pip/PraisonAI <= 4.6.39

Root cause

praisonaiagents/tools/spider_tools.py, _host_is_blocked (def at line 26):

```python

def _host_is_blocked(hostname: str) -> bool:

"""Return True when hostname resolves to loopback/private/internal targets."""

...

if host.isdigit(): # decimal-int IPv4 literal

return _ip_blocked(ipaddress.ip_address(int(host)))

if host.startswith("0x"): # hex IPv4 literal

return _ip_blocked(ipaddress.ip_address(int(host, 16)))

try:

return _ip_blocked(ipaddress.ip_address(host)) # dotted v4 / v6 literal

except ValueError:

pass

try:

return _ip_blocked(ipaddress.ip_address(socket.inet_aton(host))) # octal/short v4

except OSError:

pass

return False # <-- any DNS name lands here

```

Every branch operates on the literal string. For a DNS name (attacker.example): it is not in the literal block sets, not a .local/.internal suffix, int(host) is not applicable, ipaddress.ip_address(name) raises ValueError (swallowed), inet_aton(name) raises OSError (swallowed), and the function returns False — "not blocked." socket.getaddrinfo / gethostbyname are never called anywhere in this path.

_validate_url (def line 74) ends with:

```python

if _host_is_blocked(parsed.hostname):

return False

return True

```

so a name verdict of "not blocked" yields _validate_url(...) == True, and the caller (scrape_page, or mentions._fetch_url at lines 273–284) proceeds to fetch the original URL via requests / urllib.request.urlopen.

The literal-IP coverage is otherwise good — Python's ipaddress.is_reserved / is_private happen to flag NAT64 (64:ff9b::/96), 6to4 (2002::/16), IPv4-mapped (::ffff:), and IPv4-compatible (::/96) forms. The single residual literal gap is deprecated site-local fec0::/10 (is_private and is_reserved both False), which is low-impact on modern stacks. The DNS-name class is the material issue.

The promise that was broken

The block set explicitly contains "169.254.169.254" and "metadata.google.internal" (line 33) — documented intent to stop cloud-metadata theft. A name-based request defeats exactly that intent: register metadata-thief.example with an A record of 169.254.169.254, and the literal block is never consulted because resolution never happens.

Proof of concept

```python

import socket

from praisonaiagents.tools.spider_tools import _host_is_blocked, SpiderTools

# Literal forms the CVE-2026-47390 fix added — correctly blocked:

for h in ["127.0.0.1", "2130706433", "0x7f000001", "169.254.169.254", "::1"]:

assert _host_is_blocked(h) is True, h

# DNS names that resolve to internal targets — NOT blocked (the class the fix missed):

for h in ["attacker-controlled.example", "metadata-thief.com", "rebind.attacker.net"]:

assert _host_is_blocked(h) is False, h # A record may be 127.0.0.1 / 169.254.169.254

st = SpiderTools

assert st._validate_url("http://127.0.0.1/") is False # literal blocked

assert st._validate_url("http://metadata-thief.com/") is True # name pa

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality high, integrity low, availability none.

Weakness class

CVE-2026-57126 is classified as CWE-918: Server-Side Request Forgery (SSRF). The server fetches a URL supplied by the caller, which can be pointed at internal systems it alone can reach.

Affected software

CVE-2026-57126 is recorded against 1 package.

  • praisonaiagents (fixed in 1.6.59)

Timeline and source

Published on 18 June 2026 and last revised on 23 July 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Package)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
CWE CWE-918
Public Exploit ✅ No
Source OSV
Published 2026-06-18
Updated 2026-08-12
Modified 2026-07-23
Fix URL N/A

Affected Packages

Software From version Fixed in
praisonaiagents 1.6.59

Similar Threats

Site Security Check

Is praisonaiagents part of your stack?

CVE-2026-57126 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.