Nextcloud SSRF: Bypass del Filtro SSRF vía FTP Backend (Riesgo Asumido por el Vendor)

📅 Fecha del reporte: 25 de marzo de 2026  |  Plataforma: Nextcloud  |  Severidad: Media

⚠️ Recomendación: Vigilar esta Función

Aunque Nextcloud considera este comportamiento un riesgo aceptado al activar el almacenamiento externo FTP, no significa que debas ignorarlo. Si tu instancia tiene habilitado el soporte de almacenamiento externo, debes ser consciente de lo que esto implica y aplicar controles compensatorios. Al final del post encontrarás comandos concretos para auditar y mitigar la exposición.

Resumen Técnico

Nextcloud implementa una barrera de seguridad a través de la configuración allow_local_remote_servers (desactivada por defecto) para prevenir ataques de tipo SSRF (Server-Side Request Forgery) contra infraestructura interna. Esta investigación demostró que:

✅ El filtro sí se aplica correctamente para backends WebDAV y HTTP a través de OC\Http\Client\Client.
❌ El filtro no se aplica para el backend FTP, que llama a ftp_connect() directamente sin validación previa.

Configurando un almacenamiento FTP externo apuntando a una IP interna (p. ej. 172.17.0.x), un atacante puede establecer una conexión completa de lectura/escritura a servicios internos, habilitando exfiltración de datos y pivoting de red.

Diagrama comparativo: WebDAV bloqueado por LocalAddressChecker vs FTP sin validación en Nextcloud
Figura 1: Comparación de implementación. El cliente HTTP valida la IP antes de conectar; el backend FTP llama directamente a ftp_connect() sin ningún chequeo.

Entorno de Laboratorio

OS: Ubuntu 25.10 (Kernel 6.x)  |  Nextcloud: 34.0.0 dev (build 34.0.0.1, rama master)  |  Instalación: fuente manual desde GitHub

# Step 1 — Lab environment setup
# Nextcloud container (source: GitHub master 34.0.0.1 dev)
docker run -d --name nextcloud-dev \
  -p 8090:80 nextcloud-custom:34.0.0

# Target A: Internal WebDAV service (172.17.0.7)
docker run -d --name internal-webdav \
  --ip 172.17.0.7 rclone/rclone serve webdav /data \
  --addr 0.0.0.0:8080 --user username --pass password

# Target B: Internal FTP service (172.17.0.6)
docker run -d --name internal-ftp \
  --ip 172.17.0.6 python3-ftp-server:latest
# FTP credentials: ftpuser / ftppass

Pasos para Reproducir

Paso 1 — Habilitar soporte de almacenamiento externo

Inicia sesión como administrador. Ve a Aplicaciones y activa «External storage support». Luego dirígete a Administración > Almacenamiento externo.

Paso 2 — Prueba de control: WebDAV bloqueado ✅

Esto demuestra que la política de seguridad está activa para protocolos estándar. Añade un almacenamiento WebDAV con una IP interna:

# External Storage > WebDAV — BLOCKED ✅
Folder Name : Internal-WebDAV
URL         : http://172.17.0.7:8080/
Auth        : Username and password (username / password)

# Docker logs output:
[nextcloud] ERROR OC\Http\Client\Client: Host violates local access rules.
# Result: Files listed but NOT readable / downloadable ✅

Paso 3 — Bypass: FTP sin restricciones ❌

Este paso demuestra la vulnerabilidad. Añade un almacenamiento FTP con la misma IP interna:

# External Storage > FTP — BYPASSED ❌
Folder Name : Internal-FTP-Bypass
Host        : 172.17.0.6
Port        : 21
Secure FTP  : None (no SSL/TLS)
Auth        : Username and password (ftpuser / ftppass)

# Result: Full read/write access to internal FTP server
# No error in logs — connection succeeds silently ❌

El servidor FTP interno usado en el laboratorio se puede replicar fácilmente con Python:

# Minimal Python FTP server used in lab (Target B)
# pip install pyftpdlib
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer

authorizer = DummyAuthorizer()
# Grant full read/write to ftpuser on /data directory
authorizer.add_user('ftpuser', 'ftppass', '/data', perm='elradfmwMT')

handler = FTPHandler
handler.authorizer = authorizer
handler.passive_ports = range(60000, 60100)

server = FTPServer(('0.0.0.0', 21), handler)
server.serve_forever()
Python

Análisis de Código Fuente

Implementación Segura (HTTP/WebDAV) ✅

En lib/private/Http/Client/Client.php, Nextcloud verifica explícitamente los rangos de IP reservados antes de iniciar cualquier petición HTTP:

// lib/private/Http/Client/Client.php
// ✅ LocalAddressChecker IS enforced for HTTP/WebDAV
private function preventLocalAddress(string $uri): void {
    $host = parse_url($uri, PHP_URL_HOST);
    if ($this->localAddressChecker->isLocalAddress($host)) {
        throw new Exception('Host violates local access rules.');
    }
}
PHP

Implementación Vulnerable (FTP) ❌

En apps/files_external/lib/Lib/Storage/FTP.php, la conexión se inicia vía ftp_connect() sin ninguna validación previa contra LocalAddressChecker ni contra la config allow_local_remote_servers:

// apps/files_external/lib/Lib/Storage/FTP.php
// ❌ No LocalAddressChecker validation before connection
public function __construct(array $parameters) {
    $this->host     = $parameters['host'];
    $this->user     = $parameters['user'];
    $this->password = $parameters['password'];
    $this->port     = isset($parameters['port']) ? $parameters['port'] : 21;
    $this->secure   = isset($parameters['secure']) ? $parameters['secure'] : false;
    $this->root     = isset($parameters['root']) ? $parameters['root'] : '/';
    // ftp_connect() is called directly — NO IP range check here
}
PHP

Respuesta del Vendor

Nextcloud respondió que activar el soporte de almacenamiento externo FTP es una decisión administrativa que implica aceptar ciertos riesgos. El equipo considera que si un administrador habilita FTP externo, es responsabilidad suya controlar qué hosts son accesibles. El reporte fue cerrado como «Accepted Risk / Won’t Fix».

Esto es una postura legítima del vendor, pero conviene entenderla bien: la protección SSRF de Nextcloud NO cubre el backend FTP. Si confías en que allow_local_remote_servers = false te protege completamente, estás en un error cuando FTP está en juego.

Impacto

Explotando la falta de validación en el backend FTP, un atacante con acceso de administrador (o usuario con permisos de montar almacenamiento) puede:

Escenario de ataque completo: FTP SSRF bypass en Nextcloud alcanzando red interna con servicios sensibles
Figura 2: Escenario de ataque completo. Un usuario con permisos de configurar almacenamiento externo puede alcanzar cualquier IP de la red interna vía FTP, incluyendo backups, NAS y servicios de base de datos.

🔴 Acceder a servicios internos: Backups, NAS, dispositivos IoT, bases de datos no expuestas a internet.
🔴 Exfiltrar datos: Leer archivos de servidores FTP internos como si fueran carpetas normales de Nextcloud.
🔴 Pivoting de red: Usar Nextcloud como punto de apoyo para explorar la red interna.
🔴 Eludir el hardening: Violar directamente la política allow_local_remote_servers = false que se supone previene exactamente esto.

Mitigaciones y Comandos de Auditoría

Dado que Nextcloud no corregirá esto, la responsabilidad recae en el administrador. Aplica las siguientes medidas:

# ⚠️  ADVISORY: Si tienes habilitado el almacenamiento externo FTP en Nextcloud
# y NO necesitas acceso a IPs de red interna, aplica estas mitigaciones:

# 1. Deshabilitar FTP externo si no se usa
php occ config:app:set files_external allow_user_mounting --value=no

# 2. Revisar montajes FTP existentes
php occ files_external:list --all | grep -i ftp

# 3. Activar firewall a nivel de red para bloquear
#    conexiones salientes de Nextcloud a rangos RFC1918:
#    10.0.0.0/8 · 172.16.0.0/12 · 192.168.0.0/16

# 4. Monitorizar logs de almacenamiento externo regularmente
tail -f /var/www/nextcloud/data/nextcloud.log | grep -i "files_external"

Conclusión

Este hallazgo es un recordatorio claro de que las capas de seguridad deben aplicarse de forma consistente en todos los backends. La protección SSRF de Nextcloud es sólida para HTTP/WebDAV, pero existe un punto ciego en FTP que el vendor reconoce como riesgo asumido.

🔐 Consejo final: Si no usas FTP como almacenamiento externo, desactívalo. Si lo usas, asegúrate de que tus reglas de firewall impidan que Nextcloud alcance rangos de IP privadas. No dependas únicamente de los controles internos de la aplicación.


0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments