Resumen
bbPress, el plugin de foros de WordPress, se integra con BuddyPress para que cada tema o respuesta nueva genere una entrada en el flujo de actividad de la comunidad. Esa entrada lleva un indicador, hide_sitewide, que decide si se oculta del flujo público. El fallo estaba en cómo se calculaba ese indicador: bbPress comprobaba únicamente el estado del foro donde se publicaba, sin subir por la jerarquía para ver si algún foro padre era privado u oculto.
El resultado es una incoherencia de permisos. Si un subforo con estado "publicado" cuelga de un foro privado, el enlace directo a sus temas devuelve correctamente un 404 a quien no tiene acceso, pero el mismo contenido (título, nombre del foro, enlace y cuerpo completo) aparece en el flujo público de actividad de BuddyPress, incluida su API REST, al alcance de cualquier visitante anónimo.
Versiones con las que se reprodujo:
| Componente | Versión | |---|---| | WordPress | 7.1.2 | | BuddyPress | 14.5.2 | | bbPress | 2.6.18 | | PHP | 8.4.24 | | MariaDB | 11.8.8 |
El código afectado está en includes/extend/buddypress/activity.php, en las funciones topic_create y reply_create.
Pasos de reproducción
Escenario previo: un foro privado (/forums/forum/private/) que contiene un subforo con estado publicado (public-subforum, forum_id=6), y un usuario miembro que puede publicar en él.
- Con la sesión del miembro, se crea un tema en el subforo:
curl -s -b cookies.txt -L -d "bbp_topic_title=Test&bbp_topic_content=Secret&bbp_forum_id=6&action=bbp-new-topic&_wpnonce=$N" \
https://site/forums/forum/private/public-subforum/
La petición responde HTTP 200 y redirige a /forums/topic/test/.
- Se comprueba que el enlace directo al tema está protegido para quien no tiene acceso:
curl -s -o /dev/null -w "%{http_code}" https://site/forums/topic/test/
Devuelve 404, como debe.
- Sin sesión, se consulta el flujo de actividad de BuddyPress por su API REST:
curl -s https://site/wp-json/buddypress/v1/activity | grep -o '"rendered":"[^"]*Secret[^"]*"'
La respuesta incluye el cuerpo íntegro del tema:
"rendered":"<p>Secret</p>"
Impacto
- Fuga de datos: el título, el foro, el enlace y el contenido completo de los temas y respuestas publicados en subforos que cuelgan de foros privados u ocultos se difunden en el flujo público de actividad.
- Salto del control de acceso: cualquier visitante sin autenticar, o una cuenta con pocos privilegios, puede leer ese contenido restringido a través del feed o de la API de BuddyPress, aunque el enlace directo devuelva 404.
El programa clasificó el reporte como de severidad media, lo dio por resuelto y pagó una recompensa cuyo importe no es público.
Remediación
El investigador propuso quitar el segundo argumento, false, en la llamada a bbp_is_forum_public() dentro de topic_create y reply_create, para que la función compruebe también los foros antecesores:
// Change from: ! bbp_is_forum_public( $forum_id, false )
'hide_sitewide' => ! bbp_is_forum_public( $forum_id ),
El reporte consta como resuelto, pero no detalla qué cambio exacto aplicó el equipo de bbPress.
Qué aprender de este caso
- En aplicaciones con contenido jerárquico (foros, carpetas, proyectos), revisa si las comprobaciones de visibilidad heredan el estado de los padres: un hijo "público" bajo un padre privado es el caso que suele romperse.
- Cuando un contenido está bien protegido en su URL directa, busca las vías secundarias por las que se replica: feeds de actividad, notificaciones, RSS, buscadores internos y endpoints REST como
/wp-json/buddypress/v1/activity. - Desconfía de los parámetros que recortan una comprobación de permisos, como el
falsedebbp_is_forum_public(): cada llamada que lo usa merece revisar si de verdad le basta con la comprobación parcial. - Al integrar dos plugins, la decisión de qué se muestra fuera debería basarse en la misma lógica de permisos que protege la página original, no en un cálculo aparte hecho al crear la actividad.