S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator <= 1.7.7 - Authenticated (Editor+) Arbitrary File Upload# S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator <= 1.7.7 - Authenticated (Editor+) Arbitrary File Upload
The WordPress [S2B AI Assistant](https://wordpress.org/plugins/s2b-ai-assistant) plugin (versions 2.47 and prior) contains an arbitrary file upload vulnerability that allows authenticated WordPress users with Editor role or higher to upload malicious PHP files to the server, potentially leading to remote code execution.
## TL;DR Exploits
A POC [CVE-2025-12973.py](./CVE-2025-12973.py) is provided to demonstrate a remote attacker uploading `shell.php` and executing remote code:
```
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)
```
## Technical Description
The vulnerability exists in the `Utils.php` file at the `storeFile()` method, which is called by the `/wp-admin/admin-post.php` endpoint with action `s2b_store_chatbot_upload`. The upload functionality uses a custom file extension whitelist that explicitly allows dangerous file types including PHP files. This allows authenticated WordPress users with Editor role or higher to upload arbitrary files, including PHP files that can be executed on the server.
### Attack Path Analysis
**Source**: User input from `$_FILES['s2baia_chatbot_config_database']` ([line 300](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L300))
**Sink**: `$wp_filesystem->put_contents($outfile, $file_content, FS_CHMOD_FILE)` ([line 344](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L344))
The vulnerability occurs because:
1. **Route Registration**: The upload endpoint is registered in [`AdminChatBotController.php`](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/controllers/AdminChatBotController.php#L25).
2. **Controller Access**: The `processAssistantUpload()` method handles the upload request ([line 1103](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/controllers/AdminChatBotController.php#L1103)).
3. **Input Processing**: User-controlled file data from `$_FILES['s2baia_chatbot_config_database']` is directly processed without proper validation.
4. **File Handling**: Only a custom extension whitelist check is applied using `checkAllowedFilesearchExtensions()` ([line 320](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L320)), which explicitly allows `.php` extension ([line 366](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L366)).
5. **File Storage**: Files are saved to `wp-content/uploads/YYYY/MM/`.
### Vulnerable Code Location
**File**: [`lib/helpers/Utils.php`](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php)
**Lines**: [289-348](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L289)
```php
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`](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php)
**Lines**: [354-383](https://plugins.trac.wordpress.org/browser/s2b-ai-assistant/trunk/lib/helpers/Utils.php#L354)
```php
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;
}
```
Log in to view the POC file snapshot cached by Shenlong Bot
Log in to view