
S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Gerador de Conteúdo e Imagens <= 1.7.7 - Upload Arbitrário de Arquivo Autenticado (Editor+)
O plugin WordPress S2B AI Assistant (versões 2.47 e anteriores) contém uma vulnerabilidade de upload arbitrário de arquivos que permite que usuários autenticados do WordPress com função de Editor ou superior façam upload de arquivos PHP maliciosos para o servidor, potencialmente levando à execução remota de código.
Um POC CVE-2025-12973.py é fornecido para demonstrar um atacante remoto fazendo upload de shell.php e executando código remoto:
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)
A vulnerabilidade existe no arquivo Utils.php no método storeFile(), que é chamado pelo endpoint /wp-admin/admin-post.php com ação s2b_store_chatbot_upload. A funcionalidade de upload usa uma lista branca de extensões de arquivo personalizada que permite explicitamente tipos de arquivo perigosos, incluindo arquivos PHP. Isso permite que usuários autenticados do WordPress com função de Editor ou superior façam upload de arquivos arbitrários, incluindo arquivos PHP que podem ser executados no servidor.
Fonte: Entrada do usuário a partir de $_FILES['s2baia_chatbot_config_database'] (linha 300)
Sumidouro: $wp_filesystem->put_contents($outfile, $file_content, FS_CHMOD_FILE) (linha 344)
A vulnerabilidade ocorre porque:
AdminChatBotController.php.processAssistantUpload() lida com a solicitação de upload (linha 1103).$_FILES['s2baia_chatbot_config_database'] são processados diretamente sem validação adequada.checkAllowedFilesearchExtensions() (linha 320), que permite explicitamente a extensão .php (linha 366).wp-content/uploads/YYYY/MM/.File: lib/helpers/Utils.php
Lines: 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
}
File: lib/helpers/Utils.php
Lines: 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;
}