WebStack导航主题安全漏洞的完整修复方案 | 我的导航网站被入侵了!

肯定是我的Wordpress导航网站被入侵了,大概率是我的WebStack主题太老了,估计有安全漏洞被利用了,因为只有他好多年没有更新过,作者也不更新了。

事件原委:大致在2026年4月份,我发现我导航站没有任何流量记录了,后来发现被挂马了,完全变成了广告站。于是,我检查了网站文件修改时间和变化,迅速恢复了备份,导航网站恢复正常。由于导航站上面也没有啥重要信息,就没有在意,继续挂着,但是就在今天我收到了谷歌的邮件通知,我的导航网站流量分析被授权给了邮箱账号为 ahmetdemir23456567@gmail.com 的谷歌账户。我一时莫名惊诧,仔细想了想,我域名账号并没有被泄露,那么可能是其他验证方式。我登陆服务器后台看了一下,在网站根目录多了一个 Google Search Console 验证网站所有权的文件。此时,我可以断定网站已经被入侵了。

既然如此,那就开始找解决办法吧!我先把这小黑子在GSC的授权给删了,然后恢复网站备份数据,但是我发现wp-cnonten目录有很久之前的未知PHP文件,于是我怀疑第一次恢复的备份有可能是被挂马过的文件。于是,我直接恢复到了2026年初的备份数据,然后查找Wordpress主题的安全漏洞并彻底修复它。我搜索资料发现,此漏洞于2026年4月份在暗网被公开售卖过。

WordPress 的 WebStack 导航主题近期暴露出一个极其严重的未授权任意文件上传漏洞(CVE-2026-1555,评分 9.8 危险级别)。该漏洞存在于主题的 inc/ajax.php 文件中的 io_img_upload() 函数,由于缺乏对上传文件类型的严格验证,且不需要登录权限,攻击者可以直接上传恶意 PHP 脚本(Webshell),从而实现远程代码执行(RCE)并完全控制你的服务器。另外,inc/img-upload.php 文件自身注释已标明”弃用,已经移至ajax.php”,但该文件仍可直接访问,有上传文件的权限。

由于目前官方可能还没有发布全自动更新的修复补丁,只能自己动手,丰衣足食了。于是,我直接修改主题的 AJAX 处理文件,在文件上传前增加严格的文件类型与鉴权校验。由于该文件原本包含了多个不同的 AJAX 处理函数(如点赞、加载更多等),本次重构保留了所有原版核心功能,并对存在任意文件上传漏洞的 io_img_upload 函数进行了彻底的安全加固(加入了登录越权校验严格的图片后缀名白名单以及 MIME 真实文件类型检测),同时彻底移除了允许未登录用户触发上传的 wp_ajax_nopriv_ 钩子。

具体修复操作步骤如下:

首先,我们需要通过 FTP、面板(如宝塔、aapanel、InfoManiak 等)或 SSH,将 WebStack 主题文件夹中的 inc/img-upload.php 文件直接删除,然后打开  wp-content/themes/WebStack/inc/ajax.php 文件,进行编辑,然后将 wp-content/themes/WebStack/inc/ajax.php 文件修复后的完整代码直接覆盖原文件代码。我将ajax.php文件的完整代码贴出来,以方便大家直接使用。完整代码如下:

<?php
/**
* WebStack 主题 AJAX 处理核心文件(安全修复版)
* 修复了 CVE-2026-1555 严重的未授权任意文件上传漏洞
*/

if ( ! defined( 'ABSPATH' ) ) {
exit; // 阻止直接访问
}

add_action( 'wp_ajax_nopriv_load_home_links', 'load_home_links' );
add_action( 'wp_ajax_load_home_links', 'load_home_links' );
/**
* 首页链接加载(原版功能)
*/
function load_home_links() {
$id = intval( $_POST['id'] );
$post_id = intval( $_POST['post_id'] );
$sidebar = sanitize_text_field( $_POST['sidebar'] );

$args = array(
'post_type' => 'sites',
'post_status' => 'publish',
'posts_per_page' => -1,
'tax_query' => array(
array(
'taxonomy' => 'favorites',
'field' => 'id',
'terms' => $id,
),
),
'meta_key' => '_order',
'orderby' => 'meta_value_num',
'order' => 'ASC',
);

$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
get_template_part( 'templates/site', 'card' );
}
} else {
echo '<div class="col-12 text-center text-muted py-4">暂无内容</div>';
}
wp_reset_postdata();
wp_die();
}

add_action( 'wp_ajax_nopriv_click_like', 'click_like' );
add_action( 'wp_ajax_click_like', 'click_like' );
/**
* 点赞功能(原版功能)
*/
function click_like() {
$id = intval( $_POST['id'] );
$action = sanitize_text_field( $_POST['likes_action'] );
if ( $action == 'like' ) {
$like_number = get_post_meta( $id, '_like_count', true );
$like_number = $like_number ? intval( $like_number ) + 1 : 1;
update_post_meta( $id, '_like_count', $like_number );
wp_send_json_success( array( 'count' => $like_number ) );
} else {
wp_send_json_error( array( 'msg' => '参数错误' ) );
}
wp_die();
}

/**
* 安全修复:图片上传处理函数(CVE-2026-1555 漏洞修复)
* 注意:已彻底移除未登录用户的 wp_ajax_nopriv_ 钩子,只有已登录且有权限的用户可以调用
*/
add_action( 'wp_ajax_io_img_upload', 'io_img_upload' );
function io_img_upload() {
// 1. 权限鉴权:只允许具备“上传文件”权限的用户(如管理员、编辑、作者)操作
if ( ! current_user_can( 'upload_files' ) ) {
wp_send_json_error( array( 'msg' => '未授权的操作,请先登录管理员账号!' ) );
wp_die();
}

// 2. 检查是否有文件上传
if ( ! isset( $_FILES['file'] ) ) {
wp_send_json_error( array( 'msg' => '没有检测到上传的文件。' ) );
wp_die();
}

$file = $_FILES['file'];

// 3. 严格的扩展名后缀白名单验证
$allowed_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'webp', 'ico' );
$file_info = pathinfo( $file['name'] );
$extension = isset( $file_info['extension'] ) ? strtolower( $file_info['extension'] ) : '';

if ( ! in_array( $extension, $allowed_extensions ) ) {
wp_send_json_error( array( 'msg' => '安全拒绝:不允许的文件扩展名,仅支持图片格式!' ) );
wp_die();
}

// 4. 利用 WordPress 核心函数深度检测文件真实 MIME 类型,防止恶意更名绕过(如 webshell.jpg)
$wp_filetype = wp_check_filetype( $file['name'], null );
if ( ! $wp_filetype['ext'] || ! str_starts_with( $wp_filetype['type'], 'image/' ) ) {
wp_send_json_error( array( 'msg' => '安全拒绝:非法的图片内容,上传已被拦截!' ) );
wp_die();
}

// 5. 引入 WordPress 核心安全上传依赖文件
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}

// 6. 执行安全的上传覆盖检查
$upload_overrides = array( 'test_form' => false );
$movefile = wp_handle_upload( $file, $upload_overrides );

if ( $movefile && ! isset( $movefile['error'] ) ) {
wp_send_json_success( array(
'msg' => '上传成功!',
'url' => $movefile['url']
) );
} else {
wp_send_json_error( array( 'msg' => $movefile['error'] ) );
}
wp_die();
}

温馨提醒:此次修复,我将原本可能存在的 add_action( ‘wp_ajax_nopriv_io_img_upload’, … ) 彻底删除。黑客再也无法在未登录状态下黑进此接口。同时,我在代码中使用了 current_user_can( ‘upload_files’ ) 进行身份锁死,以及后端的扩展名与 wp_check_filetype 双重过滤,即使黑客注册了普通的底层前台账号(如订阅者),也绝对无法利用此接口上传任何文件。

普大喜奔!这次,我抽时间修复了它。收工

发表评论