Antes de atacar un sitio, un atacante necesita saber a quién atacar. El primer paso casi siempre es conseguir nombres de usuario válidos — porque con un usuario real, el trabajo de fuerza bruta se reduce a la mitad.

WordPress tiene una REST API moderna que introdujo en la versión 4.7. Es útil para desarrolladores. Pero por defecto expone el listado completo de usuarios del sitio a cualquiera que haga un GET — sin autenticación, sin dejar rastro en los logs de intentos de login.

01 / La REST API de WordPress y el endpoint de usuarios

La REST API de WordPress está disponible en /wp-json/wp/v2/ y expone distintos recursos del sitio como posts, páginas, categorías — y usuarios.

El endpoint /wp-json/wp/v2/users devuelve por defecto el listado de autores del sitio. La intención original era permitir que aplicaciones externas mostraran información de los autores. El problema es que incluye el nombre de usuario real — el mismo que se usa para iniciar sesión.

// Contexto

A diferencia de xmlrpc.php que requiere un POST con payload XML, este endpoint funciona con un simple GET desde cualquier navegador. No requiere herramientas especializadas ni conocimiento técnico previo.

02 / Cómo funciona — request y respuesta

La verificación es inmediata. Un GET al endpoint devuelve la lista de usuarios en formato JSON:

// request — cualquier navegador o curl
$ curl -s https://target.com/wp-json/wp/v2/users

# O simplemente abrí esta URL en el navegador:
# https://target.com/wp-json/wp/v2/users

Si el endpoint está activo, la respuesta se ve así:

// respuesta — usuarios expuestos HTTP 200 OK
[
  {
    "id": 1,
    "name": "Jeremías",
    "slug": "adminvilla",  ← nombre de usuario real
    "link": "https://target.com/author/adminvilla/",
    "avatar_urls": { ... }
  },
  {
    "id": 2,
    "name": "Editor",
    "slug": "editor_principal",
    "link": "https://target.com/author/editor_principal/"
  }
]

El campo slug es el nombre de usuario exacto que se usa para login. Con esto, un atacante tiene la mitad del trabajo hecho — solo le falta la contraseña.

03 / Por qué es un problema real

La enumeración de usuarios por sí sola no es un ataque — es el primer paso que habilita otros ataques mucho más peligrosos:

Fuerza bruta dirigida

Sin conocer el usuario, un ataque de fuerza bruta tiene que probar combinaciones de usuario + contraseña. Con el usuario confirmado, el atacante solo necesita encontrar la contraseña. El espacio de búsqueda se reduce drásticamente. Combinado con xmlrpc.php activo — como vimos en el artículo anterior — el ataque se vuelve altamente eficiente.

Credential stuffing

Si el mismo usuario usa el mismo nombre en otros servicios (Gmail, LinkedIn, otros WordPress), una base de datos de contraseñas filtradas puede dar acceso inmediato. El usuario expuesto por /wp-json es el punto de entrada.

// Impacto combinado

Si el sitio tiene xmlrpc.php activo + /wp-json expuesto + contraseña débil o reutilizada, el acceso completo al panel de administración es cuestión de tiempo y recursos computacionales. Los tres problemas juntos los encontramos en múltiples sitios de la región.

04 / Lo que encontramos en la región

Durante los análisis de infraestructura que realizamos en organizaciones del NOA, el endpoint /wp-json/wp/v2/users estaba activo y devolviendo usuarios reales en múltiples sitios. En algunos casos el usuario expuesto era directamente admin o variantes obvias como adminvilla — patrones que son lo primero que prueba cualquier herramienta automatizada.

// hallazgo real — output simplificado
$ curl -s https://target-regional.com.ar/wp-json/wp/v2/users \
  | python3 -m json.tool | grep slug

[!] "slug": "adminvilla"     ← usuario ID 1 expuesto
[!] "slug": "redaccion"      ← usuario ID 2 expuesto
[!] "slug": "admin_web"      ← usuario ID 3 expuesto

[✗] 3 usuarios válidos identificados sin autenticación

05 / Cómo deshabilitarlo

Opción A — functions.php (recomendado)

Agregá este código al archivo functions.php de tu tema activo o, mejor aún, creá un plugin simple para no perder el cambio al actualizar el tema:

// functions.php o plugin
// Deshabilitar enumeración de usuarios via REST API
add_filter( 'rest_endpoints', function( $endpoints ) {
  if ( isset( $endpoints['/wp/v2/users'] ) ) {
    unset( $endpoints['/wp/v2/users'] );
  }
  if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
    unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
  }
  return $endpoints;
} );

Opción B — Plugin

Si no tenés acceso al código, el plugin Disable REST API o WP Hide & Security Enhancer permiten bloquear el endpoint sin tocar código.

Verificación post-cierre

// respuesta esperada después del fix HTTP 403
{
  "code": "rest_no_route",
  "message": "No route was found matching the URL and request method.",
  "data": { "status": 404 }
}
// Resultado esperado

Después de aplicar el fix, el endpoint debe devolver 404 o un error de ruta no encontrada. Si sigue devolviendo una lista de usuarios, el código no se aplicó correctamente o hay otro plugin que está re-registrando los endpoints.

06 / Conclusión

La REST API de WordPress es una funcionalidad legítima y útil. El problema no es la API en sí — es que el endpoint de usuarios no debería ser público por defecto en la mayoría de los sitios.

Si tu sitio WordPress no es una plataforma que necesita exponer usuarios públicamente (un directorio de autores, por ejemplo), este endpoint debería estar deshabilitado. El fix tarda menos de 5 minutos y elimina uno de los vectores más usados en ataques automatizados contra WordPress.

¿Querés saber qué más tiene expuesto tu sitio WordPress?

Revisamos xmlrpc.php, REST API, wp-login y más — sin costo, sin compromiso.

Solicitar evaluación gratuita →