{"id":240,"date":"2026-02-11T16:39:35","date_gmt":"2026-02-11T15:39:35","guid":{"rendered":"https:\/\/helloblog.io\/fr\/faille-critique-wpvivid-backup-upload-fichiers-arbitraires-cve-2026-1357\/"},"modified":"2026-02-11T16:39:35","modified_gmt":"2026-02-11T15:39:35","slug":"faille-critique-wpvivid-backup-upload-fichiers-arbitraires-cve-2026-1357","status":"publish","type":"post","link":"https:\/\/helloblog.io\/fr\/faille-critique-wpvivid-backup-upload-fichiers-arbitraires-cve-2026-1357\/","title":{"rendered":"Faille critique dans WPvivid Backup : un upload de fichiers arbitraires touche jusqu\u2019\u00e0 800 000 sites WordPress (CVE-2026-1357)"},"content":{"rendered":"\n<p>Le plugin <strong>WPvivid Backup &#038; Migration<\/strong> (slug WordPress.org : <code>wpvivid-backuprestore<\/code>) est au c\u0153ur d\u2019un avis de s\u00e9curit\u00e9 publi\u00e9 par Wordfence : une faille <strong>critique<\/strong> permet, dans certaines conditions, \u00e0 un attaquant <strong>non authentifi\u00e9<\/strong> d\u2019uploader un fichier arbitraire (typiquement un fichier PHP) et d\u2019aller jusqu\u2019\u00e0 l\u2019<strong>ex\u00e9cution de code \u00e0 distance (RCE)<\/strong> &#8211; autrement dit, la prise de contr\u00f4le compl\u00e8te du site dans les sc\u00e9narios classiques (webshell, backdoor, etc.).<\/p>\n\n\n\n<p>Point important \u00e0 garder en t\u00eate : l\u2019impact \u00ab vraiment critique \u00bb d\u00e9pend d\u2019une fonctionnalit\u00e9 sp\u00e9cifique du plugin, <strong>d\u00e9sactiv\u00e9e par d\u00e9faut<\/strong>. La vuln\u00e9rabilit\u00e9 concerne surtout les sites sur lesquels une <strong>cl\u00e9 temporaire a \u00e9t\u00e9 g\u00e9n\u00e9r\u00e9e<\/strong> dans les r\u00e9glages de WPvivid afin d\u2019autoriser un autre site \u00e0 <strong>envoyer une sauvegarde vers ce site<\/strong>. D\u2019apr\u00e8s l\u2019analyse, l\u2019expiration de cette cl\u00e9 ne peut pas d\u00e9passer <strong>24 heures<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">R\u00e9sum\u00e9 de la vuln\u00e9rabilit\u00e9 (Wordfence Intelligence)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Intitul\u00e9 : <strong>Migration, Backup, Staging <= 0.9.123 - Unauthenticated Arbitrary File Upload<\/strong><\/li>\n\n\n<li>S\u00e9v\u00e9rit\u00e9 : <strong>CVSS 9.8 (Critical)<\/strong><\/li>\n\n\n<li>CVE : <strong>CVE-2026-1357<\/strong><\/li>\n\n\n<li>Versions affect\u00e9es : <strong><= 0.9.123<\/strong><\/li>\n\n\n<li>Version corrig\u00e9e : <strong>0.9.124<\/strong><\/li>\n\n\n<li>Logiciel affect\u00e9 : <strong>Migration, Backup, Staging &#8211; WPvivid Backup &#038; Migration<\/strong><\/li>\n\n\n<li>Slug : <strong>wpvivid-backuprestore<\/strong><\/li>\n\n\n<li>Chercheur : <strong>Lucas Montes (NiRoX)<\/strong><\/li>\n\n\n<li>Prime (bug bounty) : <strong>2 145,00 $<\/strong><\/li>\n\n<\/ul>\n\n\n\n<p>Le vecteur d\u00e9crit par Wordfence s\u2019appuie sur une cha\u00eene de probl\u00e8mes : <strong>gestion d\u2019erreur insuffisante lors du d\u00e9chiffrement RSA<\/strong>, combin\u00e9e \u00e0 une <strong>absence de sanitation de chemin<\/strong> (path sanitization) lors de l\u2019\u00e9criture des fichiers upload\u00e9s. R\u00e9sultat : un attaquant peut parvenir \u00e0 d\u00e9poser un fichier PHP dans un r\u00e9pertoire web accessible, puis l\u2019appeler pour d\u00e9clencher la RCE. L\u2019exploitation est associ\u00e9e au param\u00e8tre <code>wpvivid_action=send_to_site<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pourquoi cette faille peut mener \u00e0 une compromission compl\u00e8te<\/h2>\n\n\n\n<p>Une vuln\u00e9rabilit\u00e9 d\u2019<strong>arbitrary file upload<\/strong> est presque toujours synonyme de sc\u00e9nario catastrophe d\u00e8s lors que l\u2019attaquant arrive \u00e0 \u00e9crire un fichier ex\u00e9cutable (comme <code>*.php<\/code>) dans une zone servie par le serveur web. Dans ce cas, une fois le fichier en place, il suffit de le requ\u00eater pour ex\u00e9cuter du code c\u00f4t\u00e9 serveur et encha\u00eener sur : cr\u00e9ation d\u2019un compte admin, injection de portes d\u00e9rob\u00e9es, exfiltration de secrets, pivot vers d\u2019autres syst\u00e8mes, etc.<\/p>\n\n\n\n<p>Ici, Wordfence insiste n\u00e9anmoins sur le p\u00e9rim\u00e8tre : l\u2019exploitation critique cible surtout les installations o\u00f9 l\u2019option permettant de <strong>recevoir une sauvegarde d\u2019un autre site<\/strong> a \u00e9t\u00e9 activ\u00e9e via une <strong>cl\u00e9 g\u00e9n\u00e9r\u00e9e<\/strong>. Cette fonctionnalit\u00e9 est <strong>off<\/strong> par d\u00e9faut et la cl\u00e9 est <strong>courte dur\u00e9e<\/strong> (expiration max 24 h).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Analyse technique : ce qui se passe dans le code<\/h2>\n\n\n\n<p>WPvivid propose plusieurs fonctionnalit\u00e9s de sauvegarde\/restauration. Parmi elles : la possibilit\u00e9 de <strong>recevoir un backup depuis un autre site<\/strong>. Le flux de r\u00e9ception passe par la m\u00e9thode <code>send_to_site()<\/code> de la classe <code>WPvivid_Send_to_site<\/code>, qui traite un contenu re\u00e7u via <code>$_POST['wpvivid_content']<\/code> et utilise une cl\u00e9 (token) stock\u00e9e dans l\u2019option <code>wpvivid_api_token<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public function send_to_site()\n{\n    include_once WPVIVID_PLUGIN_DIR . '\/includes\/class-wpvivid-crypt.php';\n    $test_log = new WPvivid_Log();\n    $test_log-&gt;CreateLogFile('test_backup','no_folder','transfer');\n    $test_log-&gt;WriteLog('test upload.','notice');\n\n    try\n    {\n        if (isset($_POST['wpvivid_content']))\n        {\n            global $wpvivid_plugin;\n            $wpvivid_plugin-&gt;wpvivid_log = new WPvivid_Log();\n\n            $default = array();\n            $option = get_option('wpvivid_api_token', $default);\n            if (empty($option))\n            {\n                die();\n            }\n\n            if ($option['expires'] != 0 &amp;&amp; $option['expires'] &lt; time())\n            {\n                die();\n            }\n\n            $crypt = new WPvivid_crypt(base64_decode($option['private_key']));\n            $body  = base64_decode($_POST['wpvivid_content']);\n            $data  = $crypt-&gt;decrypt_message($body);\n        }\n    }\n    catch (Exception $e)\n    {\n        \/\/ ...\n    }\n}\n\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#F97583\">public<\/span><span style=\"color:#F97583\"> function<\/span><span style=\"color:#B392F0\"> send_to_site<\/span><span style=\"color:#E1E4E8\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    include_once<\/span><span style=\"color:#79B8FF\"> WPVIVID_PLUGIN_DIR<\/span><span style=\"color:#F97583\"> .<\/span><span style=\"color:#9ECBFF\"> '\/includes\/class-wpvivid-crypt.php'<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $test_log <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> WPvivid_Log<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $test_log<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">CreateLogFile<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">'test_backup'<\/span><span style=\"color:#E1E4E8\">,<\/span><span style=\"color:#9ECBFF\">'no_folder'<\/span><span style=\"color:#E1E4E8\">,<\/span><span style=\"color:#9ECBFF\">'transfer'<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $test_log<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">WriteLog<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">'test upload.'<\/span><span style=\"color:#E1E4E8\">,<\/span><span style=\"color:#9ECBFF\">'notice'<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    try<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">        if<\/span><span style=\"color:#E1E4E8\"> (<\/span><span style=\"color:#79B8FF\">isset<\/span><span style=\"color:#E1E4E8\">($_POST[<\/span><span style=\"color:#9ECBFF\">'wpvivid_content'<\/span><span style=\"color:#E1E4E8\">]))<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">            global<\/span><span style=\"color:#E1E4E8\"> $wpvivid_plugin;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $wpvivid_plugin<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#E1E4E8\">wpvivid_log <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> WPvivid_Log<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $default <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> array<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $option <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#B392F0\"> get_option<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">'wpvivid_api_token'<\/span><span style=\"color:#E1E4E8\">, $default);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">            if<\/span><span style=\"color:#E1E4E8\"> (<\/span><span style=\"color:#79B8FF\">empty<\/span><span style=\"color:#E1E4E8\">($option))<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">                die<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">            if<\/span><span style=\"color:#E1E4E8\"> ($option[<\/span><span style=\"color:#9ECBFF\">'expires'<\/span><span style=\"color:#E1E4E8\">] <\/span><span style=\"color:#F97583\">!=<\/span><span style=\"color:#79B8FF\"> 0<\/span><span style=\"color:#F97583\"> &#x26;&#x26;<\/span><span style=\"color:#E1E4E8\"> $option[<\/span><span style=\"color:#9ECBFF\">'expires'<\/span><span style=\"color:#E1E4E8\">] <\/span><span style=\"color:#F97583\">&#x3C;<\/span><span style=\"color:#79B8FF\"> time<\/span><span style=\"color:#E1E4E8\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">                die<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $crypt <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> WPvivid_crypt<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">base64_decode<\/span><span style=\"color:#E1E4E8\">($option[<\/span><span style=\"color:#9ECBFF\">'private_key'<\/span><span style=\"color:#E1E4E8\">]));<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $body  <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> base64_decode<\/span><span style=\"color:#E1E4E8\">($_POST[<\/span><span style=\"color:#9ECBFF\">'wpvivid_content'<\/span><span style=\"color:#E1E4E8\">]);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">            $data  <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#E1E4E8\"> $crypt<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">decrypt_message<\/span><span style=\"color:#E1E4E8\">($body);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    catch<\/span><span style=\"color:#E1E4E8\"> (<\/span><span style=\"color:#79B8FF\">Exception<\/span><span style=\"color:#E1E4E8\"> $e)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#6A737D\">        \/\/ ...<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Le point de rupture se situe dans la logique de chiffrement\/d\u00e9chiffrement : le plugin d\u00e9chiffre une cl\u00e9 de session (RSA), puis l\u2019utilise comme cl\u00e9 pour un chiffrement sym\u00e9trique (Rijndael\/AES via phpseclib). Probl\u00e8me : si le d\u00e9chiffrement RSA \u00e9choue, la valeur retourn\u00e9e peut \u00eatre <code>false<\/code>\u2026 et ce <code>false<\/code> est ensuite pass\u00e9 tel quel \u00e0 l\u2019initialisation du chiffrement sym\u00e9trique.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public function decrypt_message($message)\n{\n    $len = substr($message, 0, 3);\n    $len = hexdec($len);\n\n    $key = substr($message, 3, $len);\n\n    $cipherlen = substr($message, ($len + 3), 16);\n    $cipherlen = hexdec($cipherlen);\n\n    $data = substr($message, ($len + 19), $cipherlen);\n\n    $rsa = new Crypt_RSA();\n    $rsa-&gt;loadKey($this-&gt;public_key);\n    $key = $rsa-&gt;decrypt($key);\n\n    $rij = new Crypt_Rijndael();\n    $rij-&gt;setKey($key);\n\n    return $rij-&gt;decrypt($data);\n}\n\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#F97583\">public<\/span><span style=\"color:#F97583\"> function<\/span><span style=\"color:#B392F0\"> decrypt_message<\/span><span style=\"color:#E1E4E8\">($message)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $len <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, <\/span><span style=\"color:#79B8FF\">0<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#79B8FF\">3<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $len <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> hexdec<\/span><span style=\"color:#E1E4E8\">($len);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $key <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, <\/span><span style=\"color:#79B8FF\">3<\/span><span style=\"color:#E1E4E8\">, $len);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $cipherlen <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, ($len <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#79B8FF\"> 3<\/span><span style=\"color:#E1E4E8\">), <\/span><span style=\"color:#79B8FF\">16<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $cipherlen <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> hexdec<\/span><span style=\"color:#E1E4E8\">($cipherlen);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $data <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, ($len <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#79B8FF\"> 19<\/span><span style=\"color:#E1E4E8\">), $cipherlen);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rsa <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> Crypt_RSA<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rsa<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">loadKey<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">$this<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#E1E4E8\">public_key);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $key <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#E1E4E8\"> $rsa<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">decrypt<\/span><span style=\"color:#E1E4E8\">($key);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rij <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> Crypt_Rijndael<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rij<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">setKey<\/span><span style=\"color:#E1E4E8\">($key);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    return<\/span><span style=\"color:#E1E4E8\"> $rij<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">decrypt<\/span><span style=\"color:#E1E4E8\">($data);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Selon Wordfence, quand <code>openssl_private_decrypt()<\/code> \u00e9choue (dans la cha\u00eene de d\u00e9chiffrement), l\u2019ex\u00e9cution ne s\u2019arr\u00eate pas correctement et le <code>false<\/code> se propage jusqu\u2019\u00e0 l\u2019initialisation c\u00f4t\u00e9 phpseclib. La librairie traiterait alors ce <code>false<\/code> comme une cha\u00eene de <strong>null bytes<\/strong>. Cela rend la cl\u00e9 sym\u00e9trique <strong>pr\u00e9visible<\/strong>, et permet \u00e0 un attaquant de construire un payload chiffr\u00e9 qui sera correctement d\u00e9chiffr\u00e9 c\u00f4t\u00e9 serveur.<\/p>\n\n\n\n<p>Deuxi\u00e8me probl\u00e8me : en plus de rendre le chiffrement contournable, le plugin acceptait des noms de fichiers issus du payload d\u00e9chiffr\u00e9 <strong>sans sanitation suffisante<\/strong>, ouvrant la voie \u00e0 la <strong>travers\u00e9e de r\u00e9pertoires<\/strong> (directory traversal) pour sortir du r\u00e9pertoire de sauvegarde suppos\u00e9 prot\u00e9g\u00e9. Combin\u00e9 \u00e0 l\u2019absence de contr\u00f4le strict sur le type\/extension, cela permet de d\u00e9poser un fichier PHP en r\u00e9pertoire public et de d\u00e9clencher une RCE.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Correctif : ce qui a \u00e9t\u00e9 chang\u00e9 dans la version 0.9.124<\/h2>\n\n\n\n<p>Le correctif livr\u00e9 par l\u2019\u00e9diteur se joue \u00e0 deux endroits : (1) arr\u00eater le processus si la cl\u00e9 d\u00e9chiffr\u00e9e est invalide, (2) restreindre les extensions de fichiers accept\u00e9es lors de la r\u00e9ception d\u2019un backup.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1) V\u00e9rification explicite de la cl\u00e9 d\u00e9chiffr\u00e9e<\/h3>\n\n\n\n<p>Le patch ajoute un contr\u00f4le sur <code>$key<\/code> dans <code>decrypt_message()<\/code> : si la cl\u00e9 est <code>false<\/code> ou vide, la fonction retourne <code>false<\/code> (au lieu de continuer avec une cl\u00e9 implicite).<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public function decrypt_message($message)\n{\n    $len = substr($message, 0, 3);\n    $len = hexdec($len);\n\n    $key = substr($message, 3, $len);\n\n    $cipherlen = substr($message, ($len + 3), 16);\n    $cipherlen = hexdec($cipherlen);\n\n    $data = substr($message, ($len + 19), $cipherlen);\n\n    $rsa = new Crypt_RSA();\n    $rsa-&gt;loadKey($this-&gt;public_key);\n    $key = $rsa-&gt;decrypt($key);\n\n    if ($key === false || empty($key))\n    {\n        return false;\n    }\n\n    $rij = new Crypt_Rijndael();\n    $rij-&gt;setKey($key);\n\n    return $rij-&gt;decrypt($data);\n}\n\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#F97583\">public<\/span><span style=\"color:#F97583\"> function<\/span><span style=\"color:#B392F0\"> decrypt_message<\/span><span style=\"color:#E1E4E8\">($message)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $len <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, <\/span><span style=\"color:#79B8FF\">0<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#79B8FF\">3<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $len <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> hexdec<\/span><span style=\"color:#E1E4E8\">($len);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $key <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, <\/span><span style=\"color:#79B8FF\">3<\/span><span style=\"color:#E1E4E8\">, $len);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $cipherlen <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, ($len <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#79B8FF\"> 3<\/span><span style=\"color:#E1E4E8\">), <\/span><span style=\"color:#79B8FF\">16<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $cipherlen <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> hexdec<\/span><span style=\"color:#E1E4E8\">($cipherlen);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $data <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> substr<\/span><span style=\"color:#E1E4E8\">($message, ($len <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#79B8FF\"> 19<\/span><span style=\"color:#E1E4E8\">), $cipherlen);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rsa <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> Crypt_RSA<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rsa<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">loadKey<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">$this<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#E1E4E8\">public_key);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $key <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#E1E4E8\"> $rsa<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">decrypt<\/span><span style=\"color:#E1E4E8\">($key);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    if<\/span><span style=\"color:#E1E4E8\"> ($key <\/span><span style=\"color:#F97583\">===<\/span><span style=\"color:#79B8FF\"> false<\/span><span style=\"color:#F97583\"> ||<\/span><span style=\"color:#79B8FF\"> empty<\/span><span style=\"color:#E1E4E8\">($key))<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">        return<\/span><span style=\"color:#79B8FF\"> false<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rij <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#F97583\"> new<\/span><span style=\"color:#79B8FF\"> Crypt_Rijndael<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $rij<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">setKey<\/span><span style=\"color:#E1E4E8\">($key);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    return<\/span><span style=\"color:#E1E4E8\"> $rij<\/span><span style=\"color:#F97583\">-><\/span><span style=\"color:#B392F0\">decrypt<\/span><span style=\"color:#E1E4E8\">($data);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">2) Filtrage des extensions c\u00f4t\u00e9 r\u00e9ception (send_to_site)<\/h3>\n\n\n\n<p>Le fournisseur a \u00e9galement ajout\u00e9 un contr\u00f4le d\u2019extension et une normalisation du nom (<code>basename<\/code> + regex de nettoyage). L\u2019upload est limit\u00e9 aux formats de sauvegarde list\u00e9s : <code>zip<\/code>, <code>gz<\/code>, <code>tar<\/code>, <code>sql<\/code>. En cas d\u2019extension invalide, le plugin renvoie une erreur JSON et stoppe l\u2019ex\u00e9cution.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>$safe_name = basename($params['name']);\n$safe_name = preg_replace('\/[^a-zA-Z0-9._-]\/', '', $safe_name);\n\n$allowed_extensions = array('zip', 'gz', 'tar', 'sql');\n$file_ext = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));\n\nif (!in_array($file_ext, $allowed_extensions, true))\n{\n    $ret['result'] = WPVIVID_FAILED;\n    $ret['error']  = 'Invalid file type - only backup files allowed.';\n    echo wp_json_encode($ret);\n    die();\n}\n\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#E1E4E8\">$safe_name <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> basename<\/span><span style=\"color:#E1E4E8\">($params[<\/span><span style=\"color:#9ECBFF\">'name'<\/span><span style=\"color:#E1E4E8\">]);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">$safe_name <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> preg_replace<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">'\/<\/span><span style=\"color:#DBEDFF\">[^a-zA-Z0-9._-]<\/span><span style=\"color:#9ECBFF\">\/'<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">''<\/span><span style=\"color:#E1E4E8\">, $safe_name);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">$allowed_extensions <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> array<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">'zip'<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">'gz'<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">'tar'<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">'sql'<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">$file_ext <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> strtolower<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">pathinfo<\/span><span style=\"color:#E1E4E8\">($safe_name, <\/span><span style=\"color:#79B8FF\">PATHINFO_EXTENSION<\/span><span style=\"color:#E1E4E8\">));<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">if<\/span><span style=\"color:#E1E4E8\"> (<\/span><span style=\"color:#F97583\">!<\/span><span style=\"color:#79B8FF\">in_array<\/span><span style=\"color:#E1E4E8\">($file_ext, $allowed_extensions, <\/span><span style=\"color:#79B8FF\">true<\/span><span style=\"color:#E1E4E8\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $ret[<\/span><span style=\"color:#9ECBFF\">'result'<\/span><span style=\"color:#E1E4E8\">] <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> WPVIVID_FAILED<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    $ret[<\/span><span style=\"color:#9ECBFF\">'error'<\/span><span style=\"color:#E1E4E8\">]  <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#9ECBFF\"> 'Invalid file type - only backup files allowed.'<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#79B8FF\">    echo<\/span><span style=\"color:#B392F0\"> wp_json_encode<\/span><span style=\"color:#E1E4E8\">($ret);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">    die<\/span><span style=\"color:#E1E4E8\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Suis-je concern\u00e9 ? Le sc\u00e9nario d\u2019exploitation \u00e0 conna\u00eetre<\/h2>\n\n\n\n<p>D\u2019apr\u00e8s Wordfence, la faille est particuli\u00e8rement dangereuse pour les sites ayant <strong>g\u00e9n\u00e9r\u00e9 une cl\u00e9<\/strong> dans les r\u00e9glages de WPvivid afin d\u2019autoriser la fonctionnalit\u00e9 \u00ab un autre site peut envoyer un backup vers ce site \u00bb. Comme cette fonctionnalit\u00e9 est <strong>d\u00e9sactiv\u00e9e par d\u00e9faut<\/strong>, tout le monde n\u2019est pas expos\u00e9 de la m\u00eame mani\u00e8re.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Si tu utilises WPvivid mais n\u2019as <strong>jamais g\u00e9n\u00e9r\u00e9 de cl\u00e9<\/strong> pour la r\u00e9ception de sauvegardes, le sc\u00e9nario critique d\u00e9crit est moins probable (fonctionnalit\u00e9 non activ\u00e9e).<\/li>\n\n\n<li>Si tu as g\u00e9n\u00e9r\u00e9 une cl\u00e9 (m\u00eame temporairement), consid\u00e8re que ton site a potentiellement \u00e9t\u00e9 dans une fen\u00eatre d\u2019exposition, sachant que l\u2019expiration maximale annonc\u00e9e est de <strong>24 heures<\/strong>.<\/li>\n\n\n<li>Dans tous les cas : la recommandation pratique reste la m\u00eame &#8211; <strong>mettre \u00e0 jour<\/strong> vers la version corrig\u00e9e.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Mesures recommand\u00e9es : mise \u00e0 jour et protections c\u00f4t\u00e9 pare-feu<\/h2>\n\n\n\n<p>La correction est disponible dans <strong>WPvivid Backup 0.9.124<\/strong> (version indiqu\u00e9e comme patch\u00e9e au moment de la publication de l\u2019avis Wordfence). Si ton site est en <strong>0.9.123 ou ant\u00e9rieur<\/strong>, l\u2019action prioritaire est la mise \u00e0 jour.<\/p>\n\n\n\n<div class=\"wp-block-group callout callout-warning is-style-warning is-layout-flow wp-block-group-is-layout-flow\" style=\"border-width:1px;border-radius:8px;padding-top:1rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem\">\n\n<h4 class=\"wp-block-heading callout-title\">Action imm\u00e9diate<\/h4>\n\n\n<p>Mets \u00e0 jour <strong>WPvivid Backup &#038; Migration<\/strong> vers <strong>0.9.124<\/strong> (ou toute version ult\u00e9rieure) d\u00e8s que possible si tu as le plugin install\u00e9.<\/p>\n\n<\/div>\n\n\n\n<p>C\u00f4t\u00e9 protection en d\u00e9fense, Wordfence indique aussi avoir d\u00e9ploy\u00e9 une r\u00e8gle de firewall (pare-feu applicatif) :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Les utilisateurs <strong>Wordfence Premium<\/strong>, <strong>Wordfence Care<\/strong> et <strong>Wordfence Response<\/strong> ont re\u00e7u une r\u00e8gle de protection le <strong>22 janvier 2026<\/strong>.<\/li>\n\n\n<li>Les sites utilisant <strong>Wordfence Free<\/strong> doivent recevoir la m\u00eame protection <strong>30 jours plus tard<\/strong>, soit le <strong>21 f\u00e9vrier 2026<\/strong>.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Chronologie de divulgation (Disclosure timeline)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>12 janvier 2026<\/strong> &#8211; Soumission de la vuln\u00e9rabilit\u00e9 Arbitrary File Upload dans WPvivid Backup via le <strong>Wordfence Bug Bounty Program<\/strong>.<\/li>\n\n\n<li><strong>22 janvier 2026<\/strong> &#8211; Validation du rapport, confirmation du proof-of-concept. Premier contact avec l\u2019\u00e9diteur, invitation \u00e0 utiliser le <strong>Wordfence Vulnerability Management Portal<\/strong> : https:\/\/www.wordfence.com\/threat-intel\/vendor\/vulnerability-management-portal\/<\/li>\n\n\n<li><strong>22 janvier 2026<\/strong> &#8211; D\u00e9ploiement d\u2019une r\u00e8gle firewall pour les clients <strong>Wordfence Premium<\/strong>, <strong>Care<\/strong> et <strong>Response<\/strong>.<\/li>\n\n\n<li><strong>23 janvier 2026<\/strong> &#8211; R\u00e9ponse de l\u2019\u00e9diteur (choix de poursuivre par e-mail).<\/li>\n\n\n<li><strong>23 janvier 2026<\/strong> &#8211; Envoi des d\u00e9tails complets de divulgation ; accus\u00e9 de r\u00e9ception et d\u00e9marrage du correctif c\u00f4t\u00e9 \u00e9diteur.<\/li>\n\n\n<li><strong>28 janvier 2026<\/strong> &#8211; Publication de la version corrig\u00e9e <strong>0.9.124<\/strong>.<\/li>\n\n\n<li><strong>21 f\u00e9vrier 2026<\/strong> &#8211; Date annonc\u00e9e pour la protection \u00e9quivalente c\u00f4t\u00e9 <strong>Wordfence Free<\/strong>.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u00c0 propos de la d\u00e9couverte (bug bounty)<\/h2>\n\n\n\n<p>Wordfence cr\u00e9dite <strong>Lucas Montes (NiRoX)<\/strong> pour la d\u00e9couverte et le signalement responsable via son <strong>Bug Bounty Program<\/strong> : https:\/\/www.wordfence.com\/threat-intel\/bug-bounty-program\/ . Selon l\u2019avis, le report est arriv\u00e9 <strong>cinq jours apr\u00e8s l\u2019introduction<\/strong> de la vuln\u00e9rabilit\u00e9, et une prime de <strong>2 145,00 $<\/strong> a \u00e9t\u00e9 vers\u00e9e.<\/p>\n\n\n\n<p>\u00c0 noter \u00e9galement : Wordfence rappelle que son programme de bug bounty est ouvert aux plugins et th\u00e8mes WordPress \u00ab sans co\u00fbt pour les \u00e9diteurs \u00bb, et que les chercheurs peuvent gagner jusqu\u2019\u00e0 <strong>31 200 $ par vuln\u00e9rabilit\u00e9<\/strong> (selon le p\u00e9rim\u00e8tre et la s\u00e9v\u00e9rit\u00e9) : https:\/\/www.wordfence.com\/threat-intel\/bug-bounty-program\/ .<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Cette vuln\u00e9rabilit\u00e9 <strong>CVE-2026-1357<\/strong> dans WPvivid Backup illustre un classique : une combinaison de gestion d\u2019erreur cryptographique, de validation insuffisante et de contr\u00f4le trop permissif sur l\u2019\u00e9criture de fichiers peut suffire \u00e0 ouvrir la porte \u00e0 une <strong>RCE<\/strong>. M\u00eame si le sc\u00e9nario critique d\u00e9pend d\u2019une fonctionnalit\u00e9 optionnelle (r\u00e9ception de backups via cl\u00e9), la bonne hygi\u00e8ne est simple : si WPvivid est pr\u00e9sent, <strong>passe en 0.9.124<\/strong> (ou plus) imm\u00e9diatement, et v\u00e9rifie tes couches de d\u00e9fense (pare-feu applicatif, durcissement des permissions, surveillance des fichiers ajout\u00e9s).<\/p>\n\n\n<div class=\"references-section\">\n                <h2>R\u00e9f\u00e9rences \/ Sources<\/h2>\n                <ul class=\"references-list\"><li><a href=\"https:\/\/www.wordfence.com\/blog\/2026\/02\/800000-wordpress-sites-affected-by-arbitrary-file-upload-vulnerability-in-wpvivid-backup-wordpress-plugin\/\" target=\"_blank\" rel=\"noopener noreferrer\">800,000 WordPress Sites Affected by Arbitrary File Upload Vulnerability in WPvivid Backup WordPress Plugin<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/wpvivid-backuprestore\/migration-backup-staging-09123-unauthenticated-arbitrary-file-upload\" target=\"_blank\" rel=\"noopener noreferrer\">Migration, Backup, Staging &lt;= 0.9.123 &#8212; Unauthenticated Arbitrary File Upload<\/a><\/li><li><a href=\"https:\/\/www.cve.org\/CVERecord?id=CVE-2026-1357\" target=\"_blank\" rel=\"noopener noreferrer\">CVE-2026-1357<\/a><\/li><li><a href=\"https:\/\/wordpress.org\/plugins\/wpvivid-backuprestore\/\" target=\"_blank\" rel=\"noopener noreferrer\">WPvivid Backup &amp; Migration (wpvivid-backuprestore)<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/bug-bounty-program\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence Bug Bounty Program<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vendor\/vulnerability-management-portal\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence Vulnerability Management Portal<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/products\/wordfence-premium\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence Premium<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/products\/wordfence-care\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence Care<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/products\/wordfence-response\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence Response<\/a><\/li><\/ul>\n            <\/div>","protected":false},"excerpt":{"rendered":"<p>Une vuln\u00e9rabilit\u00e9 d\u2019upload de fichiers arbitraires dans WPvivid Backup peut mener \u00e0 une ex\u00e9cution de code \u00e0 distance, mais seulement dans un sc\u00e9nario pr\u00e9cis : si une cl\u00e9 \u201csend to site\u201d a \u00e9t\u00e9 g\u00e9n\u00e9r\u00e9e dans les r\u00e9glages du plugin.<\/p>\n","protected":false},"author":12,"featured_media":239,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[110,109,11,13,10],"class_list":["post-240","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-securite","tag-cve","tag-plugin","tag-securite","tag-vulnerabilite","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/posts\/240","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/comments?post=240"}],"version-history":[{"count":0,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/posts\/240\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/media\/239"}],"wp:attachment":[{"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/media?parent=240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/categories?post=240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/helloblog.io\/fr\/wp-json\/wp\/v2\/tags?post=240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}