影响范围 = Typecho ≥ 1.2.0 全部版本(1.2.0 / 1.2.1 / 1.3.0 及其间 rc/beta)
漏洞涉及 Typecho 的异步服务组件(/action/service 接口),由以下两点构成:
成因一:松散比较导致令牌校验可被类型绕过
sha1() 函数恒返回 40 位非空十六进制字符串。令牌校验中 非空字符串 == true 在 PHP 松散比较语义下恒成立。当请求体中令牌字段传入布尔值 true 时,校验函数无条件返回真值,令牌的密码学防伪造意义被完全短路,等同于鉴权不存在。
成因二:异步服务将请求参数直接作为方法名分发
异步处理流程对请求中传入的"方法名"不做白名单约束。由于插件初始化方法为公开静态方法,PHP 允许在对象上下文直接调用,攻击者可先覆写进程内插件回调表为可控内容,随后调用任意方法名命中被注入的回调,最终经 call_user_func_array 执行任意 PHP 回调,形成"覆写回调表 + 任意代码执行"的完整利用原语。
前置条件:请求需携带目标站点同源 Referer 头(Typecho 原生同源校验要求),除此之外无任何凭据要求。
- 未授权远程代码执行:任何能访问站点的人(携带同源 Referer)可获得 Web 服务用户身份的任意 PHP 回调执行、任意文件读写。
- 敏感信息泄露:应用配置文件、系统文件等均可被读取。
- 影响范围广:自 2022 年 4 月 1.2.0 发布至今四年余,官方未发布修复版本;当前最新版本仍受影响。
- 利用门槛低:单个 HTTP 请求,无加密、无验证码依赖。
if (sha1($secret . '&' . $i) == $token) {
return true;
}PHP 里字符串和布尔值拿双等号比 字符串这头会先转布尔 而 sha1 出来永远是四十位非空串 转布尔永远是 true 那么你从 JSON 里传个布尔 true 进去 true 等于 true 恒成立 密钥是什么根本不用知道 校验形同虚设
第二个在 var/Widget/Service.php 的 asyncHandle 过了校验之后它会遍历你传的 requests 数组 拿数组里的方法名直接调插件对象 方法名完全可控 而 Plugin 类有个 public static 的 init 方法 传个数组进去就能把整个插件回调表替换掉 回调表里放什么函数名它就 call_user_func_array 执行什么 到这一步想干嘛就干嘛了
这个路由本身不挂任何鉴权 注册在 Action 的映射表里 匿名 POST 即达 也就是说这两个条件一叠 任何人都能在你博客上执行任意 PHP 函数
至于回调表是怎么被换掉的 Plugin 类的 init 方法是 public static 传个数组进去 self::$plugin 整个就换了 里面 handles 就是回调表
真正干活的是 Plugin 的魔术方法 __call 按回调表逐个 call_user_func_array 表里放什么函数名就执行什么
我自己博客实测打穿到落webshell RCE一路畅通 命令执行被 disable_functions 拦截了 但任意文件读取仍可执行 
修复
修复一:令牌校验改恒时强比较
将 var/Typecho/Common.php 中时间令牌校验方法内的松散比较改为恒时强比较:
// 修改前
if (sha1($secret . '&' . $i) == $token) {
// 修改后
if (hash_equals(sha1($secret . '&' . $i), (string) $token)) {hash_equals 兼具强类型比较与恒时特性,可同时防御类型绕过与时序侧信道。修复二:异步服务入口增加令牌类型约束
在 var/Widget/Service.php 的两个令牌校验入口(异步请求处理方法与 Ping 处理方法)中,于校验前增加字符串类型判断:
// 修改前
$token = $data['token'] ?? '';
if (!Common::timeTokenValidate($token, $this->options->secret, 3)) {
throw new Exception(_t('禁止访问'), 403);
}
// 修改后
$token = $data['token'] ?? '';
if (!is_string($token) || !Common::timeTokenValidate($token, $this->options->secret, 3)) {
throw new Exception(_t('禁止访问'), 403);
}以上两处最小补丁已在测试环境部署并回归验证:修复后鉴权绕过路径在两个入口均失效,未发现旁路。





我选择直接把service文件里边action方法中那两个入口直接注释掉
对我来说没啥用
每次出个漏洞就得折腾半天
这个洞挺严重的 如果涉及的话尽早修复吧