CVE-2026-55524: PraisonAI: SSRF in web_crawl tool via redirect-following and DNS rebinding (validate-then-fetch gap)

Published Aug 5, 2026
·
Updated

PraisonAI is a multi-agent teams system. In versions prior to 1.6.58, the webcrawl tool performs its SSRF check only on the initially supplied URL, allowing the protection to be bypassed so the tool connects to attacker-chosen internal destinations. The check resolves the hostname once with socket.gethostbyname and rejects private/loopback/link-local results, but then passes the URL to a fetcher using httpx.Client(followredirects=True) (or urllib.request.urlopen when httpx is absent, which also follows redirects) that re-resolves the hostname at connect time with no further validation. This validate-here/fetch-there gap is exploitable through both HTTP redirects and DNS rebinding. If an attacker can influence URLs passed to webcrawl(), directly or through an agent/tool workflow, they can cause the PraisonAI host to fetch loopback, private-network, or cloud metadata endpoints reachable from that host, with the response body returned in the webcrawl() result. This issue has been fixed in version 1.6.58.

Other sources

The webcrawl tool performs its SSRF check only on the initial URL: it resolves the hostname once with socket.gethostbyname and rejects private/loopback/link-local results. It then passes the URL to a fetcher that uses httpx.Client(followredirects=True) - or urllib.request.urlopen when httpx is absent, which also follows redirects - and re-resolves the hostname at connect time, with no further validation. This validate-here/fetch-there gap is bypassable two independent ways: HTTP redirects and DNS rebinding.

Affected code: src/praisonai-agents/praisonaiagents/tools/webcrawltools.py - Single-shot validation (lines 229-238): if os.environ.get("ALLOWLOCALCRAWL") != "true": ipstr = socket.gethostbyname(hostname) # resolved ONCE, at validation time ip = ipaddress.ipaddress(ipstr) if ip.isloopback or ip.isprivate or ip.islinklocal or ip.ismulticast or ip.isunspecified: continue # rejected urllist.append(u) - Vulnerable fetch (crawlwithhttpx, lines 142 / 149): follows redirects, re-resolves DNS, no re-check: with httpx.Client(followredirects=True, timeout=30.0) as client: response = client.get(url) # fallback: urllib.request.urlopen(url, timeout=30) (also follows redirects by default) - webcrawl / crawlweb are registered tools (tools/init.py:156-157); httpx is the default fallback provider (dispatch at webcrawltools.py:269).

The two bypasses: 1) Redirect: validation approves an attacker domain resolving to a public IP; attacker server replies 302 Location: http://169.254.169.254/... (or any internal host); the fetcher follows it unchecked. 2) DNS rebinding (TOCTOU): validator's gethostbyname and fetcher's connect-time resolution are independent; a low-TTL attacker domain answers public to the validator and private/loopback to fetch.

Impact: An agent with webcrawl - driven by direct input or indirect prompt injection - can be made to read internal-only HTTP services and cloud instance-metadata endpoints (e.g. IAM credentials), with the response body returned in the tool output. Scope is Changed because the request pivots into the internal network.

Proof of concept: A PoC drives the real webcrawl() (httpx absent -> genuine urllib fallback). It runs a loopback "internal metadata" service and a loopback attacker redirector, substituting DNS only to stand in for "attacker owns a public domain" / offline routing - the redirect-following and connect-time re-resolution are the repo's own behavior. Observed: CONTROL: webcrawl("http://127.0.0.1:.../meta-data/") -> blocked (validator works) PoC 1A (redirect): attacker.example approved (public); 302 -> loopback metadata -> result.content leaks {"AccessKeyId":"ASIAFAKESTOLENCREDENTIAL..."} PoC 1B (rebinding): gethostbyname(rebind.example)->public (allowed); connect->127.0.0.1 -> same secret leaked The control proves the validator blocks a direct loopback request, so the bypasses are genuine.

Remediation: Resolve the hostname once, validate that IP, and connect to that exact validated IP (pin it) rather than re-resolving. Disable redirect following (followredirects=False; for urllib use a redirect handler that re-validates), or re-validate every redirect hop's resolved IP. Apply the deny check to both the validator and the actual socket target. filetools.py:364 already uses followredirects=False and is the correct pattern to propagate.

Distinct from prior advisories: The accepted SSRF advisories concern host-string parsing in different code — alternate loopback encodings in spidertools (GHSA-5c6w-wwfq-7qqm) and the CLI @url feature (GHSA-5cxw-77wg-jrf3). This is in the webcrawl tool, which neither advisory names, and the mechanisms (redirect-following and DNS rebinding) differ categorically from host-string encoding; the spidertools hostisblocked hardening does not apply to this tool.

GitHub

Affected Software

2 affected componentsFixes available
PraisonAI PraisonAI<1.6.58
pip/praisonaiagents<1.6.58
1.6.58

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/praisonaiagents to a version that resolves this vulnerability.

    Fixed in 1.6.58
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 1.6.58
  3. Configuration

    Disable redirect following in web_crawl's fetcher (use follow_redirects=False); do not use httpx.Client(follow_redirects=True) or urllib.request.urlopen() when it follows redirects.

    PraisonAI web_crawl tool (src/praisonai-agents/praisonaiagents/tools/web_crawl_tools.py) follow_redirects = false
  4. Configuration

    Resolve the hostname once via socket.gethostbyname at validation time, validate the resulting IP, and then connect only to that exact validated IP (pin the validated IP/address for the fetch), rather than re-resolving at connect time.

    PraisonAI web_crawl tool (src/praisonai-agents/praisonaiagents/tools/web_crawl_tools.py) SSRF validation target (hostname vs pinned IP) = pin to validated IP
  5. Configuration

    Ensure the SSRF-blocking logic is enforced: block if ip.is_loopback or ip.is_private or ip.is_link_local or ip.is_multicast or ip.is_unspecified unless os.environ.get("ALLOW_LOCAL_CRAWL") == "true".

    PraisonAI web_crawl tool (src/praisonai-agents/praisonaiagents/tools/web_crawl_tools.py) ALLOW_LOCAL_CRAWL environment gate = not 'true'

Event History

Aug 5, 2026
CVE Published
via MITRE·07:58 PM
Data Sourced
via MITRE·07:58 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·08:17 PM
DescriptionSeverityWeakness
Aug 25, 2026
Advisory Published
via GitHub·02:43 PM
Data Sourced
via GitHub·02:43 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-55524?

The severity of CVE-2026-55524 is rated high with a score of 7.5.

2

How do I fix CVE-2026-55524?

To fix CVE-2026-55524, upgrade to PraisonAI version 1.6.58 or later.

3

What is the type of vulnerability described in CVE-2026-55524?

CVE-2026-55524 is a server-side request forgery (SSRF) vulnerability.

4

What can attackers achieve with CVE-2026-55524?

Attackers can exploit CVE-2026-55524 to bypass SSRF protections and connect to internal destinations.

5

Which tool is affected by CVE-2026-55524?

The web_crawl tool in PraisonAI is affected by CVE-2026-55524.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203