Paquetes de Python y PHP envenenados roban contraseñas para acceder a AWS

Un investigador de SANS descubrió recientemente un nuevo y bastante específico tipo de ataque a la cadena de suministro contra módulos de software de código abierto en Python y PHP.

Siguiendo las discusiones en línea sobre un módulo público sospechoso de Python, Yee Ching Tok observó que un paquete llamado ctx en el popular repositorio PyPi había recibido repentinamente una “actualización”, a pesar de no haber sido tocado desde finales de 2014.

En teoría, por supuesto, no hay nada malo en que los viejos paquetes vuelvan a la vida de repente.

A veces, los desarrolladores regresan a los viejos proyectos cuando una pausa en su agenda regular (o un correo electrónico de un usuario antiguo que les hace sentirse culpables) finalmente les da el impulso para aplicar algunas correcciones de errores largamente esperadas.

En otros casos, los nuevos mantenedores dan un paso adelante de buena fe para revivir los proyectos “abandonados”.

Pero los paquetes pueden ser víctimas de tomas de control secretas, en las que la contraseña de la cuenta correspondiente es pirateada, robada, restablecida o comprometida de alguna manera, de modo que el paquete se convierte en una cabeza de playa para una nueva ola de ataques a la cadena de suministro.

En pocas palabras, algunas “renovaciones” de paquetes se llevan a cabo de mala fe, para dar a los ciberdelincuentes un vehículo para la distribución de malware bajo la apariencia de “actualizaciones de seguridad” o “mejoras de funciones”.

Los atacantes no se dirigen necesariamente a ningún usuario específico del paquete que comprometen; a menudo, simplemente están observando y esperando a ver si alguien cae en su trampa y en ese momento tienen una forma de atacar a los usuarios o empresas que pican.

Código nuevo, número de versión antiguo

En este ataque, Yee Ching Tok se dio cuenta de que, aunque el paquete se actualizaba de repente, su número de versión no cambiaba, presumiblemente con la esperanza de que algunas personas [a] utilizaran la nueva versión de todos modos, tal vez incluso automáticamente, pero [b] no se molestaran en buscar las diferencias en el código.

Pero un diff (abreviatura de diferencia, donde sólo se examinan las líneas nuevas, cambiadas o eliminadas en el código) mostraba líneas añadidas de código Python como estas:

Tal vez recuerdes, por Log4Shell, que las llamadas variables de entorno, accesibles a través de os.environ en Python, son configuraciones de clave=valor asociadas a un programa específico en ejecución.

Los datos que se presentan a un programa a través de un bloque de memoria no necesitan ser escritos en el disco, por lo que es una forma práctica de pasar datos secretos, como las claves de cifrado, a la vez que se evita guardar los datos incorrectamente por error.

Sin embargo, si puedes engañar a un programa en ejecución, que ya tendrá acceso al entorno de procesos de sólo memoria, puedes leer los secretos y robarlos, por ejemplo, enviándolos enterrados en el tráfico de red de aspecto normal.

Si dejas intacto el grueso del código fuente que estás atacando, sus funciones habituales seguirán funcionando como antes, por lo que es probable que los retoques malévolos en el paquete pasen desapercibidos.

¿Por qué ahora?

Aparentemente, la razón por la que este paquete fue atacado recientemente es que el nombre del servidor utilizado para el correo electrónico por el mantenedor original acababa de expirar.

Por lo tanto, los atacantes pudieron comprar el nombre de dominio que ya no se utilizaba, crear un servidor de correo electrónico propio y restablecer la contraseña de la cuenta.

Curiosamente, el paquete ctx envenenado fue actualizado dos veces más, con más “salsa secreta” escondida en el código infectado, esta vez incluyendo un código más agresivo de robo de datos.

La línea requests.get() que se muestra a continuación conecta con un servidor externo controlado por los delincuentes, aunque aquí hemos eliminado el nombre del dominio:

El servidor de exfiltración recibirá las variables de entorno codificadas (incluyendo cualquier dato robado, como las claves de acceso) como una cadena de aspecto inocente de datos de aspecto aleatorio al final de la URL.

La respuesta que se reciba no importa en realidad, porque lo que buscan los atacantes es la solicitud saliente, completa con los datos secretos añadidos.

Si quieres probar esto por ti mismo, puedes crear un programa independiente en Python basado en el pseudocódigo anterior, como este:

A continuación, inicia un pseudoservidor HTTP de escucha en una ventana separada (utilizamos la excelente utilidad ncat del conjunto de herramientas Nmap, como se ve a continuación), y ejecuta el código Python.

Aquí, estamos en el shell de Bash, y hemos utilizado env -i para eliminar las variables de entorno para ahorrar espacio, y hemos ejecutado el script de exfiltración de Python con una variable de entorno de AWS falsa (la clave de acceso que elegimos es uno de los ejemplos deliberadamente no funcionales de Amazon utilizados para la documentación):

El servidor de escucha (es necesario iniciarlo primero para que el código de Python tenga algo a lo que conectarse) responderá a la solicitud y volcará los datos que se enviaron:

La línea GET/… de arriba captura los datos codificados que fueron exfiltrados en la URL.

Ahora podemos decodificar los datos base64 de la petición GET y revelar la clave falsa de AWS que añadimos al entorno del proceso en la otra ventana:

Criminalidad relacionada

Intrigado, Yee Ching Tok buscó el nombre del servidor de exfiltración que borramos anteriormente.

El mismo servidor apareció en el código subido recientemente a un proyecto PHP en GitHub, presumiblemente porque fue comprometido por los mismos atacantes más o menos al mismo tiempo.

Ese proyecto es lo que solía ser un legítimo kit de herramientas de hash de PHP llamado phppass, pero ahora contiene estas tres líneas de código no deseado y peligroso:

Aquí, cualquier secreto de acceso de Amazon Web Services, que son cadenas de caracteres pseudoaleatorias, se extrae de la memoria del entorno (getenv() es el equivalente en PHP de os.environ.get() en el código Python falso que vimos antes) y se convierte en una URL.

Esta vez, los delincuentes han utilizado http en lugar de https, por lo que no sólo roban tus datos secretos para ellos, sino que también realizan la conexión sin cifrado, exponiendo así tus secretos de AWS a cualquier persona que registre tu tráfico mientras atraviesa Internet.

¿Qué hacer?

  • No aceptes ciegamente las actualizaciones de paquetes de código abierto cuando aparezcan. Revisa tú mismo las diferencias de código antes de decidir que la actualización te interesa. Sí, los delincuentes suelen ocultar sus cambios de código ilegales de forma más sutil que los hacks que ves arriba, por lo que puede que no sea tan fácil de detectar. Pero si no miras nunca, los delincuentes pueden salirse con la suya.
  • Comprueba si hay cambios sospechosos en la cuenta de cualquier mantenedor antes de confiar en él. Busca en la documentación de la versión anterior del código (presumiblemente, código que ya tienes) los datos de contacto del anterior mantenedor, y mira qué ha cambiado en la cuenta desde la última actualización. En particular, si ves nombres de dominio que han caducado y se han vuelto a registrar recientemente, o cambios de correo electrónico que introducen nuevos mantenedores sin interés previo evidente en el proyecto, sospecha.
  • No confíes sólo en las pruebas de los módulos que verifican el comportamiento correcto. Intenta realizar pruebas genéricas que busquen también comportamientos no deseados, inusuales e inesperados, especialmente si ese comportamiento no tiene una conexión obvia con el paquete que has cambiado. Por ejemplo, una utilidad para calcular los hash de las contraseñas no debería realizar conexiones de red, por lo que si se detecta que lo hace (utilizando datos de prueba en lugar de información real, por supuesto), deberías sospechar que se trata de un juego sucio.

Las herramientas de detección de amenazas como Sophos XDR pueden ayudar en este caso, ya que te permiten vigilar los programas que estás probando, y luego revisar su registro de actividad en busca de tipos de comportamiento que no deberían estar allí.

Al fin y al cabo, si sabes lo que se supone que debe hacer tu software, también deberías saber lo que se supone que no debe hacer.