
S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Générateur de contenu et d'images <= 1.7.7 - Authenticated (Editor+) Arbitrary File Upload
Le plugin WordPress S2B AI Assistant (versions 2.47 et antérieures) contient une vulnérabilité de téléchargement arbitraire de fichier qui permet aux utilisateurs WordPress authentifiés ayant le rôle d'Éditeur ou supérieur de télécharger des fichiers PHP malveillants sur le serveur, ce qui peut conduire à une exécution de code à distance.
Un POC CVE-2025-12973.py est fourni pour démontrer un attaquant distant téléchargeant shell.php et exécutant du code à distance :
python3 ./CVE-2025-12973.py http://techcorp.cc editor $PASSWORD
[+] Target: http://techcorp.cc
[+] Username: editor
[+] Nonce obtained: a15be47119
[+] File uploaded successfully!
[+] Shell URL: http://techcorp.cc/wp-content/uploads/2025/11/shell.php
[+] Command output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
La vulnérabilité se trouve dans le fichier Utils.php au niveau de la méthode storeFile(), qui est appelée par le point de terminaison /wp-admin/admin-post.php avec l'action s2b_store_chatbot_upload. La fonctionnalité de téléchargement utilise une liste blanche personnalisée d'extensions de fichiers qui autorise explicitement des types de fichiers dangereux, y compris les fichiers PHP. Cela permet aux utilisateurs WordPress authentifiés ayant le rôle d'Éditeur ou supérieur de télécharger des fichiers arbitraires, y compris des fichiers PHP pouvant être exécutés sur le serveur.
Source : Entrée utilisateur provenant de $_FILES['s2baia_chatbot_config_database'] (ligne 300)
Puits : $wp_filesystem->put_contents($outfile, $file_content, FS_CHMOD_FILE) (ligne 344)
La vulnérabilité se produit parce que :
AdminChatBotController.php.processAssistantUpload() gère la demande de téléchargement (ligne 1103).$_FILES['s2baia_chatbot_config_database'] sont directement traitées sans validation appropriée.checkAllowedFilesearchExtensions() (ligne 320), qui autorise explicitement l'extension .php (ligne 366).wp-content/uploads/YYYY/MM/.Fichier : lib/helpers/Utils.php
Lignes : 289-348
public static function storeFile($targetDir) {
global $wp_filesystem;
// Initialize WP_Filesystem
if (!function_exists('request_filesystem_credentials')) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
if (!WP_Filesystem()) {
return '';
}
if (!isset($_FILES) || !is_array($_FILES) || !isset($_FILES['s2baia_chatbot_config_database'])) { // SOURCE: User input ([line 300](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L300))
return '';
}
if (!isset($_FILES['s2baia_chatbot_config_database']['error']) || !isset($_FILES['s2baia_chatbot_config_database']['name']) || !isset($_FILES['s2baia_chatbot_config_database']['size']) || !isset($_FILES['s2baia_chatbot_config_database']['tmp_name'])) {
return '';
}
$chunk = isset($_REQUEST["chunk"]) ? (int) $_REQUEST["chunk"] : 0;
$name = sanitize_file_name($_FILES['s2baia_chatbot_config_database']['name']);
if (strlen($name) == 0) {
return '';
}
$finfo = pathinfo($name);
if (is_array($finfo)) {
$fname = sanitize_file_name($finfo['filename']);
$fext = $finfo['extension'];
if (!self::checkAllowedFilesearchExtensions($fext)) { // Only custom whitelist check ([line 320](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L320))
return '';
}
if ($wp_filesystem->exists($targetDir . DIRECTORY_SEPARATOR . $name)) {
$timest = time();
$name = $fname . '_' . $timest . '_' . random_int(1000, 9999) . '.' . $fext;
}
}
$tmp_name = sanitize_text_field($_FILES['s2baia_chatbot_config_database']['tmp_name']);
$outfile = $targetDir . DIRECTORY_SEPARATOR . $name;
// Open the output file and write contents using WP_Filesystem
if ($chunk === 0) {
$wp_filesystem->put_contents($outfile, '', FS_CHMOD_FILE);
}
// Read the temporary file and append its contents to the output file
$file_content = $wp_filesystem->get_contents($tmp_name);
if ($file_content === false) {
return '';
}
// Append content to the file
if (!$wp_filesystem->put_contents($outfile, $file_content, FS_CHMOD_FILE)) { // SINK: Direct file write ([line 344](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L344))
return '';
}
// Delete the temporary file using WordPress method
// ... rest of function
}
Fichier : lib/helpers/Utils.php
Lignes : 354-383
public static function checkAllowedFilesearchExtensions($ext) {
switch ($ext) {
case 'c':
case 'cs':
case 'cpp':
case 'doc':
case 'docx':
case 'html':
case 'java':
case 'json':
case 'md':
case 'pdf':
case 'php': // Explicitly allows PHP files ([line 366](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L366))
case 'pptx':
case 'py':
case 'rb':
case 'tex':
case 'txt':
case 'css':
case 'js':
case 'sh':
case 'ts':
return true;
default:
return false;
}
return false;
}