Grafana SQL表达式漏洞 | CVE-2026-27876概念复现&研究

0x0 背景介绍

Grafana是一个开源的度量分析和可视化套件。该漏洞源于Grafana的SQL 表达式(sqlExpressions)功能在处理SQL语法时存在安全漏洞,允许攻击者进行任意文件写入。攻击者可以通过链式利用该漏洞,覆盖Sqlyze驱动程序或写入恶意的AWS数据源配置文件。由于攻击者可以控制写入的文件内容,这将导致在Grafana主机上实现远程代码执行(RCE),甚至可能获取SSH连接权限。

0x1 环境搭建(Ubuntu24)

  • 简单YML参考
bash 复制代码
# docker-compose.yml
version: '3.8'

services:
  grafana:
    image: grafana/grafana:12.4.1
    container_name: grafana-vuln
    ports:
      ◦ "3000:3000"

    environment:
      ◦ GF_SECURITY_ADMIN_USER=admin
    
      ◦ GF_SECURITY_ADMIN_PASSWORD=admin
    
      # 关键配置:启用 sqlExpressions 功能开关,这是漏洞利用的前提
      ◦ GF_FEATURE_TOGGLES_ENABLE=sqlExpressions
    
    volumes:
      ◦ grafana-storage:/var/lib/grafana


volumes:
  grafana-storage:

0x2 漏洞复现

  • 前置条件
条件 说明
身份与权限 能够执行数据源查询
功能开 关实例启用sqlExpressions特性
链式组件(RCE场景) 环境中存在可被覆盖的Sqlyze相关文件,或AWS数据源配置落盘路径等(见 下文,也就是存在问题的插件)

2.1-手动复现

首先必须说明,我环境没有Sqlyze和AWS数据源(因为搞不到),所以使用MYSQL辅助验证,严格意义上算复现了半个,剩下就是原理讨论,没错!!又是原理讨论

2.1.1 场景 A:通过统一查询 API 投递 SQL 表达式(攻击面验证)

目标:确认请求可到达服务端表达式管线,且 type: sql 与 expression 字段被解析。

bash 复制代码
1.使用具备数据源查询权限的会话登录 Grafana,取得会话 Cookie(或 API Key / Service Account Token,视部署而定)。
2.准备一个基础查询 refId: A(任意已授权数据源,返回至少一帧表格或可转换时序数据)。
3.在同一 MetricRequest 中增加 refId: B,数据源为内置 __expr__,type 为 sql,expression 为受控 SQL 字符串。
4.向 POST /api/ds/query(或经特性开关改写后的 Query API 路径)发送 JSON 体。
  • 首先创建一个测试的数据源,获取其ID

  • Payload 结构示意(字段名与 pkg/expr/service_test.go、service_sql_test.go 中构造一致)

bash 复制代码
POST /api/ds/query HTTP/1.1
Host: grafana.example.com 
Content-Type: application/json 
Cookie: grafana_session=<会话 Cookie> 
Content-Length: <自动>

{"from":"now-1h","to":"now","queries":[{"refId":"A","datasource":{"type":"grafana-testdata-datasource","uid":"<你的测试数据源 UID>"},"scenarioId":"random_walk","intervalMs":1000,"maxDataPoints":100},{"refId":"B","datasource":{"uid":"__expr__","type":"__expr__"},"type":"sql","expression":"SELECT * FROM A LIMIT 10"    }  ]}
  • 创建一个MYSQL容器(是主动开启了不安全的secure_file_priv,用于验证)

  • 进行写入直接写入容器(图不清晰呀...难道是大屏截图后转发压缩了?)

2.1.2 场景 B:与 Enterprise 生态链式 RCE(概念)

目标:说明从「SQL 表达式」到「远程代码执行」的业务级链条,而非单一HTTP 请求即完成RCE。

bash 复制代码
1.攻击者在满足 场景 A 的前提下,利用 SQL 表达式实现对文件系统的非预期写入。
2.将恶意内容写入可被 Sqlyze 加载的驱动路径,或写入 AWS 数据源使用的配置文件路径。
3.在后续查询或插件加载过程中执行任意代码,进而可能获得对 Grafana 宿主机的 SSH 级控制(官方说明)依赖环境中是否安装、启用上述组件;
OSS源码树本身不自带Enterprise插件二进制,但SQL表达式能力在OSS 中由特性开关启用,与公告中攻击描述一致。
2.1.3 其他理论姿势展示认证方式差异与同一漏洞面的不同入口

1)验证接口

bash 复制代码
POST /api/ds/query HTTP/1.1
Host: grafana.example.com
Content-Type: application/json
Cookie: grafana_session=<会话 Cookie>Content-Length: <自动>

{"from":"now-5m","to":"now","queries":[{"refId":"A","datasource":{"uid":"<ds-uid>","type":"testdata"},"maxDataPoints":100},{"refId":"B","datasource":{"uid":"__expr__","type":"__expr__"},"type":"sql","expression":"SELECT * FROM A"}]}

2)API Token(Header)

bash 复制代码
POST /api/ds/query HTTP/1.1
Host: grafana.example.com
Content-Type: application/json
Cookie: grafana_session=<会话 Cookie>

{"from":"now-5m","to":"now","queries":[/* 同结构 */]}

3)经反向代理 / K8s Ingress 的改写路径若启用FlagQueryServiceRewrite,客户端可能访问被改写至Kubernetes API聚合层路径,例如:

bash 复制代码
POST /apis/query.grafana.app/v0alpha1/namespaces/<org-namespace>/query HTTP/1.1

此时请求体 JSON结构仍为多查询 +__expr__SQL节点,与ds_query.go中重写逻辑一致。

  • WAF/IDS 可关注特征
bash 复制代码
URI 包含 /api/ds/query 或 query.grafana.app。
Body 同时出现 "__expr__" 与 "type":"sql"。
expression 字段中出现与文件、导出、路径相关的 SQL 关键字组合。

2.2-复现流量特征 (PCAP)

  • 测试语句(明文流量,不要问为什么截图恶意流量...因为复现要了我半条命)

  • 测试环境

0x3 漏洞原理分析

3.1 [入口定位] 从「面板查询」到「服务端表达式」:谁在处理 type: sql

  • 先看在用户可控输入上:Grafana 的 Server-Side Expressions(SSE)允许在 MetricRequest 中声明 __expr__数据源,并在 JSON 里携带 type 与具体表达式。对于 SQL 表达式,结构体定义在 pkg/expr/query.go 中,SQLExpression 明确要求 expression 字符串:
go 复制代码
// SQLQuery requires the sqlExpression feature flag
type SQLExpression struct {
    Expression string `json:"expression" jsonschema:"minLength=1,example=SELECT * FROM A LIMIT 1"`
    Format     string `json:"format"`
}
  • 接着看谁在控制这个能力:pkg/expr/nodes.go中,若命令类型为TypeSQL,则必须全局启用featuremgmt.FlagSqlExpressions,否则直接拒绝构建管线:
go 复制代码
// pkg/expr/nodes.go    
if commandType == TypeSQL {        
 //nolint:staticcheck  // not yet migrated to OpenFeature        
 	if !toggles.IsEnabledGlobally(featuremgmt.FlagSqlExpressions) {            
 		return nil, fmt.Errorf("sql expressions are disabled")        
 	}    
 }
  • 预期边界是清晰的------SQL 表达式是可选特性,仅应在显式开启时可用
  • 但「开启」本身只解决产品功能问题,并不自动等价于「对宿主文件系统零写入」
  • 后续执行栈是否足够收敛,要看SQL引擎与允许语法集合

3.2 [执行路径] 从 SQLCommand.Execute 到 go-mysql-server:最后一道防线在哪里?

  • SQLCommand在执行阶段将查询字符串交给sql.DB.QueryFrames
go 复制代码
// :pkg/expr/sql_command.go
 gr.logger.Debug("Executing query", "query", gr.query, "frames", len(allFrames))

    db := sql.DB{}
    frame, err := db.QueryFrames(ctx, tracer, gr.refID, gr.query, allFrames, sql.WithMaxOutputCells(gr.outputLimit), sql.WithTimeout(gr.timeout))
    if err != nil {
        rsp.Error = err
        return rsp, nil
    }
  • 先经过AllowQuery(基于 Vitesssqlparser 的允许列表),再创建 go-mysql-server引擎,并设置 IsReadOnly: true
go 复制代码
    if allow, err := AllowQuery(name, query); err != nil || !allow {
        if err != nil {
            return nil, err
        }
        return nil, err
    }
    // ...
    engine := sqle.New(a, &sqle.Config{
        IsReadOnly: true,
    })
  • 这里存在两层预期安全设计:语法/函数白名单(parser_allow.go)------ 阻断明显危险函数(仓库中单测亦验证如load_file会被拦)。
  • 引擎只读 ------ 试图从配置层面禁止写操作。

与官方披露的反差 Grafana Labs仍将 CVE-2026-27876 定性为可导致任意文件写入并进一步 RCE,说明在受影响版本中,上述组合未能在全部语义路径上形成等价于「纯内存只读分析」的安全边界。

3.3 [静态线索] 白名单里值得警惕的「INTO」节点

  • 在AllowQuery的allowedNode中,*sqlparser.Into被放行:
go 复制代码
// pkg/expr/sql/parser_allow.go    
case *sqlparser.Into:        
	return
  • 在MySQL语义族中,SELECT ... INTO类语句与「结果落盘/导出」强相关;
  • 若底层引擎对某类INTO变体的处理与「只读引擎」假设不一致就会出现偏差。

同期我也对比了修复后的,修改了条件并增加更多白名单:

go 复制代码
 // 12.4.2版本  修改校验case *sqlparser.Into:// Plain SELECT statements may carry a typed-nil Into pointer.// Reject only when INTO is actually present.	  return v == nil 
 // 12.4.2版本 增加防绕过 case *sqlparser.SetOp:-     return+     // SetOp.walkSubtree() does not traverse Into, so reject explicitly.+     return v.GetInto() == nil

增加了更多的白名单(也可能是其他漏洞的)

go 复制代码
-[] 未修复版本(4个):
if, sum/avg/count/min/max, coalesce, str_to_date

-[] 修复版本(80+个):
条件:ifnull, nullif, least
聚合:stddev, variance, group_concat
窗口:row_number, rank, lead, lag 等
数学:abs, round, pow, sqrt, log, sin, cos 等
字符串:concat, length, substring, replace, regexp_* 等
日期:大量日期函数
JSON:完整 JSON 函数集
类型转换:cast, convert

3.4 [攻击链路] 从「文件写入」到「RCE」:

官方闭环与本地代码印证:

  • 注入点:经认证用户可控的expression字符串(及与之配套的查询图依赖refId)。
  • 爆发点(文件系统):官方SQL表达式语法允许以不当方式向文件系统写入内容。
  • 二次爆发(代码执行):官方:覆盖Sqlyze 驱动相关文件,或写入 AWS 数据源配置,在后续加载或查询路径上达成RCE,并可能进一步获得SSH主机访问。
    链路总结
bash 复制代码
HTTP POST /api/ds/query 
 -> MetricRequest.queries[] (含 __expr__ + type:sql + expression)     [注入点] 
  -> expr.Service 构建 DAG / SQLCommand 
   -> sql.DB.QueryFrames: AllowQuery + go-mysql-server(IsReadOnly) 
    -> (受影响版本)文件写入 primitive                                    [爆发点-1]
     -> 覆盖 Sqlyze / AWS 数据源配置等                                      [爆发点-2]  
     -> 插件或进程加载恶意内容 -> RCE / SSH                                [极限危害]

0x4 修复建议

修复方案

  1. 升级最新版本:将组件升级安全版本:
bash 复制代码
项目地址:
https://github.com/grafana/grafana  

Grafana 12.4.2 版本,包含安全修复更新:
https://grafana.com/grafana/download/12.4.2?pg=grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880&plcmt=in-text

Grafana 12.3.6 安全修复版:
https://grafana.com/grafana/download/12.3.6?pg=grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880&plcmt=in-text

Grafana 12.2.8 安全修复版:
https://grafana.com/grafana/download/12.2.8?pg=grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880&plcmt=in-text

Grafana 12.1.10 版本,包含安全修复:
https://grafana.com/grafana/download/12.1.10?pg=grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880&plcmt=in-text

Grafana 11.6.14 安全修复版:
https://grafana.com/grafana/download/11.6.14?pg=grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880&plcmt=in-text
  1. 临时防护措施:
    关闭特性: 在配置中禁用sqlExpressions功能(会影响业务使用)
    防火墙 / WAF: 对包含__expr__ + sql的大体积或可疑expression请求做告警或限流(注意可别误杀合法请求)
    限制访问: 仅允许管理网段或堡垒机访问Grafana UI/API;对/api/ds/query 实施更严格的身份与RBAC/FGAC
    权限最小化: 以最小权限用户运行Grafana,宿主磁盘对进程可写目录做严格隔离,降低写入后的二次利用面。
    官方建议: 如果你安装了Sqlyze:更新到至少v1.5.0或禁用它。禁用已安装的所有AWS数据源
  • 官方链接:

    1\] Grafana 安全公告: https://grafana.com/blog/grafana-security-release-critical-and-high-severity-security-fixes-for-cve-2026-27876-and-cve-2026-27880/

相关推荐
掌勺者2 小时前
MySQL 事务简介
数据库·mysql
小码吃趴菜2 小时前
服务器预约系统linux小项目-第四节课
数据库·sql·mysql
七七powerful2 小时前
MySQL 8.0 性能优化利器:Percona Toolkit 实战指南
数据库·mysql·性能优化
渣渣盟5 小时前
Flink Table API与SQL流数据处理实战
大数据·sql·flink·scala
成为大佬先秃头11 小时前
数据库连接池:Druid
数据库·mysql·druid
晓华-warm14 小时前
Warm-Flow 1.8.5 正式发布:超时自动审批、暂存功能来了!
数据库
何中应14 小时前
Grafana如何重置密码
linux·运维·服务器·grafana
u01368638215 小时前
将Python Web应用部署到服务器(Docker + Nginx)
jvm·数据库·python
light blue bird15 小时前
多页签Razor组支轴业务整顿组件
数据库·.net·ai大数据·多功能图表报表·web mvc + razor