JNDI注入工具改造

发布者:SecIN
发布于:2022-11-17 12:48

源码链接

(修改前的源码,因为feihong师傅的仓库已经不在了,这里使用的是Jeromeyoung师傅的源码进行改造)91fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6v1k6i4u0G2L8h3g2&6L8%4g2F1k6#2)9J5c8V1A6z5c8p5W2q4P5s2m8D9L8$3W2@1i4K6u0V1x3b7`.`.

0x00 JNDI注入工具代码结构分析

controllers模块:负责LDAP请求的处理

enum模块:负责存储各种模板类型名称,如:反序列化的Gadget、内存马的类型

异常模块:负责处理可能抛出的异常

反序列化模块:各种反序列化链的处理

模板模块:命令执行、回显、DNS、内存马模板

工具类模块:主要是为了方便而编写的一些工具类

协议服务及启动模块:负责LDAP、HTTP协议的具体实现

下面先对启动及协议服务模块进行说明

com.feihong.ldap.Starter类为整个工具的启动入口,从命令行接收参数传参到com.feihong.ldap.utils.Config类

Config类中的@Parameter注解为参数的说明及代表的意思,类似于py中的argparse,命令接收到参数,分别赋值给Config类的几个私有变量。

然后在之后的HTTPServer与LDAPServer中得到应用

关于LDAP服务端的编写和Demo可以参考以下链接:584K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2X3M7X3g2W2j5Y4g2X3i4K6u0W2j5$3!0E0i4K6u0r3N6Y4g2D9M7#2)9J5c8U0t1#2x3K6f1@1y4g2)9J5k6h3S2@1L8h3H3`.,HTTPServer则是正常的Java Demo编写即可

第一部分注解的应用

在JNDIEXP中,作者为了可以反射一种类型的类,Controller类,这里的类起到了寻找特定类的作用,而且每个注解类都给定了uri属性

在LDAPServer.start()之前,会先通过new Reflections(this.getClass().getPackage().getName())的方法获取到com.feihong.ldap包下面所有LdapMapping的类,之后将其以键值对(TreeMap<String, LdapController>)放入到Map中,以便于后续调用

比如com.feihong.ldap.controllers.BasicController类,在Map中的存储格式就是(basic=>Object BasicController)

根据发送来的LDAP请求去决定调用哪个类,具体通过com.feihong.ldap.processSearchResult

根据工具运行的实际效果,如果我们的LDAP请求为ldap://192.168.85.1:1389/Basic/123

那么DN为Basic/123,首先根据DN中的开头字符串决定是哪个Controller来处理当前的LDAP的请求

第二部分服务端动态调用类

在LDAP调用LdapController接口实现类之后(以BasicController为例),会先调用process方法,以/为标志分割,获取到相应的模块名称,比如ldap://0.0.0.0:1389/Basic/Command/whoami,那么第一部分Basic用来指定是BasicController,Command指定执行BasicController下的命令执行模块

如果是命令执行模块

通过com.feihong.ldap.utils.Util的getCmdFromBase方法获取到执行命令的内容,如果是base编码的,进行base64解码后返回内容

将其赋值给params,之后再调用BasicController的sendResult方法,如果是command模块,初始化该模块,通过asm码的方法,这里为了避免出现类名重复的情况,使用随机字符命令类名

之后命令执行模块调用cache方法

将其存储在map中,之后开始进行LDAP的步骤

首先LDAP设定好

javaClassName:记录序列化对象的类名,这样应用程序就可以确定类信息,而不必首先反序列化

javaClassNames:关于序列化对象的附加类信息。

javaCodebase:实例化工厂所需的类定义的位置(HTTP地址)

javaFactory:用于存储对象工厂的完全限定类的可选属性(即类名)

一张图说明LDAP请求的过程

wKg0C2I4Qs6AAM4QAABoz1qOh2I020.png

接下来看HTTPServer如何接收HTTP请求并返回

当HTTPServer接收到请求之后,将Cache类中map存储的类写入到响应中,这样避免了class文件落地的情况。

0x01 去除server console内容

在这个JNDI的注入工具中,会在注入内存马的时候,使用系统输出语句,在控制台打印出东西,无关紧要的东西,直接去掉就好。

0x02 冰蝎3.11内存马注入

改造前的filter类,获取session是通过ServletRequest的方式获取的

改造之后的filter类,不再使用自写的classLoader而是直接使用URLClassLoader,同理对其他的组件也进行类似的改造,如:spring的Interceptor,weblogic和jboss的filter

所有回显使用的是header头的WWW-Authenticate字段,而非cmd

0x03 JDK版本匹配及tomcat版本兼容解决

利用JDK向下兼容的特性,这里使用JDK6编译filter class。

tomcat版本兼容,根据先知不久之前提出的,tomcat6-9的通用StandardContext获取方法,获取到StandardContext,接下来的问题就是解决tomcat版本识别与不同版本间filter注入的问题。

tomcat8以下版本FilterDef、FilterMap都在org.apache.catalina.deploy包里,tomcat8以上包括8都在org.apache.tomcat.util.descriptor.web里。

2.识别版本之后,进行不同版本间的filter注入

实现逻辑大概如下:

对各个版本的filter内存马全部转为字节码Base64的编码格式,通过loadClass的办法,根据版本注入到内存中。(class一律使用JDK1.6编译)

为了避免相关类加载不到的问题,这里统一使用反射写所有的filter注入步骤。

tomcat6789的StandardContext先知上已经给出了解决方案:

9c6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5P5W2)9J5k6h3q4D9K9i4W2#2L8W2)9J5k6h3y4G2L8g2)9J5c8Y4c8Q4x3V1j5&6z5e0p5@1

对tomcat特定类进行筛选,来决定FilterDef和FilterMap类加载的使用,最后形成的Demo如下:

getStand的源码如下:

经测试,可用于常规的tomcat6、7(某凌)、8、9版本。

当所有的tomcat版本都测试完毕之后,发现这个通用方法对某远并不适用,原因是致远这里的ServerPort会永远等于-1。

解决方案是使用原版的feihong-jndi将TomcatMemShell2中的filter类替换为自定义的filter类即可。

0x04 spring memShell的改进

在一些版本的测试中,发现原版的JNDIExploit的spring并不适用于所有的spring框架场景,对此做出补充(借鉴自lz2y与microworld师傅的成果)

以ruoyi cms后台的snake yaml为例,lz2y师傅这里已经说了具体的原因(addK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5P5W2)9J5k6h3q4D9K9i4W2#2L8W2)9J5k6h3y4G2L8g2)9J5c8Y4c8Q4x3V1j5I4x3o6j5#2x3b7`.`.),直接使用师傅的Demo就好,我这里使用的是Windows的环境,使用比较在Linux和Windows都通用的Demo获取的WebApplicationContext

使用com.ruoyi.common.utils.spring.SpringUtils在windows ruoyi4.6.0的环境中没有成功(POC使用的是com.sun.rowset.JdbcRowSetImpl)

使用com.ruoyi.common.utils.spring.SpringUtils在windows ruoyi4.6.0的环境中成功(POC使用的是javax.script.ScriptEngineManager)

剩下的步骤直接注入内存马即可,实现Demo如下:

将其添加到类的引用处,对于spring的框架添加新增类的引用,需要修改的是四个类:

com.feihong.ldap.enumtypes.PayloadType

com.feihong.ldap.utils.Cache

com.feihong.ldap.controllers.BasicController

com.feihong.ldap.controllers.TomcatBypassController

修改Cache类是为了能够让恶意类能够在内存中加载,具体的原因已经在0.1中分析完毕,修改BasicController和TomcatBypassController是为了能在LDAP在JDK的高低版本中找到引用。

0x05 jetty memShell的改进

对于jetty内存马的改进,主要是log4j对solr的影响,针对于实战的场景,参考Qu3een师傅的文章(bdbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1N6s2c8S2L8X3N6Q4x3X3g2U0L8$3#2Q4x3V1k6S2M7X3y4Z5K9i4k6W2i4K6u0r3x3e0x3^5y4W2)9J5c8R3`.`.

实现的Demo如下,直接集成到JNDI注入工具中

修改com.feihong.ldap.enumtypes.PayloadType

修改com.feihong.ldap.utils.Cache

修改com.feihong.ldap.controllers.BasicController

0x06 webSphere memShell的改进

暂缺

0x07 增加resin中间件下的内存马注入

基于pen4uin师傅的通用研究成果直接集成到JNDI注入工具中,原理不再赘述,参考pen4uin师傅的公众号即可,直接给出Demo

同样的,修改com.feihong.ldap.enumtypes.PayloadType

修改com.feihong.ldap.utils.Cache

修改com.feihong.ldap.controllers.BasicController

0x08 增加tomcatValue内存马

在tomcat容器中,有些场景filter内存马并不如valve内存马,所以为了增加兼容性,这里增加valve内存马,实现的Demo大致如下:

同样的修改配置文件,增加到BasicController、TomcatBypassController、Cache、Config中。

参考链接

324K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9j5h3&6V1k6%4u0W2P5g2)9J5k6h3#2W2i4K6u0r3j5X3I4G2k6#2)9J5c8U0p5&6i4K6u0r3

03eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6y4M7W2)9J5k6s2S2F1i4K6u0r3d9V1&6p5d9f1g2^5M7r3I4G2K9i4c8Q4x3X3b7I4

fb7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6@1N6s2c8S2L8X3N6Q4x3X3g2U0L8$3#2Q4x3V1k6S2M7X3y4Z5K9i4k6W2i4K6u0r3x3e0x3^5y4W2)9J5c8R3`.`.

dbeK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9J5c8U0g2K6z5h3g2&6f1Y4m8S2f1o6N6i4K9q4k6V1x3X3g2K6N6s2N6@1d9$3M7`.

8e5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6D9P5U0u0&6i4K6u0r3P5h3q4E0L8q4)9J5k6s2m8S2P5h3I4G2j5h3c8Q4x3X3c8X3L8%4u0Q4x3X3c8J5N6h3!0&6K9b7`.`.

480K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5P5W2)9J5k6h3q4D9K9i4W2#2L8W2)9J5k6h3y4G2L8g2)9J5c8Y4c8Q4x3V1j5I4x3o6j5#2x3b7`.`.



声明:该文观点仅代表作者本人,转载请注明来自看雪