
CVE-2026-77818 - Yordam Kütüphane Otomasyon Sistemi - Injection HTML réfléchie à trois points distincts, détournement d'action de formulaire et vol d'identifiants (CWE-79)
CVE-2026-77818 · CVSS 3.1 6.1 (Moyen) · Présidence de la Cybersécurité · Publication 2026-09-04 · TR-26-1011
Statut : Les vulnérabilités ont été corrigées dans la version v22.2. Les installations concernées doivent être mises à niveau vers v22.2 ou une version ultérieure.
Le système d'automatisation de bibliothèque Yordam est un logiciel commercial d'automatisation de bibliothèque et de catalogue en ligne (OPAC) largement utilisé dans les bibliothèques universitaires, publiques et institutionnelles en Turquie. L'installation est sur site ; chaque client exécute une copie distincte au sein de son institution.
Dans la version v22.1 du produit, on trouve des injections HTML réfléchies à trois points distincts et indépendants. Aucune des trois ne nécessite d'authentification, et chacune est déclenchée par un simple lien.
| # | Point | Cause racine |
|---|---|---|
| 1 | Page de connexion, paramètre devam | Aucun échappement appliqué |
| 2 | Attribut value d'un champ de formulaire masqué | Double décodage URL après échappement |
| 3 | Attribut name d'un champ de formulaire masqué | Échappement appliqué uniquement à la valeur, pas au nom |
Comme les trois se trouvent dans la même version du même produit et relèvent de la même classe de vulnérabilité, elles ont été regroupées sous un seul avis et publiées sous un seul identifiant CVE. En termes d'impact, le point n° 1 est le plus grave.
devamC'est le plus critique. Le point d'injection se trouve directement dans la balise HTML du formulaire d'authentification lui-même.
Le paramètre devam transporte l'adresse vers laquelle l'utilisateur reviendra après la connexion et arrive encodé en hexadécimal — la valeur 2f796f7264616d2f signifie /yordam/. L'application décode cette valeur depuis l'hexadécimal et l'écrit dans la balise d'ouverture du formulaire de connexion. Aucun échappement n'est appliqué entre les deux :
<form class='girisForm collapse show ikiAdimliGiris' method='post'
action='inc/islem.fm.inc.php'
data-url='<ENTRÉE UTILISATEUR DÉCODÉE EN HEXADÉCIMAL>'
autocomplete="off">
Dans la sortie, les caractères <, > et les guillemets apparaissent bruts. La seule chose qui retient le payload est que l'attribut data-url est entouré de guillemets simples. Lorsqu'un guillemet simple est inséré dans l'entrée, cela suffit : l'attribut se ferme, la balise <form> se ferme, et le HTML écrit par l'attaquant remplace le formulaire d'authentification de la page.
L'action du formulaire est détournée. Aucun faux formulaire n'est dessiné ici — le formulaire propre de l'application est laissé vide et fermé, puis un nouveau <form> portant les mêmes classes CSS est ouvert immédiatement après. Comme les champs de nom d'utilisateur, de mot de passe et de code de vérification de la page proviennent tous du HTML d'origine de l'application, ils restent à l'intérieur de ce nouveau formulaire. L'utilisateur voit le vrai formulaire, remplit le vrai formulaire ; les informations saisies partent vers le serveur de l'attaquant. Il n'y a aucune différence visuellement perceptible.
Le point crucial : l'injection ne se produit pas sur une page quelconque, mais sur la page où l'utilisateur est déjà censé saisir son mot de passe. Dans une injection réfléchie ordinaire, l'attaquant doit convaincre la victime ; ici, c'est l'interface de l'application elle-même qui fait le travail de persuasion.
value d'un champ de formulaire masqué — Double décodage URLSur la page de recherche, les valeurs des paramètres GET sont écrites dans des champs de formulaire masqués. Un échappement est appliqué à ce stade — mais dans le mauvais ordre.
La même valeur q est utilisée dans trois contextes distincts au sein d'une même réponse, chacun ayant une profondeur de décodage différente :
| Contexte | Décodage | Statut |
|---|---|---|
Chaîne JS dans un bloc <script> | 1 fois | Sûr |
Champ de recherche principal <input value="…"> | 1 fois | Sûr |
Champs de formulaire masqués <input type='hidden' value="…"> | 2 fois | Vulnérable |
La séquence d'opérations est la suivante :
Entrée client : %2522
↓ analyse $_GET
Variable PHP : %22
↓ filtre d'entrée → ne voit aucun contenu malveillant, pas de guillemet présent
↓ htmlspecialchars → aucun caractère à échapper, aucune modification
↓ urldecode → %22 est décodé
Écrit sur la page : " ← guillemet brut, sortie de l'attribut
Alors que le filtre d'entrée et l'échappement opèrent sur la première couche de décodage, la sortie est alimentée par la seconde couche. La différence est clairement visible en comparant les versions à encodage simple et double du même payload :
| Envoyé | Réponse | Sortie du champ masqué |
|---|---|---|
q=foo%22… (encodage simple) | 302 Found | value="foo"…" — le filtre l'attrape |
q=foo%2522… (double encodage) | 200 OK | value="foo"><…>" — HTML brut |
La vulnérabilité n'est pas spécifique au paramètre q. Le bloc qui génère les champs masqués parcourt tous les paramètres GET de la requête ; elle a également été vérifiée sur tip et alan.
name d'un champ de formulaire masqué — Injection dans le nom du paramètreLe même bloc génère la structure suivante pour chaque paramètre GET :
<input type='hidden' name="<NOM DU PARAMÈTRE>" value="<VALEUR DU PARAMÈTRE>"/>
L'échappement n'est appliqué que du côté value. Il n'est jamais appliqué du côté name. À ce stade, le double encodage n'est même pas nécessaire — un simple encodage suffit, car il n'y a aucun échappement à contourner.
Un nom de paramètre inventé est écrit directement et brut dans l'attribut name, permettant de sortir de l'attribut. Comme le nom du paramètre est sous le contrôle de l'attaquant, il n'a pas besoin d'être un paramètre reconnu par l'application.
Les points n° 2 et n° 3 de ces trois points proviennent du même bloc de code, et ce bloc se répète dans six formulaires distincts : dilForm, adetForm, siralaForm, tkForm, ekForm, tmForm. Autrement dit, en une seule requête, l'injection se produit six fois.
Le caractère dynamique du bloc a été vérifié en comparant la sortie de deux requêtes :
Requête A : ?p=1&dil=0&alan=&tip=basit&gorunum=liste&q=…
Sortie A : name="p" · name="alan" · name="tip" · name="gorunum" · name="q"
Requête B : ?p=2&dil=0&devam=…
Sortie B : name="p" · name="devam"
Les champs générés ne proviennent pas d'une liste fixe, mais sont dérivés directement des paramètres de la requête. Par conséquent, le contenu écrit à la fois dans les attributs name et value est sous le contrôle de l'attaquant.
La seule chose dont l'attaquant a besoin est un lien que la victime ouvrira. Il n'a pas besoin de se connecter ni d'avoir un compte.
action du formulaire de connexion est redirigée vers l'attaquant. Vérifié.Comme la plateforme concernée héberge les identifiants et les données personnelles des membres de la bibliothèque, l'accès aux dossiers des membres est possible via les comptes compromis.