Reports
ALTA[AUTENTICACIÓN]#3329310

Campfire (Basecamp): una clave de bot sin token permitía escribir mensajes en nombre de cualquier usuario

En Campfire, de Basecamp, una clave de bot con la parte del token vacía (por ejemplo `2-`) se aceptaba como válida y permitía, sin iniciar sesión, publicar mensajes en las salas haciéndose pasar por cualquier usuario.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

Campfire, el chat de Basecamp, deja que los bots publiquen mensajes en las salas. Para identificarse, el bot incluye en la URL una "clave de bot" con el formato <id>-<token>. El servidor la valida con esta función:

def authenticate_bot(bot_key)
  bot_id, bot_token = bot_key.split("-")
  active.find_by(id: bot_id, bot_token: bot_token)
end

El problema está en qué pasa cuando la clave no trae nada después del guion. En Ruby, "2-".split("-") devuelve solo ["2"], así que bot_token queda en nil. La consulta pasa a buscar un usuario activo con id = 2 y sin token de bot... y eso es precisamente lo que tienen las cuentas de personas normales, que no son bots y tienen bot_token: nil. Resultado: la "autenticación de bot" devuelve el usuario humano con ese ID.

Para explotarlo basta conocer el ID del usuario que se quiere suplantar. Los IDs son números consecutivos (el investigador lo mostró desde la consola de Rails, donde los usuarios aparecen con IDs 1, 2…), así que se adivinan sin esfuerzo, y además aparecen en algunas URL de la aplicación.

El fallo se clasificó como autenticación incorrecta (Improper Authentication) con severidad alta. Se resolvió y el programa pagó recompensa, aunque el importe no es público. Se reportó el 6 de septiembre de 2025 y se divulgó el 21 de noviembre de 2025.

Pasos de reproducción

  1. Elegir el usuario a suplantar y averiguar su ID numérico. En la prueba se usó el usuario "test", con ID 2.
  2. Elegir una sala a la que ese usuario tenga acceso; en el ejemplo, la sala 2.
  3. Sin iniciar sesión, enviar un mensaje a la ruta de mensajes de bots usando como clave el ID seguido solo de un guion (2-):
POST /rooms/2/2-/messages HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0
Accept: text/vnd.turbo-stream.html, text/html, application/xhtml+xml
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 193

Hello ! I'm the test user, even though I'm not authenticated
  1. Abrir la sala: el mensaje aparece publicado como si lo hubiera escrito el usuario "test" (el investigador lo documentó con una captura).

Impacto

Cualquier persona sin cuenta ni sesión podía publicar mensajes arbitrarios en nombre de cualquier usuario, en las salas a las que ese usuario tuviera acceso. Como los IDs son consecutivos, bastaba con recorrerlos para hablar como cualquier miembro, incluidos los administradores.

Qué aprender de este caso

  • Si una credencial se compone de varias partes separadas (id-token, usuario:clave), prueba a enviar solo la primera con el separador al final (2-) o sin él: un split que deja la segunda parte en nil o vacía puede convertir la comprobación en otra muy distinta.
  • Desconfía de las consultas tipo find_by(id: x, token: y) cuando y viene del usuario: en Rails, un nil se traduce a IS NULL y coincide con todos los registros que no tienen token, como las cuentas que no son bots.
  • Al programarlo, rechaza antes de consultar cualquier clave con el token vacío o ausente, filtra explícitamente por el tipo de cuenta (solo bots) y compara el token con una función de tiempo constante en lugar de meterlo en el WHERE.
  • Las rutas alternativas de autenticación (bots, webhooks, integraciones) suelen revisarse menos que el login normal: son un buen sitio para buscar fallos de suplantación.