Seguridad mejorada: corregimos la vulnerabilidad de ImageMagick
El jueves 20 de junio de 2019, nuestro capitán de soporte dio la alerta roja cuando se reportó una vulnerabilidad que permitía obtener acceso root a un servidor de Transloadit. Este es el máximo nivel de privilegios posible en una máquina y la peor pesadilla de un ingeniero de seguridad o de un fundador de una startup técnica.

En esta publicación de blog revelaremos cómo ocurrió el hackeo, qué impacto tuvo, qué hemos hecho y qué seguimos planeando hacer para evitar que esto vuelva a ocurrir en el futuro.
Antecedentes
Transloadit usa muchas herramientas de encoding distintas para manipular y convertir medios. Para las imágenes usamos ImageMagick y hemos patrocinado este proyecto durante muchos años. Hasta el día de hoy, le debemos muchísimo a ImageMagick.
Los clientes de Transloadit construyen sus negocios sobre el nuestro y esperan que esta base sea sólida. Esto hace que nos resulte difícil cambiar o actualizar software, ya que hacerlo provoca cambios de comportamiento sutiles y no tan sutiles. Por eso, hasta ahora hemos estado lanzando nuevas versiones de stacks, manteniendo su uso opcional. Los clientes pueden probar los nuevos stacks cuando les convenga, mientras que las versiones antiguas siguen brindando el servicio que esperan y del que dependen.
La versión más antigua, conocida internamente como imagemagick_stack v1.0.0, ha contado con nuestro soporte durante diez años. Hemos tenido la intención de deprecarla, pero esto debe hacerse con cuidado. Si quitamos un ladrillo esencial de los cimientos de nuestros clientes, sus negocios podrían venirse abajo. Como en cualquier software, se encuentran errores y vulnerabilidades que necesitan parches. ImageMagick no es la excepción. Con los años, se han descubierto algunas vulnerabilidades graves en imagemagick_stack v1.0.0. Una de ellas hacía posible ejecutar comandos del sistema ocultos en imágenes SVG especialmente diseñadas.
Esto pone a Transloadit entre la espada y la pared. Por un lado, necesitamos ofrecer esta base robusta que nunca cambia; por otro, necesitamos actualizar el software vulnerable para no poner en riesgo a nuestros clientes. Hemos abordado este dilema tomando una tercera opción: deprecar el software vulnerable de forma progresiva. Ayudamos a nuestros clientes a alejarse poco a poco de lo antiguo, mientras ganamos tiempo para hacerlo conteniendo el software defectuoso.
Nuestra contención consiste en:
- Limitar a qué tienen acceso nuestras máquinas de encoding. En este caso, solo a archivos temporales con hash, a S3 y a tomar Jobs de una cola.
- Escanear imágenes SVG (y similares) que contengan comandos del sistema y rechazarlas antes de que ImageMagick opere sobre ellas.
- Ejecutar nuestros procesos como un usuario sin privilegios que no tiene acceso a ningún secreto, más allá de lo que el usuario root haya inyectado en la memoria de su proceso.
Qué ocurrió
El 20 de junio, Jeremy Matos, ingeniero sénior de seguridad en GitLab, reportó que un hacker había obtenido acceso root a uno de nuestros servidores. GitLab había estado realizando una campaña en HackerOne en la que invitan a hackers a exponer vulnerabilidades y ofrecen recompensas por cualquier intento de hackeo exitoso que se les divulgue de forma responsable. GitLab adquirió Gitter.im en 2017, y Gitter (piensa en Slack, pero para proyectos de código abierto) había estado usando nuestros servicios desde 2014. Cuando subes una imagen junto con tus mensajes de chat en Gitter, el proceso de subida y redimensionamiento podía ser gestionado por la plataforma de Transloadit.
El investigador de seguridad Sergey Kashatov entró en este programa e intentó comprometer Gitter.im de GitLab subiendo una imagen con una carga maliciosa. Pensó que había encontrado una vulnerabilidad de GitLab, pero, sin saberlo en ese momento, la imagen terminó siendo procesada en nuestros servidores, comprometiendo así a Transloadit. Jeremy se dio cuenta rápidamente y nos fue transmitiendo la conversación con Sergey hasta que pudimos acercarnos a él directamente y saber cómo había obtenido acceso root.
Cómo ocurrió: el análisis de la «causa raíz»
Richard I. Cook explica en How Complex Systems Fail que
«La atribución posterior a un accidente [del] accidente a una “causa raíz” es fundamentalmente errónea. Dado que un fallo manifiesto requiere múltiples defectos subyacentes, no existe una “causa” aislada de un accidente. Hay múltiples factores que contribuyen a los accidentes».
Aunque quizá existan gradaciones y a veces baste con señalar la anomalía que más contribuyó, en este caso creemos que es útil describir más de una.
Como indicamos antes, éramos conscientes de las vulnerabilidades del stack de ImageMagick v1.0.0 y conteníamos el daño potencial mediante:
Ejecutar nuestros procesos como un usuario sin privilegios
Este mecanismo de protección falló cuando introdujimos un nuevo ejecutor de procesos a principios de este año. Para mitigar la lentitud en la lectura de metadatos, queríamos aprovechar mejor las capacidades multinúcleo de nuestras máquinas de encoding y paralelizar este trabajo iniciando varias instancias del mismo proceso. Para ello, implementamos un orquestador existente que ya se había usado ampliamente en producción.
Sin embargo, al migrar al nuevo supervisor, no nos aseguramos de que siguiera iniciando sus procesos
con el usuario que no tiene privilegios. En cambio, iniciaba los procesos de escaneo de metadatos
como el usuario ubuntu, que tiene el privilegio de acceder a más archivos e incluso de convertirse en
root. Esto pasó desapercibido por dos razones: A) no teníamos monitoreo en producción que
garantizara que nuestros procesos siguieran ejecutándose con el usuario limitado y B) en desarrollo y
pruebas ejecutamos todo con el mismo usuario por comodidad, así que esta situación le parecería
normal a la mayoría de los desarrolladores.
Escanear imágenes SVG (y similares)
Nuestro filtrado, que rechazaría las cargas maliciosas, escanea todos los vectores de ataque posibles, pero no lo hacía de forma recursiva para los includes. Los archivos SVG pueden hacer referencia a otros archivos con la intención de incrustarlos. En ese sentido, los archivos SVG se parecen mucho a los archivos HTML: una mezcla heterogénea de etiquetas XML que describe cómo deben renderizarse las cosas, e incluso puede incluir otras imágenes como parte de ello. Mientras que otros hackers optaron por entregar directamente cargas maliciosas, Sergey decidió subir un SVG válido que hacía referencia a otro archivo que contenía los comandos del sistema maliciosos. Enmascaró esa inclusión y le dio una extensión .jpeg, engañando aún más a nuestro sistema para que creyera que era seguro pasarlo a ImageMagick.
Impacto
Sergey divulgó este problema de forma responsable y no robó ni eliminó ningún dato. Por supuesto, es posible que otro hacker haya explotado esta vulnerabilidad antes de que Sergey la descubriera y sí haya robado o eliminado datos. Nuestra investigación no revela ninguna evidencia en esa dirección, pero no se puede descartar por completo.
Si un hacker malicioso hubiera usado este exploit y obtenido con éxito acceso root a una máquina de encoding, habría podido:
- Inspeccionar archivos temporales. Estos archivos consisten en la entrada o la salida de un Robot y existen brevemente en la máquina de encoding. Sin embargo, los archivos temporales se nombran con UUIDv4 sin guiones y no se pueden rastrear hasta el usuario o cliente original.
- Acceder a los buckets de S3 de Transloadit
Un pequeño punto positivo en este descubrimiento alarmante es que, unos años antes, ya habíamos limitado a qué tienen acceso nuestras máquinas de encoding. Así que, incluso con acceso root, las máquinas no podrían obtener acceso a recursos de AWS (además de S3), a nuestra base de datos ni a los secretos de nuestros clientes.
Medidas correctivas
Como es natural, de inmediato:
- Mejoramos nuestro filtrado para contemplar cargas maliciosas referenciadas externamente y ya no
permitimos este raro tipo de imagen en un stack vulnerable. La Assembly ahora termina con
un error y lanza una advertencia que te pide actualizar al stack
v2.0.3. - Eliminamos el nuevo proceso supervisor y restauramos la ejecución de nuestro proceso como usuario sin privilegios, añadiendo monitoreo en producción para que se nos alerte si esto vuelve a cambiar.
- Creamos una biblioteca en C que impide que ImageMagick haga cualquier petición de red por su cuenta.
- Rotamos todas nuestras claves (por ejemplo, las de nuestros propios buckets de S3) y limitamos aún más el acceso de IAM para las máquinas de encoding, de modo que ahora solo tengan acceso de escritura a los pocos buckets que necesitan (por ejemplo, tmp.transloadit.com). También hicimos que la rotación de claves sea mucho más sencilla, así podemos hacerla en un abrir y cerrar de ojos como precaución general.
- Compensamos a Sergey por su trabajo, dado que GitLab evidentemente tiene la política de que no puede entregar recompensas por vulnerabilidades descubiertas en sistemas que ellos mismos no gestionan.
- Actualizamos todas nuestras máquinas a la última LTS de Ubuntu: bionic, que garantiza cuatro años más de parches y ofrece herramientas adicionales para aislar aún más nuestros procesos de encoding.
Esto debería resolver los problemas inmediatos. Le pedimos a Sergey que confirmara que Transloadit ya no es vulnerable y así lo reconoce. Sin embargo, esto no significa que hayamos terminado. Aún quedan pendientes en nuestra lista:
- Usar el usuario sin privilegios también en desarrollo y pruebas. Esto puede ralentizar un poco el desarrollo, pero esta discrepancia entre producción y desarrollo permitió que esta vulnerabilidad pasara desapercibida.
- Utilizar nuestras nuevas herramientas del sistema operativo para aislar aún más nuestros procesos de encoding y darles acceso únicamente al archivo temporal en el que están trabajando.
- Iniciar el procedimiento de deprecación para eliminar imagemagick_stack v1.0.0. Identificaremos
quién lo sigue usando y lanzaremos advertencias que les recuerden probar un stack más nuevo.
También enviaremos un email a los clientes y les ofreceremos ayuda para adoptar sin problemas
stacks más modernos. Cuando el último cliente deje de usarlo, eliminaremos por completo
v1.0.0. - Implementar una lista de permitidos de variables de entorno para nuestras herramientas de encoding. Aunque nuestras máquinas de encoding solo tengan acceso de escritura a nuestros buckets de S3, realmente no hay ninguna necesidad de que ImageMagick sepa de ello.
Recomendaciones
No creemos que sea necesaria ninguna rotación de contraseñas de tu parte, ya que no hubo acceso a contraseñas ni a nada similar. No tenemos indicios de que este ataque se haya realizado con éxito antes que Sergey. Aunque consideramos que el acceso no autorizado a archivos temporales de encoding es el peor escenario posible, por suerte no todo es tan sombrío. Estos archivos temporales están anonimizados, a menudo no están completos (ya que están en proceso de descarga) y se eliminan en cuanto se ha realizado el encoding y la subida está completa.
Aun así, no podemos descartar la posibilidad de que un hacker haya obtenido archivos temporales y, por supuesto, el medio en sí podría contener información identificable (por ejemplo, una foto de alguien con una etiqueta con su nombre o un letrero de calle que revele su ubicación y, si se pasa a Google o Facebook, esas corporaciones podrían identificar a quién aparece retratado). Por esta razón, te recomendamos que se lo comuniques a tus clientes, tal como nosotros te lo estamos comunicando a ti.
Afortunadamente, no se filtró ninguna credencial como resultado de esta vulnerabilidad, pero eso no es motivo para relajarse. Aunque trabajamos cada día para alcanzar los más altos niveles de seguridad, la seguridad al 100 % siempre será un mito. Por lo tanto, es mejor darnos acceso de escritura a un solo bucket, o incluso a una sola carpeta, que darnos acceso root a todo, solo para que podamos escribir en ese único bucket o carpeta.
Y si aún no lo has hecho, también es buena idea actualizar tu Robot
/image/resize a
"imagemagick_stack": "v2.0.3".
Conclusión
Esto nos ha preocupado profundamente. Mantener a salvo a nuestros clientes a toda costa es vital para la continuidad de Transloadit y nuestra empresa lo significa todo para nosotros. Durante las últimas semanas, hemos trabajado sin descanso para desplegar actualizaciones de modo que algo así no pueda volver a ocurrir. También estamos trabajando con Sergey y otros investigadores de vulnerabilidades como él para asegurarlo. Aún vienen más actualizaciones, pero ya sentimos la necesidad de divulgar este problema de seguridad. Esperamos que esto te dé, como cliente, suficiente tiempo para gestionarlo de la forma más adecuada para tu negocio.
Lamento profundamente los errores de nuestra parte que llevaron a tener esta vulnerabilidad en producción. Si tienes preguntas o inquietudes después de leer esto, por favor no dudes en escribirme: por supuesto, estoy más que dispuesto a ofrecer cualquier aclaración adicional.
