解析Ash框架授权绕过漏洞CVE-2025-48044:当Bypass策略条件为真时

本文详细分析了Ash框架中的一个高危授权绕过漏洞(CVE-2025-48044)。该漏洞存在于Ash的策略评估逻辑中,当Bypass策略的条件评估为真但其内部授权检查失败时,可能导致系统错误地授权请求。文章包含漏洞详情、影响版本、修复代码和概念验证。

Ash框架授权绕过漏洞分析:CVE-2025-48044

概述

Ash(一个Erlang/Elixir应用程序框架)在3.6.3至3.7.0版本中存在一个授权绕过漏洞。当Bypass策略的条件评估为true,但其内部的授权检查失败,且没有其他适用策略时,系统可能错误地授权本应被拒绝的请求。

受影响版本

  • 受影响版本:>= 3.6.3, <= 3.7.0
  • 已修复版本:3.7.1

漏洞详情

漏洞位置

漏洞代码位于:lib/ash/policy/policy.ex:69

1
2
3
4
5
{%{bypass?: true}, cond_expr, complete_expr}, {one_condition_matches, all_policies_match} ->
  {
    b(cond_expr or one_condition_matches),  # <- 漏洞:仅使用了条件表达式
    b(complete_expr or all_policies_match)
  }

漏洞原理

最终的授权决策逻辑为:one_condition_matches AND all_policies_match

当一个Bypass策略的条件为真但授权失败,且后续策略的条件不匹配时:

  1. one_condition_matches = cond_expr (Bypass条件) = true (漏洞点:本应检查Bypass是否真正授权)
  2. 对于不匹配的策略:all_policies_match 的计算结果为 true(因为策略不适用)

最终得到:true AND true = true(被错误授权)。

核心问题:仅Bypass条件满足就足以让系统认为“至少有一个策略适用”,即使该Bypass策略本身未能成功授权。

修复方案

将第69行的cond_expr替换为complete_expr

1
2
3
4
5
{%{bypass?: true}, _cond_expr, complete_expr}, {one_condition_matches, all_policies_match} ->
  {
    b(complete_expr or one_condition_matches),  # <- 已修复
    b(complete_expr or all_policies_match)
  }

为了保持一致性,第52行也应更新(尽管在实践中,仅当Bypass是最后一个策略时才会触发,使其偶然安全):

1
2
3
4
5
{%{bypass?: true}, _cond_expr, complete_expr}, {one_condition_matches, true} ->
  {
    b(complete_expr or one_condition_matches),  # <- 为保持一致性
    complete_expr
  }

概念验证(PoC)

以下策略配置展示了该漏洞:

1
2
3
4
5
6
7
8
9
policies do
  bypass always() do
    authorize_if actor_attribute_equals(:is_admin, true)
  end

  policy action_type(:read) do
    authorize_if always()
  end
end

在此配置下,非管理员用户可以执行创建操作(本应被拒绝)。

测试代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
test "bypass policy bug" do
  policies = [
    %Ash.Policy.Policy{
      bypass?: true,
      condition: [{Ash.Policy.Check.Static, result: true}],  # 条件 = true
      policies: [
        %Ash.Policy.Check{
          type: :authorize_if,
          check: {Ash.Policy.Check.Static, result: false},  # 策略 = false
          check_module: Ash.Policy.Check.Static,
          check_opts: [result: false]
        }
      ]
    },
    %Ash.Policy.Policy{
      bypass?: false,
      condition: [{Ash.Policy.Check.Static, result: false}],
      policies: [
        %Ash.Policy.Check{
          type: :authorize_if,
          check: {Ash.Policy.Check.Static, result: true},
          check_module: Ash.Policy.Check.Static,
          check_opts: [result: true]
        }
      ]
    }
  ]

  expression = Ash.Policy.Policy.expression(policies, %{})
  
  assert expression == false
  # 期望结果:false (拒绝)
  # 主分支上的实际结果:true (错误授权)
end

安全信息

  • 严重等级:高
  • CVSS 4.0 评分:8.6 (AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N)
  • CWE分类:CWE-863 - 不正确的授权

参考链接

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计