欢迎来到 Xiuno BBS

Xiuno BBS 审计之问题:插件分发无签名,可被篡改

此文章为XIUNOX版本重构审计时发现问题,XIUNOX版本已优化修复此问题。分享出来方便后续想基于xiuno bbs4.0.4版本制作维护版本或插件模板等需求的开发者和站长参考。

现象

Xiuno BBS 4.0.4 的插件分发完全依赖已失效的 http://plugin.xiuno.com/,用户被迫从第三方站点(论坛帖、网盘、QQ 群文件)下载插件 .zip 包,但 BBS 没有任何离线安装校验机制:

  1. 没有 .zip 包签名校验。
  2. 没有 conf.json 中的 author_pubkey / signature 字段。
  3. 没有 plugin_download_unzip() 解压后的完整性校验(仅检查 conf.json 是否存在与 name 字段非空)。
  4. 没有插件来源溯源(从哪个 URL 下载、谁发布的、何时发布的)。
  5. 没有插件黑名单/漏洞通告机制。

源码证据

文件:xiunobbs_4.0.4/admin/route/plugin.php 第 388-434 行(解压后仅检查 conf.json 存在与 name 字段,无签名校验)

function plugin_download_unzip($dir) {
    global $conf;
    // ...
    $url = PLUGIN_OFFICIAL_URL."plugin-download-$dir-$siteid-$app_url.htm";
    $s = http_get($url);
    // ...
    $zipfile = $conf['tmp_path'].'plugin_'.$dir.'.zip';
    $destpath = APP_PATH."plugin/";
    file_put_contents($zipfile, $s);

    // 清理原来的钩子,防止叠加。
    rmdir_recusive(APP_PATH."plugin/$dir/hook/", 1);
    rmdir_recusive(APP_PATH."plugin/$dir/overwrite/", 1);

    // 直接覆盖原来的 plugin 目录下的插件目录
    xn_unzip($zipfile, $destpath);

    // 检查配置文件
    $conffile = "../plugin/$dir/conf.json";
    !is_file($conffile) AND message(-1, 'conf.json '.lang('not_exists'));
    $arr = xn_json_decode(file_get_contents($conffile));
    empty($arr['name']) AND message(-1, 'conf.json '.lang('format_maybe_error'));

    !is_dir("../plugin/$dir") AND message(-1, lang('plugin_maybe_download_failed')." plugin/$dir");
}
仅校验 conf.json 存在与 name 非空,无 file_md5 / signature / author_pubkey 校验。

文件:xiunobbs_4.0.4/model/plugin.func.php 第 481 行(conf.json 字段定义中虽然有 file_md5,但从未被使用)

!isset($official['file_md5']) && $official['file_md5'] = '';
file_md5 字段从官方源返回,但本地 plugin_install() 流程从未调用 md5_file() 比对,形同虚设。

文件:xiunobbs_4.0.4/model/plugin.func.php 第 439-513 行(plugin_read_by_dir 读取大量官方元数据,但官方源失效后这些字段全部为空)

// 加上官方插件的信息
!isset($official['pluginid']) && $official['pluginid'] = 0;
!isset($official['is_cert']) && $official['is_cert'] = 0;       // 官方认证字段,失效后永远为 0
!isset($official['file_md5']) && $official['file_md5'] = '';    // 文件 MD5,失效后永远为空
!isset($official['lastupdate']) && $official['lastupdate'] = 0; // 最后更新时间,失效后永远为 0
// ...

文件:xiunobbs_4.0.4/model/plugin.func.php 第 11 行(PLUGIN_OFFICIAL_URL 硬编码失效 URL)

define('PLUGIN_OFFICIAL_URL', DEBUG == 4 ? 'http://plugin.x.com/' : 'http://plugin.xiuno.com/');

风险等级与结论

风险等级:严重(Critical)|生态缺陷

危害:

  1. 用户从第三方下载的插件包无任何校验,攻击者可植入后门后重新打包分发。
  2. file_md5 字段存在但从未被使用,给用户造成"已校验"的虚假安全感。
  3. is_cert(官方认证)字段失效后永远为 0,但 UI 仍展示该字段,用户无法判断插件可信度。
  4. 没有插件来源记录,事后追溯供应链攻击时无法定位下载源。
  5. 没有插件黑名单机制,已知恶意插件无法被 BBS 自动拒绝安装。

修复建议:

  • 实现 Ed25519 签名校验:插件包必须附 conf.json.sig,BBS 内置官方公钥,安装前强制校验。
  • 在 conf.json 中新增 authorauthor_urlsource_urlsignaturesigned_at 字段,安装时记录到本地 plugin_audit.json
  • 实现 plugin_verify_signature($zip_path, $pubkey) 函数,集成到 plugin_download_unzip() 与离线上传安装流程。
  • 建立"插件黑名单"远程拉取机制(类似浏览器恶意扩展列表),定期从 HTTPS 端点获取已知恶意插件 dir 与 md5 列表,安装时拦截。
  • UI 显著展示插件来源、签名状态、官方认证状态,未签名插件用红色警告。
  • 提供"离线安装"入口,允许上传本地 .zip,但同样强制签名校验。
0 0 0
复制成功