VYPR
Moderate severityNVD Advisory· Published Feb 19, 2026· Updated Feb 19, 2026

Indico has Server-Side Request Forgery (SSRF) in multiple places

CVE-2026-25738

Description

Indico is an event management system that uses Flask-Multipass, a multi-backend authentication system for Flask. Versions prior to 3.3.10 are vulnerable to server-side request forgery. Indico makes outgoing requests to user-provides URLs in various places. This is mostly intentional and part of Indico's functionality but is never intended to let users access "special" targets such as localhost or cloud metadata endpoints. Users should upgrade to version 3.3.10 to receive a patch. Those who do not have IPs that expose sensitive data without authentication (typically because they do not host Indico on AWS) are not affected. Only event organizers can access endpoints where SSRF could be used to actually see the data returned by such a request. For those who trust their event organizers, the risk is also very limited. For additional security, both before and after patching, one may also use the common proxy-related environment variables (in particular http_proxy and https_proxy) to force outgoing requests to go through a proxy that limits requests in whatever way you deem useful/necessary. These environment variables would need to be set both on the indico-uwsgi and indico-celery services.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
indicoPyPI
< 3.3.103.3.10

Affected products

1

Patches

1
70d341826116

Protect against SSRF via DNS rebinding (#7283)

https://github.com/indico/indicoAdrianJan 28, 2026via ghsa
2 files changed · +35 1
  • indico/__init__.py+2 0 modified
    @@ -6,9 +6,11 @@
     # LICENSE file for more details.
     
     from indico.util.mimetypes import register_custom_mimetypes
    +from indico.util.network import patch_socket_getaddrinfo
     
     
     __version__ = '3.3.10-dev'
     PREFERRED_PYTHON_VERSION_SPEC = '~=3.12.2'
     
     register_custom_mimetypes()
    +patch_socket_getaddrinfo()
    
  • indico/util/network.py+33 1 modified
    @@ -9,6 +9,7 @@
     import socket
     from urllib.parse import urlsplit
     
    +from flask import g, has_app_context
     from requests import Response
     from requests.exceptions import RequestException
     
    @@ -29,7 +30,7 @@ def is_private_url(url):
             return True
     
         try:
    -        host_data = socket.getaddrinfo(hostname, None, 0, 0, socket.IPPROTO_TCP)
    +        host_data = socket.getaddrinfo(hostname, None)
             return any(_is_private_ip(str(item[4][0])) for item in host_data)
         except (socket.gaierror, ipaddress.AddressValueError, ValueError):
             return True
    @@ -48,6 +49,8 @@ def validate_request_url(url: str):
         hook using ``hooks={'response': validate_redirect_target_hook}`` or
         ``**make_validate_request_url_hook()`` when making the request.
         """
    +    # Enable getaddrinfo interception to spot DNS rebinding attacks
    +    g.setdefault('gai_cache', {})
         # This could also allow for configurable whitelisting of specific hosts, but until
         # someone needs this let's keep it simple and just blacklist private ranges.
         if is_private_url(url):
    @@ -74,3 +77,32 @@ def make_validate_request_url_hook():
         Use it like this: ``requests.get(..., **make_validate_request_url_hook())``
         """
         return {'hooks': {'response': validate_redirect_target_hook}}
    +
    +
    +def patch_socket_getaddrinfo():
    +    """Monkey-patch socket.getaddrinfo to check for DNS-rebinding attacks.
    +
    +    This avoids SSRF via dns rebinding, where the first lookup in `validate_request_url`
    +    returns a safe IP, but the second lookup (when making the actual connection) returns
    +    a restricted IP. Once `validate_request_url` has been used, any subsequent lookups
    +    for the same host are checked against the first one, and if a private IP shows up
    +    in the result set, we abort.
    +    """
    +    orig_gai = socket.getaddrinfo
    +
    +    def safe_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
    +        rv = orig_gai(host, port, family, type, proto, flags)
    +        if not has_app_context() or 'gai_cache' not in g:
    +            return rv
    +        cached_addrs = g.gai_cache.get(host)
    +        addrs = {x[4][0] for x in rv}
    +        if (
    +            cached_addrs is not None
    +            and (new_addrs := (addrs - cached_addrs))
    +            and (offender := next((x for x in new_addrs if _is_private_ip(x)), None))
    +        ):
    +            raise InsecureRequestError(f'Possible DNS rebinding attack for {host} -> {offender}')
    +        g.gai_cache[host] = addrs
    +        return rv
    +
    +    socket.getaddrinfo = safe_getaddrinfo
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.