Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
ifuncd-up — GNU IFUNC est le véritable responsable derrière CVE-2024-3094 | Kitploit
Outils/GitHubGitHub/robertdfrench/ifuncd-up
Analyse des VulnérabilitésAnalyse Dynamique de Code (DAST)ExploitationRétro-ingénierieAnalyse de BinairesSécurité de la Chaîne LogistiqueArticles et RechercheApprentissage et Éducation
GitHubrobertdfrench/ifuncd-up

ifuncd-up

GNU IFUNC est le véritable responsable derrière CVE-2024-3094

Voir le dépôt
6017il y a 4 moisVérifié par Kitploit
Site web

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

En réponse à Hacker News

Je vois que vous, les rigolos, sur [le site orange][hn], me menez la vie dure. Je vais apporter quelques réponses ciblées ci-dessous, mais d’abord je lance un défi : j’enverrai 500 $ de ma poche à la première personne qui démontrera cette attaque sans ifunc. Je suis sincèrement intéressé et prêt à payer pour l’illumination. Forkez ce dépôt et soumettez une PR avec votre PoC fonctionnel, je vous demanderai votre adresse postale en privé si vous gagnez. Passons aux réponses :

C’est aboyer au mauvais arbre.

Mon petit, moi je vis dans le mauvais arbre, je peux aboyer sur qui je veux.

Ce n’était pas essentiel à l’exploit,

C’est toi qui n’es pas essentiel à l’exploit !

Il y a toujours selinux si on veut ajouter une protection contre l’exécution de code arbitraire en tant que root.

Une fois chargée, cette attaque n’avait plus besoin de franchir d’autres barrières d’appels système. Donc oui, on aurait pu restreindre une session root « bonus », mais on aurait quand même eu des invités non invités sur la machine !

Mais qu’est‑ce que c’est, l’anniversaire de Bilbo ?? Pas d’entrée sauf pour affaires de fête !!

  1. IFUNC n’est pas le seul moyen d’exécuter du code avant main.

Mais c’est un moyen inutile d’exécuter du code avant que la protection mémoire soit mise en place.

  1. L’alternative qu’ils présentent est potentiellement moins sécurisée car le pointeur de fonction restera inscriptible pendant toute la durée de vie du processus.

On peut improviser avec mprotect ! Voir la dernière phrase au‑dessus de la sous‑section Modification de LD_PRELOAD.

Ouais, ce blog est mal orienté.

Excusez‑moi, ce blog était désorienté. J’ai fait toutes ces bêtises tout seul ! Personne ne m’a forcé à être aussi stupide.

IFUNC devrait être implémenté par le logiciel [client] lui‑même,

@CountWSS 💯 carrément ouais

une série de défaillances de processus flagrantes de la part du mainteneur Github jusqu’à…

C’est le seul point auquel je répondrai sérieusement :

Je trouve extrêmement injuste envers le mainteneur de xz-utils et assez dangereux pour la communauté de considérer que cela commence par une erreur de sa part. Cela a commencé parce que personne ne se souciait d’aider à maintenir ce projet. L’attaquant s’est appuyé sur ifunc comme vulnérabilité technique et sur notre négligence collective de xz-utils comme vulnérabilité sociale. Je trouve honteux de voir les actions de M. Collin autrement qu’un dévouement héroïque de plusieurs années au service de la communauté.

Et d’ailleurs Bruce Schneier est d’accord avec moi donc… désolé pour vous, votre argument est cuit.

Le langage était peut‑être plus dur que nécessaire

Vous n’allez pas croire à quel point mes amis m’ont fait atténuer tout ça d’abord.

Les distributions Linux ne devraient pas avoir une si haute estime d’elles‑mêmes au point d’attendre qu’OpenBSD se conforme et s’adapte à leur bordel

@debazel!!! Me gusta.

Quelle connerie totale.

OK, cette partie est exacte.

IFUNC’d up

Pourquoi vous devriez arrêter de blâmer xz-utils pour [CVE-2024-3094][nvd]. Et aussi regardez ma présentation ETSA !

Je pense être IFUNC’d up

CVE-2024-3094, plus connue sous le nom de « backdoor xz-utils », a failli être une catastrophe pour la cybersécurité mondiale. Si cette attaque n’avait pas été découverte de justesse par [Andres Freund][freund], la plupart des serveurs SSH de notre planète auraient commencé à accorder un accès root à l’acteur derrière cette attaque.

Malheureusement, trop d’analyses se sont concentrées sur la façon dont le [code malveillant][JiaT75] s’est introduit dans le dépôt xz-utils. Je voudrais plutôt soutenir que deux choix de conception de longue date dans des logiciels libres critiques ont rendu cette attaque possible : [le lien d’OpenSSH avec SystemD][biebl], et l’existence de [GNU IFUNC][sourceware].

Avant de commencer : Une grande partie de cette discussion traite des subtilités de l’édition de liens dynamiques sous Linux. Si vous avez besoin d’une piqûre de rappel, jetez un œil à dynamic_linking.md.

Bref résumé de CVE-2024-3094

Il existe des tonnes de bons articles détaillant les aspects généraux de la backdoor xz-utils, comme celui de Dan Goodin [Ce que nous savons de la backdoor xz-Utils qui a failli infecter le monde][goodin1] et le gist de Sam James [FAQ sur la backdoor xz-utils (CVE-2024-3094)][thesamesam]. Pas besoin de tout répéter ici, donc pour les besoins de cet article, voici un résumé très grossier :

  • Certaines distributions Linux modifient OpenSSH pour dépendre de SystemD.
  • SystemD dépend de xz-utils, qui utilise GNU IFUNC.
  • Par conséquent, xz-utils se retrouve dans l’espace d’adressage d’OpenSSH.
  • Cela permet à ifunc de modifier du code dans le serveur SSH.```mermaid flowchart TD G["GNU IFUNC"] A["OpenSSH (OpenBSD)"] B["Portable OpenSSH
    (Linux / macOS / etc)"] C[OpenSSH + IFUNC] D[xz-utils] E["SystemD (Linux)"] A -->|Remove OpenBSD specifics| B B -->|Add SystemD specifics| C D --> E E --> C C --> F["Mayhem"] G --> D
root@kitploit:~
## Pourquoi les distributions Linux modifient-elles OpenSSH ?
La réponse courte est qu'elles y sont obligées. OpenSSH est développé par la communauté OpenBSD, pour la communauté OpenBSD, et ils se moquent éperdument de Linux.  Le projet [Portable OpenSSH][mindrot] est un ensemble de correctifs de bonne volonté qui remplacent les composants spécifiques à OpenBSD par des composants POSIX génériques, ainsi que du code spécifique à certaines plateformes le cas échéant. La chaîne d'approvisionnement logicielle pour SSH ressemble en pratique à ceci :```mermaid
flowchart TD
  subgraph OpenBSD Folks
    A[OpenBSD]
    B[OpenSSH]
    H[improvements]
  end
  B-->A
  A-->H
  H-->B

  B-->C
  C[Portable OpenSSH]

  subgraph Debian Folks
    D[Debian SSH]
    G[improvements]
  end
  C-->D
  D-->G
  G-->C

  subgraph Fedora Folks
    J[Fedora SSH]
    K[improvements]
  end
  C-->J
  J-->K
  K-->C

La version d'OpenSSH d'OpenBSD est en amont de toutes les autres, et la plupart des améliorations proviennent de la communauté OpenBSD. Ces modifications descendent en aval vers le projet Portable OpenSSH, qui tente de réimplémenter les nouvelles fonctionnalités d'une manière qui n'est pas spécifique à OpenBSD. C'est ce qui permet à SSH de fonctionner sur des plateformes comme Linux, macOS, FreeBSD, et même Windows.

Mais cela ne s'arrête pas là. Certains systèmes d'exploitation appliquent des personnalisations supplémentaires au-delà de ce que fournit Portable OpenSSH. Par exemple, Apple ajoute le drapeau [--apple-use-keychain][keith] à ssh-add pour l'aider à s'intégrer avec le gestionnaire de mots de passe de macOS.

Dans le cas de CVE-2024-3094, Fedora et Debian ont maintenu leurs propres [correctifs SystemD][biebl] pour leurs forks d'OpenSSH afin de corriger une [condition de concurrence autour des redémarrages de sshd][schmidt]. Ainsi, la véritable chaîne d'approvisionnement de SSH a commencé à ressembler à ceci :```mermaid flowchart TD A[OpenSSH] B[Portable OpenSSH] C[Debian SSH] D[Fedora SSH] A-->B B-->C B-->D C<-->|SystemD Patches|D

root@kitploit:~
Ces correctifs n'ont jamais été intégrés dans Portable OpenSSH, car les développeurs de Portable OpenSSH ['n'étaient pas intéressés par une dépendance à libsystemd'][djmdjm]. Et ils n'ont jamais été intégrés dans l'OpenSSH officiel, car OpenBSD n'a aucun besoin de prendre en charge SystemD.



### Préoccupations concernant la « Séparation des préoccupations »
Cela semble assez inoffensif, mais c'est un exemple d'un problème bien plus vaste dans l'Open Source, particulièrement sous Linux : des composants critiques du système d'exploitation sont développés par des personnes qui ne se connaissent pas et ne communiquent pas entre elles. 

* Les personnes qui ont patché OpenSSH pour SystemD savaient-elles (ou se souciaient-elles) que libsystemd dépend de xz-utils ?
* Les développeurs de SystemD savaient-ils (ou se souciaient-ils) que xz-utils avait commencé à utiliser ifunc ?
* Les développeurs d'OpenSSH savaient-ils (ou se souciaient-ils) qu'ifunc existait ? Ce n'est certainement pas quelque chose sur OpenBSD.

Dans un sens, cette rupture de communication est une caractéristique de l'open source : je peux adapter votre travail à mes besoins sans avoir à vous déranger. Mais cela peut aussi conduire à un degré d'indirection qui empêche de maintenir des hypothèses de conception critiques (comme un processus de liaison dynamique traditionnel).

Le corollaire évident de la [Loi de Conway][conway] est que si vous expédiez votre organigramme, vous expédiez aussi les bogues qui se cachent dans les fissures de votre organigramme. Aucune personne ni aucune équipe n'a vraiment commis d'erreur ici, mais avec le recul, il est clair que les attaquants ont perçu que la main gauche de Debian/Fedora SSH ne savait pas ce que faisait la main droite de xz-utils.




## Que est-ce que GNU IFUNC est *censé* faire ?
Il vous permet de déterminer, à l'exécution, quelle version d'une fonction vous souhaitez utiliser. Il le fait en vous donnant l'opportunité d'exécuter du **code arbitraire** pour influencer la manière dont l'éditeur de liens résout les symboles.

![](https://assets.kitploit.com/production/public/readmes/31477/ea52603b42c9b38ff0f8f693b09c5d5c0d107a4175f3be4e1e48b39c9ca3eb55.png)



### Détection des fonctionnalités CPU
Supposons que vous ayez une application qui doit s'exécuter sur une grande variété de processeurs x86. Selon les fonctionnalités spécifiques du processeur actuel, vous pouvez préférer utiliser différents algorithmes pour la même tâche. L'idée originale derrière IFUNC était de permettre aux programmes de vérifier les fonctionnalités du processeur la première fois qu'une fonction est appelée, puis d'utiliser une implémentation qui sera la plus appropriée pour ce processeur.

Jetez un œil à [`cpu_demo.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/cpu_demo.c) :```c
void print_cpu_info() __attribute__((ifunc ("resolve_cpu_info")));
void print_avx2() { printf("AVX2 is present.\n"); }
void print_nope() { printf("AVX2 is missing.\n"); }

static void* resolve_cpu_info(void) {
    __builtin_cpu_init();

	if (__builtin_cpu_supports("avx2")) {
		return print_avx2;
	} else {
		return print_nope;
	}
}

int main() {
	print_cpu_info();
	return 0;
}

Ce programme montre l'utilisation la plus courante d'IFUNC : il demande au processeur s'il supporte ou non certaines fonctionnalités, et fournit une implémentation différente d'une fonction en fonction des fonctionnalités supportées. Dans ce cas, notre fonction print_cpu_info affichera soit "AVX2 est présent" soit "AVX2 est absent" selon l'ancienneté de votre processeur.

Interrogation de l'environnement du processus

Bien qu'IFUNC soit destiné à sonder les capacités du CPU, rien ne vous empêche d'exécuter du code plus complexe dans vos résolveurs. Par exemple, tty_demo.c montre comment charger une implémentation de fonction différente selon que STDOUT est un fichier ou un terminal:```c // Print Green text to the Terminal void print_to_tty(const char *message) { const char *green_start = "\033[32m"; const char *color_reset = "\033[0m"; printf("%sTTY: %s%s\n", green_start, message, color_reset); }

// Print plain text to a file void print_to_file(const char *message) { printf("FILE: %s\n", message); }

void print_message(const char *message)
attribute((ifunc("resolve_print_function")));

void (*resolve_print_function(void))(const char *) { struct termios term;

root@kitploit:~
// Ask the kernel whether stdout is a file or a tty
int result = ioctl(STDOUT_FILENO, TCGETS, &term);
if (result == 0) {
    // stdout is a terminal
    return print_to_tty;
} else {
    // stdout is not a terminal
    return print_to_file;
}

}

int main() { print_message("Hello, World!"); return 0; }

root@kitploit:~
Ce n'est pas vraiment l'utilisation prévue d'IFUNC, mais cela montre ce qui est possible : vous pouvez exécuter du code arbitraire avant `main` dans tout programme qui utilise un IFUNC que vous avez déclaré.





## IFUNC est probablement une mauvaise idée
![](https://assets.kitploit.com/production/public/readmes/31477/6d420d0d1b4f4c49a3ba8a4b2103d7f79d284c9d8fbda4449e6e6d0afa5b3cfa.png)

GNU IFUNC est difficile à implémenter, difficile à utiliser correctement, et (en tant qu'outil de performance supposé) n'est pas beaucoup plus rapide que les alternatives. Comme nous l'avons vu avec CVE-2024-3094, c'est aussi un outil très puissant pour les attaques sur la chaîne d'approvisionnement logicielle.

IFUNC est largement utilisé dans la bibliothèque GNU C, et cela est probablement acceptable. Ce sont les personnes pour lesquelles il a été initialement développé, et elles sont étroitement liées aux équipes du compilateur et de l'éditeur de liens qui implémentent réellement IFUNC. Elles sont les mieux placées pour comprendre les compromis, et il existe des tonnes de fonctions libc qui bénéficient d'implémentations spécifiques au CPU. Je pense que nous devrions considérer IFUNC comme une interface interne pour glibc et éviter son utilisation dans d'autres applications.



### C'est trop déroutant pour être utilisé en toute sécurité
ifunc est beaucoup trop difficile à utiliser. Il y a trop de [cas particuliers][nagy], et la [documentation officielle][gnu-cfa] est [insuffisante][sourceware]. Cela donne aux utilisateurs l'idée trompeuse que l'adoption d'ifunc est simple.

Même plusieurs années après que ifunc soit devenu disponible, l'interface annoncée [ne fonctionnait pas][agner]. Les développeurs de GCC l'ont qualifiée [d'erreur][odonell] et ont envisagé d'ajouter des avertissements pour compenser la fragilité d'IFUNC :

> Les solutions glibc nécessaires pour rendre IFUNC robuste ne sont pas en place, et nous devons donc faire ce que nous pouvons pour avertir les utilisateurs qu'il pourrait se casser.

Il n'y a pas qu'IFUNC non plus. Le Mach-O d'Apple a une fonctionnalité similaire appelée `.symbol_resolver` qu'ils [« regrettent d'avoir ajoutée »][rjmccall].



### Cela compromet RELRO
En permettant l'exécution de code arbitraire alors que la table de décalage globale est encore accessible en écriture, les protections offertes par [RELRO](https://github.com/robertdfrench/ifuncd-up/blob/trunk/dynamic_linking.md#relro) sont [rendues inutiles][binarly-io].

Ceci est important à noter, car RELRO se présente comme un moyen de protéger l'intégrité des symboles chargés dynamiquement. Du point de vue de l'utilisateur (vous, en tant qu'utilisateur du compilateur et de l'éditeur de liens), cela viole le [principe de moindre étonnement][pola] : aucune personne raisonnable ne s'attendrait à ce que *le chargement d'une bibliothèque dynamique* compromette une fonctionnalité de sécurité conçue pour *protéger les bibliothèques dynamiques*.

![](https://assets.kitploit.com/production/public/readmes/31477/ad252f1cb2937e3f8b168ece1e3f15455dcb0d7647b8715dff7435f3babc0e1c.png)



### Ce n'est pas toujours nécessaire
Il existe plusieurs autres façons de gérer cette situation. Elles ont chacune des compromis différents, mais elles sont toutes bien plus simples qu'IFUNC. Toutes ces méthodes sont plus portables qu'IFUNC, plus faciles à comprendre et plus difficiles à exploiter.

> "Ifunc est juste une façon complètement stupide de faire de la sélection de code spécifique à la microarchitecture à l'exécution."
>
> -- [Rich Felker](https://hachyderm.io/@dalias/112952237145378821), mainteneur de [musl](https://musl.libc.org).


#### Pointeurs de fonction globaux
IFUNC est attrayant car il permet aux développeurs d'exprimer la sélection de fonctions *déclarativement* plutôt qu'*impérativement*. Mais le faire de manière impérative n'est en fait pas si difficile. Considérez [`static_pointer.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/static_pointer.c), qui résout un pointeur de fonction global à l'exécution :```c
static int (*triple)(int) = 0;
int triple_sse42(int n) { return 3 * n; }
int triple_plain(int n) { return n + n + n; }

void print_fifteen() {
	int fifteen = triple(5);
	printf("%d\n", fifteen);
}

int main() {
	__builtin_cpu_init();
	if (__builtin_cpu_supports("sse4.2")) {
		triple = triple_sse42;
	} else {
		triple = triple_plain;
	}
	
	print_fifteen();
	return 0;
}

Est-ce vraiment si grave que nous ayons besoin d'artifices spéciaux dans l'éditeur de liens pour l'éviter ?

Un inconvénient de cette approche est que le pointeur de fonction triple est modifiable à l'exécution, alors qu'IFUNC+RELRO garantirait que les adresses ifunc dans la GOT sont immuables une fois résolues. Cependant, avec un peu d'effort supplémentaire, nous pourrions utiliser [mprotect(2)][mprotect] pour marquer ces pointeurs en lecture seule.

Modifier LD_PRELOAD

Si vous savez de quelles fonctionnalités CPU votre code a besoin et que vous disposez d'une copie distincte de votre bibliothèque dynamique pour chaque cas, vous pouvez accomplir la même chose en spécifiant la bonne bibliothèque avec $LD_PRELOAD comme suit :```bash #!/bin/bash if (cat /proc/cpuinfo | grep flags | grep avx2 > /dev/null); then LD_PRELOAD=./myfunc_avx2.so ./my_app else LD_PRELOAD=./myfunc_normal.so ./my_app fi

root@kitploit:~
(Si vous n'êtes pas familier avec `LD_PRELOAD`, consultez le ["Tutoriel simple sur `LD_PRELOAD`"][catonmat] de catonmat.)



#### Binaires séparés par combinaison de fonctionnalités
Combien de combinaisons uniques de fonctionnalités CPU devez-vous vraiment supporter ? Combien existent même ?

De prime abord, cela ressemble à une explosion combinatoire. Il existe des dizaines d'extensions différentes d'arithmétique vectorielle, de virtualisation et de sécurité pour l'ISA amd64. Mais ces fonctionnalités n'apparaissent pas indépendamment dans la nature. Par exemple, aucun CPU disposant d'AVX-512 ne manque de SSE4.2 ou d'AES-NI.

Savoir de quelles fonctionnalités CPU votre application a besoin, et lesquelles apparaissent ensemble sur les puces réelles, peut vous aider à déterminer combien de binaires distincts vous devez livrer. Ce n'est peut-être pas autant que vous le pensez. La plupart des gestionnaires de paquets vous permettent d'exécuter des scripts au moment de l'installation ; vous pouvez livrer plusieurs binaires dans un seul fichier rpm ou deb et utiliser une logique d'installation pour choisir le meilleur pour le CPU hôte.



### Ce n'est pas beaucoup plus rapide que les alternatives

> Ce qui m'a semblé évident depuis longtemps, c'est que même s'il y avait un avantage de performance à ifunc, cela ne pourrait être que lorsque l'appel de fonction entier est si court que le surcoût d'appel peut représenter une part significative du temps total.
>
> -- [Rich Felker](https://hachyderm.io/@dalias/113074264762553873),
> mainteneur de [musl](https://musl.libc.org).

Étant donné que la justification habituelle d'ifunc est liée à la performance, j'ai voulu voir combien de surcoût *ifunc lui-même* cause. Après tout, toute fonction qui mérite d'être optimisée est probablement appelée fréquemment, donc le surcoût de l'invocation de la fonction mérite d'être reconnu.

Pour le déterminer, j'ai conçu une expérience qui appelle une fonction *résolue dynamiquement* en boucle serrée. Jetez un œil à [`speed_demo/ifunc`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/ifunc/main.c) et [`speed_demo/pointer`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/pointer/main.c). Ces deux programmes font le même travail (incrémenter un compteur statique), mais les fonctions d'incrémentation sont résolues de différentes manières : la première utilise GNU IFUNC, et la seconde repose sur de simples pointeurs de fonction.

Voici la logique globale :

1. Appeler une fonction de résolution pour déterminer quel incrémenteur utiliser.
1. Enregistrer cette réponse quelque part (dans la GOT, ou comme pointeur de fonction).
1. Appeler cette fonction d'incrémentation quelques milliards de fois pour obtenir une estimation de son coût.

Comme contrôle, il y a aussi [`speed_demo/fixed`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/fixed/main.c) qui fait le même travail d'incrémentation mais sans aucune fonction résolue dynamiquement. Cela peut être utilisé pour obtenir une estimation de la partie du temps d'exécution dédiée à l'invocation de fonction par rapport à la partie qui ne fait que de l'addition.

La cible Makefile `rigorous_speed_demo` effectue plusieurs exécutions de chacun de ces programmes et produit quelques statistiques simples sur leurs performances. Ces nombres changeront bien sûr en fonction de votre matériel, mais le test `fixed` devrait servir de référence pour la comparaison.

| *Résultats* | LOW  | HIGH | AVG   |
|-----------|------|------|-------|
| fixed     | 2.93 | 4.20 | 3.477 |
| ifunc     | 9.50 | 10.56| 9.986 |
| pointer   | 6.23 | 7.44 | 6.791 |

Ce que nous voyons ici, c'est qu'ifunc a un surcoût non négligeable par rapport à l'utilisation d'un simple pointeur de fonction. En moyenne, sur mon matériel, il faut environ deux fois plus de temps pour appeler une fonction ifunc 2 milliards de fois que pour invoquer un pointeur de fonction 2 milliards de fois.

Est-ce que cela a de l'importance dans la vie réelle ? Absolument pas. Les fonctions qui valent la peine d'être optimisées sont beaucoup plus coûteuses que les fonctions "incrémenter de un" que nous analysons ici. C'est seulement intéressant parce que GNU IFUNC prétend être un atout pour la performance, mais semble encourir plus de coût que les pointeurs de fonction.


#### Performances d'autres techniques
Il existe d'autres techniques plus lentes qu'ifunc. Jetez un œil à `super_rigorous_speed_demo`, qui introduit deux autres expériences : [`speed_demo/upfront`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/upfront/main.c) et [`speed_demo/always`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/speed_demo/always/main.c).

`speed_demo/upfront` se comporte de manière similaire à `speed_demo/pointer`, sauf qu'il stocke les résultats des vérifications des fonctionnalités CPU dans des variables globales plutôt que de garder la trace d'un pointeur de fonction. Cela nécessite toujours qu'une fonction de "résolution" s'exécute d'abord pour déterminer quelle implémentation est utilisée, en fonction de la valeur de ces variables globales. Cette technique s'avère plus lente qu'ifunc, mais elle est aussi plus sûre que le stockage de pointeurs de fonction : alors que les pointeurs de fonction peuvent être définis à des valeurs arbitraires, les indicateurs booléens ne le peuvent pas. Ainsi, un attaquant capable de modifier ces variables peut rendre le programme *plus lent*, mais ne peut pas le faire se comporter *différemment*.

`speed_demo/always` est conçu pour être la technique la plus lente -- il vérifie toutes les fonctionnalités CPU nécessaires à chaque fois qu'une implémentation est nécessaire et en choisit une à la volée. Curieusement, cette technique n'est pas significativement plus lente que les autres. Elle n'est que marginalement plus lente qu'ifunc dans le cas où nous n'avons qu'une seule fonctionnalité CPU à vérifier.

| TEST    | LOW  | HIGH  | AVG     |
|---------|------|-------|---------|
| fixed   | 5.02 | 5.70  | 5.37    |
| pointer | 6.40 | 7.02  | 6.66    |
| ifunc   | 8.56 | 11.11 | 9.64    |
| upfront | 9.24 | 9.41  | 9.33333 |
| always  | 10.07| 10.56 | 10.2333 |




## Conclusion
GNU IFUNC est une fonctionnalité de niche de gcc/ld.so que peu de gens connaissaient avant qu'elle ne soit utilisée dans CVE-2024-3094. Elle présente des pièges non évidents et une documentation insuffisante. En permettant à l'éditeur de liens d'exécuter du code arbitraire avant `main`, avant que des parties critiques de l'image du processus aient été initialisées et protégées, elle sape l'une des hypothèses les plus fondamentales de la programmation : que le simple fait de charger une bibliothèque ne va pas intrinsèquement *changer* votre programme.

Les avantages en matière de performance d'IFUNC sont réels, mais pas significativement meilleurs que les alternatives. La simplicité de déployer un seul binaire optimisé pour plusieurs CPU est très attrayante, mais elle peut être réalisée avec des techniques plus simples (comme les pointeurs de fonction).

Je pense qu'IFUNC devrait être désactivé par défaut dans gcc. L'activer devrait nécessiter un drapeau à l'apparence effrayante, comme `--enable-cve-2024-3094`. Quiconque l'utilise en dehors de libc devrait être tenu de fournir un argument rigoureux et bien documenté selon lequel aucune solution alternative n'est appropriée.

![Oui, toutes les bibliothèques partagées](https://assets.kitploit.com/production/public/readmes/31477/039631e9f0441a38341d4f2299c0666f63a731c02cb2239257afd7613455461c.png)

<!-- REFERENCES -->
[aes-ni]: https://en.wikipedia.org/wiki/AES_instruction_set
[agner]: https://www.agner.org/optimize/blog/read.php?i=167
[biebl]: https://salsa.debian.org/ssh-team/openssh/-/commit/818791ef8edf087481bd49eb32335c8d7e1953d6
[binarly-io]: https://github.com/binarly-io/binary-risk-intelligence/tree/master/xz-backdoor
[catonmat]: https://catonmat.net/simple-ld-preload-tutorial
[conway]: https://en.wikipedia.org/wiki/Conway%27s_law
[djmdjm]: https://github.com/openssh/openssh-portable/pull/251#issuecomment-2027935208
[fr0gger]: https://infosec.exchange/@fr0gger/112189232773640259
[freund]: https://www.openwall.com/lists/oss-security/2024/03/29/4
[gnu-cfa]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-ifunc-function-attribute
[goodin1]: https://arstechnica.com/security/2024/04/what-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world/
[hn]: https://news.ycombinator.com/item?id=48056749
[jasoncc]: https://jasoncc.github.io/gnu_gcc_glibc/gnu-ifunc.html#relocations-and-pic
[JiaT75]: https://github.com/tukaani-project/xz/commit/cf44e4b7f5dfdbf8c78aef377c10f71e274f63c0
[keith]: https://keith.github.io/xcode-man-pages/ssh-add.1.html#apple-use-keychain
[mindrot]: https://anongit.mindrot.org/openssh.git
[mprotect]: https://www.man7.org/linux/man-pages/man2/mprotect.2.html
[musl]: https://musl.libc.org
[nagy]: https://sourceware.org/legacy-ml/libc-alpha/2015-11/msg00108.html
[nvd]: https://nvd.nist.gov/vuln/detail/CVE-2024-3094
[odonell]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70082#c0
[OpenSSH9.8p1]: https://www.openssh.com/releasenotes.html#9.8p1
[openssh-unix-dev]: https://marc.info/?l=openssh-unix-dev&m=171288895109872&w=2
[pola]: https://en.wikipedia.org/wiki/Principle_of_least_astonishment
[rjmccall]: https://reviews.llvm.org/D139163#3993795
[schmidt]: https://bugzilla.redhat.com/show_bug.cgi?id=1381997#c4
[sourceware]: https://sourceware.org/glibc/wiki/GNU_IFUNC
[thesamesam]: https://gist.github.com/thesamesam/223949d5a074ebc3dce9ee78baad9e27#design
Télécharger l’outil