Threat Intelligence

CVE-2026-13182 : l'oracle de padding qui transforme un upload Telerik en webshell IIS

Admin CyberAfrik 08 September 2026 26 lectures
CVE-2026-13182 : l'oracle de padding qui transforme un upload Telerik en webshell IIS

Rapport CTI technique du 8 septembre 2026

Résumé exécutif

Tanto Security a publié le 7 septembre 2026 l'analyse complète et l'outil d'exploitation d'une chaîne de quatre vulnérabilités dans Telerik UI for ASP.NET AJAX, la suite de contrôles WebForms de Progress présente dans un grand nombre d'applications d'entreprise .NET. Le point de départ est un oracle de padding AES-CBC dans le contrôle RadAsyncUpload (CVE-2026-13182) : le serveur renvoie deux erreurs distinctes selon que le padding PKCS#7 est invalide ou que le JSON déchiffré est illisible. Cette différence suffit à déchiffrer puis à forger l'état chiffré du contrôle sans jamais connaître la clé. En bout de chaîne, une résolution de type .NET sans liste blanche (CVE-2026-13181) permet d'instancier le gadget System.Configuration.Install.AssemblyInstaller, dont le setter Path appelle Assembly.LoadFrom sur une DLL mixte téléversée quelques requêtes plus tôt. Le résultat est une exécution de code non authentifiée sous l'identité du pool d'applications IIS.

Progress a corrigé la chaîne le 8 juillet 2026 dans la version 2026.2.708 (2026 Q2 SP1) en abandonnant AES-CBC au profit d'AES-GCM, puis publié les CVE le 22 juillet. L'outil telerik-rau-exploit, écrit en Go et accompagné de deux payloads DLL en mode mixte, est disponible publiquement depuis le 7 septembre. Il a été validé par ses auteurs sur les builds 2026.1.225, 2026.1.421 et 2026.2.519. La chaîne n'est pas exploitable sur une installation par défaut : elle exige une page atteignable exposant un RadAsyncUpload dont le handler serveur FileUploaded lit la propriété UploadResult, ainsi qu'une clé Telerik.AsyncUpload.ConfigurationEncryptionKey explicitement configurée, réglage pourtant recommandé par la documentation de l'éditeur.

Chronologie

DateÉvénement
Mars 2026Tanto Security confirme la présence des deux oracles (handler et postback) sur la version 2026.1.225
21 avril 2026 (build 2026.1.421)Progress ajoute un catch-all dans GetConfiguration, un jeton CSRF et un UploadID lié à la session, sans CVE associée
22 mai 2026Rapport transmis à Progress Software via le canal de divulgation
25 mai 2026Accusé de réception de Progress
23 juin 2026Deux constats supplémentaires transmis
1er juillet 2026Progress fournit un build de préversion pour validation du correctif
3 juillet 2026Revue du build de préversion, dont une XXE non authentifiée dans RadLayoutBuilder
8 juillet 2026Publication de la version corrigée 2026.2.708 (2026 Q2 SP1)
22 juillet 2026Progress publie le bulletin et les CVE-2026-13181 à 13186 et 13190
7 septembre 2026Publication de l'analyse technique et de l'outil telerik-rau-exploit sur GitHub

Fiche vulnérabilité

CVE-2026-13182 | Sévérité haute (score exact non publié par le NVD au 8 septembre) | Telerik UI for ASP.NET AJAX, contrôle RadAsyncUpload

Progress la titre « RadAsyncUpload Client-State Decrypt-vs-Parse Oracle Vulnerability ». Les blobs _serializedConfiguration, _serializedConfigurationType et metaData sont chiffrés en AES-CBC sans MAC. Sur le chemin de déchiffrement, un padding PKCS#7 invalide remonte une CryptographicException produite par le wrapper CryptoExceptionThrower.ThrowIfFails. Un padding valide qui donne un JSON incohérent passe la couche crypto et échoue plus loin, dans un JavaScriptSerializer non enveloppé, sous forme d'InvalidOperationException. Deux exceptions distinguables sur le fil, donc un oracle de padding classique, exploitable octet par octet de droite à gauche.

Conditions d'exploitation : accès réseau à une page rendant un RadAsyncUpload, aucun compte requis. Le blob doit être authentique au départ, car le vecteur d'initialisation est dérivé du mot de passe avec un sel constant (58 84 91 25 10 34 29 68 60 88 44 51 1) et n'est jamais transmis avec le message, ce qui interdit de forger le premier bloc. PoC disponible : oui, outil Go telerik-rau-exploit publié le 7 septembre 2026. Patch : 2026.2.708, qui remplace AES-CBC par AES-GCM.

CVE-2026-13183 | Sévérité haute | RadAsyncUpload, variante temporelle

Même défaut, lecture différente. Quand customErrors est positionné sur On ou que les messages d'erreur sont normalisés, les deux chemins de code continuent de consommer des quantités de travail différentes : le mauvais padding échoue tôt, le bon padding suivi d'un JSON invalide échoue après le passage dans le sérialiseur. Justin Steven a exploité cet écart avec une attaque Timeless Timing (Van Goethem et al., USENIX Security 2020), en faisant courir deux requêtes dans le même paquet pour que l'ordre des réponses porte le signal, ce qui neutralise la gigue réseau. Le test a tenu depuis Melbourne contre une instance us-east-1 avec 221 ms de latence aller-retour. Conséquence opérationnelle directe : masquer le message d'erreur ne ferme pas l'oracle.

CVE-2026-13181 | CVSS 8.1 (haute) | RadAsyncUpload, résolution de type non contrôlée

Le getter FileUploadedEventArgs.UploadResult exécute Type.GetType(obj.FileType)FileType provient de AsyncUploadTypeName, champ de l'objet MetaData que l'oracle permet de forger. Aucune liste blanche, aucune contrainte de type de base, contrairement au chemin GetConfiguration qui, lui, appelle CheckWhitelistTypes. L'attaquant contrôle donc le type instancié et le JSON qui peuple ses propriétés (SerializedData), soit le schéma de désérialisation .NET exploitable au sens CWE-502. Condition d'exploitation supplémentaire, souvent oubliée : le sink ne se déclenche que si le code applicatif lit e.UploadResult dans son gestionnaire FileUploaded. Une page qui accepte le fichier sans jamais lire le résultat reste hors de portée de ce vecteur précis.

CVE-2026-13184 | Sévérité haute | RadAsyncUpload, clé HMAC de repli prévisible

Quand Telerik.Upload.ConfigurationHashKey est absent et qu'aucune machineKey explicite n'est définie, Telerik retombe sur une clé HMAC prévisible pour signer le blob interne TempTargetFolder. Un attaquant peut alors injecter son propre dossier temporaire au lieu de devoir le récupérer par oracle. Dans la démonstration de Tanto, la cible de laboratoire utilisait une clé personnalisée : l'outil bascule en mode lecture et récupère le chemin réel via le vecteur d'initialisation extrait du blob MetaData.

Le bulletin Progress du 22 juillet couvre également CVE-2026-13185, CVE-2026-13186 et CVE-2026-13190, qui touchent RadPersistenceManager et RadDockLayout. CWE de référence pour l'ensemble : CWE-326 (chiffrement de force inadéquate), CWE-502 (désérialisation de données non fiables), CWE-22 (traversée de chemin).

Diagramme de la chaîne d'attaque

Matrice de progression de la chaine Telerik RadAsyncUpload

Lecture des phases telle que documentée par Tanto Security dans l'exécution de telerik-rau-exploit contre un laboratoire en 2026.1.421.

Analyse technique

Étape 1 : trouver l'oracle, deux portes pour un même défaut

Telerik chiffre l'état serveur puis le confie au client, en partant du principe qu'un état chiffré ne peut être ni lu ni forgé. Deux chemins ramènent cet état au serveur. Le premier passe par le handler Telerik.Web.UI.WebResource.axd?type=rau et AsyncUploadHandler.GetConfiguration. Le second passe par un postback de page ordinaire : LoadPostData appelle PlayClientState, qui déserialise rau_ClientState avec un JavaScriptConverter maison, AsyncUploadClientStateConverter, dont la méthode Deserialize exécute en une seule ligne le déchiffrement puis le parsing du blob metaData.

Le correctif d'avril 2026 (2026.1.421) a enveloppé GetConfiguration dans un try/catch qui aplatit tout en une CryptographicException unique. Le chemin postback, lui, n'a pas été touché. Les deux exceptions continuent d'y remonter jusqu'à la couche ASP.NET, donc l'oracle survit au correctif intermédiaire.

Étape 2 : contourner le vecteur d'initialisation statique avec un bloc sacrificiel

Le CBC inversé permet de forger un bloc en modifiant le bloc de chiffré précédent, mais brouille au passage le texte clair de ce bloc précédent. En remontant de droite à gauche, on finit par buter sur le premier bloc, qui est XORé avec un vecteur d'initialisation dérivé du mot de passe et jamais transmis. Impossible donc de fabriquer un message complet à partir de rien.

La sortie tient à une observation de format. La configuration est du JSON encodé en UTF-16LE, soit huit caractères ASCII par bloc de seize octets. En coupant le chiffré à l'intérieur du littéral AllowedFileExtensions, assez long pour absorber un bloc entier entre ses guillemets, le bloc dont le clair n'est pas contrôlable produit une bouillie qui reste du contenu de chaîne valide. La clé JSON d'origine devient un nom bâtard du type AllowedFileq9%Kf2#z, refermé par une valeur jetable, puis l'attaquant réécrit sa propre clé AllowedFileExtensions avec la valeur dll. Le JavaScriptSerializer de .NET conserve la dernière occurrence d'une clé dupliquée, donc l'allowlist d'origine disparaît. Le bloc sacrificiel doit rester du UTF-16LE valide et ne contenir ni guillemet, ni antislash, ni caractère de contrôle : en cas d'échec, on insère un bloc aléatoire supplémentaire et on relance.

Étape 3 : neutraliser les garde-fous de 2026.1.421

Deux protections ajoutées en avril visent le chemin d'upload : un jeton CSRF vérifié par HMAC-SHA256 contre la session, et un UploadID qui doit correspondre au _pageGUID du rendu courant. Les deux vivent dans EnsureSetup, sur le handler. Les sondes de l'oracle passent par le postback, qui n'appelle jamais EnsureSetup, donc la phase de déchiffrement les ignore complètement.

Reste l'upload final, qui doit franchir les deux contrôles. Le champ CsrfToken est déclaré en cinquième position dans AsyncUploadConfiguration, donc avant AllowedFileExtensions dans l'ordre de sérialisation : il tombe dans le préfixe repris verbatim d'un blob authentique. Un simple rechargement de la page cible fournit un préfixe portant un jeton CSRF valide et le _pageGUID courant. Le bloc sacrificiel absorbe la jointure entre ce préfixe frais et le suffixe forgé.

Étape 4 : du gadget AssemblyInstaller au code natif

La DLL est téléversée en deux morceaux pour emprunter le chemin d'assemblage par chunks. Le serveur renvoie un blob MetaData chiffré, dont le bloc 0 permet de récupérer le vecteur d'initialisation, partagé par tous les blobs puisque dérivé du même mot de passe. De là, l'outil déchiffre le TempTargetFolder réel et reconstruit le nom de fichier temporaire à partir du _pageGUID et du nom de la DLL.

L'attaquant forge ensuite un MetaData dont AsyncUploadTypeName vaut System.Configuration.Install.AssemblyInstaller et dont SerializedData contient une propriété Path pointant vers la DLL plantée. Au postback, le getter UploadResult résout le type, appelle SerializationService.Deserialize, et le setter Path déclenche Assembly.LoadFrom. La requête se termine en HTTP 500 parce que AssemblyInstaller n'implémente pas IAsyncUploadResult, mais l'erreur de cast survient bien après l'exécution du code.

Étape 5 : les payloads en mode mixte

Une DLL C++/CLI compilée en image /clr est à la fois un assembly managé accepté par le CLR et un PE natif avec un vrai DllMain. Le chargeur Windows appelle DllMain(DLL_PROCESS_ATTACH) avant qu'une seule méthode managée ne tourne. Le code s'exécute sous le loader lock, ce qui interdit toute opération réentrante : dans les tests de Tanto, un simple GetFileAttributesW faisait tomber le worker avec un 0xe0434352.

Le payload write-webshell contourne la contrainte en lisant la ligne de commande du processus via GetCommandLineW, qui tape dans le PEB sans toucher au disque. IIS passe à w3wp.exe le chemin de son fichier de configuration, dont l'entrée physicalPath donne la racine du site. Le payload y dépose un .aspx auto-déchiffrant, dont la clé dérive du nom de fichier. Le payload inmemory-webshell renonce au disque et greffe un handler dans le pipeline de requêtes du worker : toute URL du site devient un shell dès qu'un en-tête HTTP secret est présent, au prix d'une persistance qui ne survit pas à un recyclage du pool.

Le CLR mettant en cache les assemblies par nom de manifeste, DllMain ne se déclenche qu'une fois par assembly et par processus. L'outil réécrit donc le nom du manifeste dans les octets de la DLL avant chaque envoi.

Indicateurs de compromission

Aucune campagne d'exploitation en production n'a été publiée à ce jour, il n'existe donc pas d'IOC réseau attribués. Les artefacts suivants sont ceux que produit l'outil public, tels que décrits par ses auteurs.

TypeValeur
Lignée de processusw3wp.exe engendrant cmd.exe
Fichier.aspx inconnu apparaissant dans la racine web, contenu chiffré sans chaîne de webshell reconnaissable
FichierDLL en mode mixte (PE natif portant un manifeste .NET) sous le dossier temporaire de RadAsyncUpload, typiquement App_Data\RadUploadTemp\<guid>_<nom>.dll.tmp
Chargement de modulew3wp.exe chargeant un assembly depuis App_Data
Volumétrie HTTPenviron 127 000 requêtes vers la page ou le handler WebResource.axd?type=rau pour une chaîne complète, à environ 30 par seconde dans le laboratoire de référence
Réponse serveurHTTP 500 sur get_UploadResult immédiatement après une séquence d'upload en deux chunks

MITRE ATT&CK

Aucune cartographie officielle n'accompagne la publication de Tanto Security. Le tableau ci-dessous est une lecture technique construite à partir des éléments confirmés dans l'analyse et dans le bulletin Progress.

TactiqueTechniqueIDJustification
ReconnaissanceActive Scanning: Vulnerability ScanningT1595.002Phases 1 et 2 de l'outil : sondage massif de l'oracle pour localiser le point de coupe et le blob TempTargetFolder
Accès initialExploit Public-Facing ApplicationT1190Chaîne non authentifiée contre un handler et une page exposés sur Internet
ExécutionCommand and Scripting Interpreter: Windows Command ShellT1059.003Les deux payloads exécutent les commandes via cmd.exe
ExécutionNative APIT1106DllMain(DLL_PROCESS_ATTACH) et Assembly.LoadFrom déclenchés par le setter du gadget
PersistanceServer Software Component: Web ShellT1505.003Dépôt d'un .aspx auto-déchiffrant dans la racine web
Évasion défensiveObfuscated Files or InformationT1027Webshell chiffré au repos, clé dérivée du nom de fichier, nom de manifeste réécrit à chaque envoi
Évasion défensiveReflective Code LoadingT1620Variante inmemory greffée dans le pipeline du worker, sans écriture disque

Remédiation : checklist opérationnelle

  1. Mettre à jour Telerik UI for ASP.NET AJAX vers 2026.2.708 (2026 Q2 SP1) ou plus récent. C'est la seule mesure qui ferme les deux oracles, puisque AES-GCM supprime l'étape de padding et rejette tout octet modifié avant le déchiffrement.
  2. Inventorier les applications embarquant Telerik.Web.UI.dll : la version se lit dans les références du projet ou directement sur l'assembly. Toute version comprise entre 2010.1.309 et 2026.2.519 est concernée pour RadAsyncUpload, entre 2013.1.220 et 2026.2.519 pour RadPersistenceManager et RadDockLayout.
  3. Si la mise à jour ne peut pas être immédiate, désactiver le handler d'upload quand il n'est pas utilisé, avec la clé Telerik.Web.DisableAsyncUploadHandler positionnée à true dans appSettings.
  4. Revoir l'usage de Telerik.AsyncUpload.ConfigurationEncryptionKey, Telerik.Upload.ConfigurationHashKey et Telerik.Web.UI.DialogParametersEncryptionKey. Sur les versions antérieures à 2026.2.708, Progress recommande de retirer ces clés pour retomber sur MachineKey.Unprotect en AES plus HMAC.
  5. Générer manuellement les clés de machine dans IIS avec la méthode de validation HMACSHA256 et décocher les deux cases de génération automatique au runtime, puis recycler le pool d'applications. Une génération automatique reproduit la faiblesse d'origine.
  6. Positionner customErrors sur RemoteOnly ou On. Cette mesure ne ferme pas l'oracle, la variante temporelle CVE-2026-13183 le traverse, mais elle augmente le coût d'exploitation.
  7. Auditer le code applicatif à la recherche des gestionnaires FileUploaded qui lisent e.UploadResult : ce sont les pages qui rendent le gadget CVE-2026-13181 atteignable.
  8. Déplacer le dossier temporaire d'upload hors de App_Data via Telerik.AsyncUpload.TemporaryFolder, et retirer le droit d'écriture du compte du pool d'applications sur la racine web.
  9. Déployer une règle d'alerte sur w3wp.exe engendrant cmd.exe, ainsi que sur tout chargement d'assembly par w3wp.exe depuis un chemin d'upload temporaire.
  10. Vérifier que RadPersistenceManager n'utilise pas CookieStateStorageProvider, que RadDockLayout.LayoutPersistenceRepositoryType n'est pas Cookies, et que StorageProviderKey n'est jamais dérivé de données de requête.

Sources

Tags : CVE-2026-13182CVE-2026-13181Telerik UI for ASP.NET AJAXRadAsyncUploadpadding oracleAES-CBCCWE-502Tanto Security
Partager cet article