Statamic CMS敏感信息泄露漏洞CVE-2026-71293
Statamic 诞生于 2012 年,基于Laravel开发的现代化混合架构 CMS,主打文件存储优先、可选数据库、兼顾开发者自由与编辑易用性。
一、基本情况
Core内核永久免费,开放全部核心CMS功能,个人、商用均免费使用,前端完全自主可控,没有预设主题捆绑,适合定制化品牌官网。

Statamic是海外主流扁平化(Flat File)内容管理系统,分为免费开源内核版、商用 Pro 授权版,后台UI现代简洁,支持内容实时预览。
栋科技漏洞库关注到Statamic CMS通过 Antlers current_user 变量不受保护地暴露2FA,追踪为CVE-2026-71293,CVSS 3.X评分6.2。
二、漏洞分析
CVE-2026-71293是Statamic CMS 信息泄露漏洞,由于模板变量 current_user 缺少防护,双因子认证(2FA)应急恢复码被直接读取。
密码这类敏感字段原本做了隐藏防护,唯独恢复码漏掉了保护规则,攻击者拿到恢复码即可绕过二次验证登录账号,盗取管理员权限。
Statamic CMS的用户增强解析器AugmentedUser::get()在src/Auth/AugmentedUser.php中,
包含一个显式的“two_factor_recovery_codes”句柄案例,该句柄返回用户的原始双因素恢复代码,
没有访问限制:
“if($handle==='two_factor-recovery_codes'){返回新值($this->data->get('two_factory_recovery_codes'),…);}`。
与完全从AugmentedUser中排除的敏感字段(如password/password_hash)不同,
two_factor_recovery_codes既没有从Augmented User中排除,
也没有出现在Statamic的Antlers变量保护列表中:
src/Providers/ViewServiceProvider.php中的guardVariablePatterns/guardContentVariablePatterns和运行时GlobalRuntimeState保护路径
默认情况下只有guard config.app.key。
在为给定字段启用原始/动态模板渲染的任何Antlers模板字段上(管理员/开发人员配置的蓝图级字段选项),
诸如“{current_user.two_factor_recovery_codes}{value}|{/current_user.tw_factor_reivery_codes}”之类的模板,
将查看用户自己的2FA恢复代码直接渲染到HTML响应中,
允许可以查看或捕获该响应的攻击者(例如通过共享/可观察页面,或特制的链接导致受害者渲染)获取代码并绕过2FA。
触发条件:网站开启了动态模板渲染功能,登录任意账号后,写入一段简易模板代码就能读出本人的 2FA 救援码。
因此利用该漏洞需要在目标用户数据流经的字段上启用动态Antlers渲染,这是一种蓝图配置权限,而不是标准的内容编辑权限。
src/Auth/AugmentedUser.php
<?php
namespace Statamic\Auth;
use Statamic\Data\AbstractAugmented;
use Statamic\Facades\Role;
use Statamic\Facades\User;
use Statamic\Facades\UserGroup;
use Statamic\Fields\Value;
use Statamic\Support\Str;
class AugmentedUser extends AbstractAugmented
{
private $cachedKeys;
public function keys()
{
if ($this->cachedKeys) {
return $this->cachedKeys;
}
return $this->cachedKeys = $this->data->data()->keys()
->merge(collect($this->data->supplements() ?? [])->keys())
->merge($this->commonKeys())
->merge($this->roleHandles())
->merge($this->groupHandles())
->merge($this->blueprintFields()->keys())
->unique()->sort()->values()->all();
}
private function commonKeys()
{
return [
'id',
'name',
'title',
'email',
'initials',
'edit_url',
'is_user',
'last_login',
'avatar',
'api_url',
'preferred_locale',
];
}
public function get($handle): Value
{
if ($handle === 'is_user') {
return new Value(true, 'is_user', null, $this->data);
}
if ($handle === 'is_super') {
return new Value($this->data->isSuper(), 'is_super', null, $this->data);
}
if ($handle === 'two_factor_recovery_codes') {
return new Value($this->data->get('two_factor_recovery_codes'), 'two_factor_recovery_codes', null, $this->data);
}
if (Str::startsWith($handle, 'is_')) {
return new Value(in_array(Str::after($handle, 'is_'), $this->roles()), $handle, null, $this->data);
}
if (Str::startsWith($handle, 'in_')) {
return new Value(in_array(Str::after($handle, 'in_'), $this->groups()), $handle, null, $this->data);
}
return parent::get($handle);
}
protected function roles()
{
return $this->data->roles()->map->id()->values()->all();
}
protected function groups()
{
return $this->data->groups()->map->id()->values()->all();
}
protected function roleHandles()
{
return Role::all()->map(function ($role) {
return 'is_'.$role->handle();
})->values()->all();
}
protected function groupHandles()
{
return UserGroup::all()->map(function ($group) {
return 'in_'.$group->handle();
})->values()->all();
}
protected function initials()
{
if (! $this->data->hasQueriedColumn('name')) {
$user = User::query()
->where('id', $this->data->id())
->get(['name'])
->first();
$this->data->set('name', $user->get('name'));
}
return $this->data->initials();
}
protected function avatar()
{
return $this->data->hasAvatarField() ? $this->data->avatarFieldValue() : $this->data->gravatarUrl();
}
protected function preferredLocale()
{
return $this->data->preferredLocale();
}
public function notifications()
{
return $this->data->get('notifications');
}
}
三、影响范围
未知
四、修复建议
未知
五、参考链接
管理员已设置登录后刷新可查看