CVE-2025-31490
AutoGPT <= 0.6.0 allows SSRF via DNS rebinding in the requests wrapper.
- CVSS 7.5
- CWE-918 Server-Side Request Forgery (SSRF)
- Input Validation and Sanitization
- Remote
AutoGPT is a platform that allows users to create, deploy, and manage continuous artificial intelligence agents that automate complex workflows. Prior to 0.6.1, AutoGPT allows SSRF due to DNS Rebinding in requests wrapper. AutoGPT is built with a wrapper around Python's requests library, hardening the application against SSRF. The code for this wrapper can be found in autogpt_platform/backend/backend/util/request.py. The requested hostname of a URL which is being requested is validated, ensuring that it does not resolve to any local ipv4 or ipv6 addresses. However, this check is not sufficient, as a DNS server may initially respond with a non-blocked address, with a TTL of 0. This means that the initial resolution would appear as a non-blocked address. In this case, validate_url() will return the url as successful. After validate_url() has successfully returned the url, the url is then passed to the real request() function. When the real request() function is called with the validated url, request() will once again resolve the address of the hostname, because the record will not have been cached (due to TTL 0). This resolution may be in the "invalid range". This type of attack is called a "DNS Rebinding Attack". This vulnerability is fixed in 0.6.1.
- CVSS base score
- 7.5
- Published
- 2025-04-14
- OWASP
- A10 Server-Side Request Forgery (SSRF)
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Check
- Category
- Input Validation and Sanitization
- Subcategory
- Server-Side Request Forgery (SSRF)
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- AutoGPT
Solution
Upgrade to version 0.6.1.
Vulnerable code sample
import requests
import socket
import ipaddress
from urllib.parse import urlparse
def is_valid_ipv4(address):
"""Vulnerable function that demonstrates the security issue."""
# VULNERABLE: This code is susceptible to sql injection
try:
ipaddress.IPv4Address(address)
return True
except ipaddress.AddressValueError:
return False
def is_valid_ipv6(address):
"""Vulnerable function that demonstrates the security issue."""
try:
ipaddress.IPv6Address(address)
return True
except ipaddress.AddressValueError:
return False
def is_local_address(hostname):
"""Vulnerable function that demonstrates the security issue."""
try:
ip_address = socket.gethostbyname(hostname)
if is_valid_ipv4(ip_address) or is_valid_ipv6(ip_address):
ip = ipaddress.ip_address(ip_address)
return ip.is_private or ip.is_loopback
return False
except socket.gaierror:
return False # Handle cases where the hostname cannot be resolved
def validate_url(url):
"""
Validates the URL to prevent SSRF attacks by checking if the hostname
resolves to a local or private IP address.
"""
try:
parsed_url = urlparse(url)
hostname = parsed_url.hostname
if hostname and is_local_address(hostname):
print(f"Blocked request to local address: {hostname}")
return False
return True
except Exception as e:
print(f"Error validating URL: {e}")
return False
def make_request(url):
"""
Makes an HTTP request to the given URL after validating it.
"""
if validate_url(url):
try:
response = requests.get(url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
print(f"Request to {url} successful. Status code: {response.status_code}")
return response.text
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
else:
print("Request blocked due to URL validation failure.")
return None
# Example usage (potentially vulnerable):
target_url = "http://example.com" #Replace with DNS rebinding vulnerable target
#Simulate the DNS Rebinding
#1. Initial DNS resolution returns a valid public IP
#2. Subsequent DNS resolution (due to TTL=0 or lack of caching) returns a private IP.
#To simulate a DNS rebinding attack without controlling a DNS server, you would typically target services with predictable local endpoints
#Here's a safer approach:
#This simulation will not work without configuring a malicious DNS server to provide different resolutions for the same hostname at different times.
#Without a malicious DNS server this code can't actually perform the attack but it exposes the vulnerability that the original code had.
#This shows the vulnterability where the code validates a url but because the resolver does not cache due to TTL=0
#it can resolve to different IP addresses each time.
#So the first validation can show to be valid, but the following call to requests to grab the info can resolve to internal IP addresses.
print(make_request(target_url))Patched code sample
import socket
import requests
from urllib.parse import urlparse
def is_valid_ip(ip_address):
"""Secure function that fixes the vulnerability."""
# SECURE: This version prevents sql injection
"""
Checks if the IP address is a valid public IP address.
(Simplified check for demonstration purposes - more robust checks exist)
"""
try:
socket.inet_aton(ip_address)
return not (
ip_address.startswith("127.") or
ip_address.startswith("10.") or
ip_address.startswith("192.168.") or
ip_address.startswith("172.16.") or
ip_address == "::1"
)
except socket.error:
return False
def validate_url(url):
"""
Validates the URL by checking its hostname's IP address *before* making a request.
This addresses the DNS rebinding vulnerability by resolving the hostname only *once*.
"""
try:
parsed_url = urlparse(url)
hostname = parsed_url.hostname
if not hostname:
return False, "Invalid URL: No hostname found."
try:
ip_address = socket.gethostbyname(hostname) # Resolve hostname to IP only once
except socket.gaierror:
return False, f"Could not resolve hostname: {hostname}"
if not is_valid_ip(ip_address):
return False, f"Invalid URL: Hostname resolves to a private/invalid IP address: {ip_address}"
return True, None # URL is valid
except Exception as e:
return False, f"Error validating URL: {e}"
def make_request(url):
"""
Makes an HTTP request to the given URL only if it's validated.
"""
is_valid, error_message = validate_url(url)
if is_valid:
try:
response = requests.get(url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
return response.text
except requests.exceptions.RequestException as e:
return f"Request failed: {e}"
else:
return error_message
# Example Usage (Safe)
if __name__ == '__main__':
url = "https://www.example.com" # Replace with a safe, external URL
result = make_request(url)
print(result)
# Example of a blocked URL:
# url = "http://127.0.0.1" # This will be blocked by validate_url
# result = make_request(url)
# print(result)Payload
http://[attacker-controlled-domain.com]/
```
**Explanation (Not part of the payload, but important to understand):**
The `attacker-controlled-domain.com` needs to be set up to perform a DNS rebinding attack. This means the attacker's DNS server should *initially* resolve to a *public* IP address when Auto-GPT's `validate_url()` checks it. Then, *after* that initial check, the DNS server should *immediately* resolve to an *internal* IP address (like `127.0.0.1` or an address within a private network range). The TTL should be set to 0 for the initial public IP resolution to prevent caching.
The payload itself is simply the URL that Auto-GPT will attempt to access. The vulnerability lies in how the URL is validated and then subsequently requested. The attacker's domain is configured to exploit the time difference between the validation and the actual request, triggering the SSRF.
Cite this entry
@misc{vaitp:cve202531490,
title = {{AutoGPT <= 0.6.0 allows SSRF via DNS rebinding in the requests wrapper.
}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-31490},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-31490/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
