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

接下来看HTTPServer如何接收HTTP请求并返回
当HTTPServer接收到请求之后,将Cache类中map存储的类写入到响应中,这样避免了class文件落地的情况。
在这个JNDI的注入工具中,会在注入内存马的时候,使用系统输出语句,在控制台打印出东西,无关紧要的东西,直接去掉就好。

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

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

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

利用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先知上已经给出了解决方案:
对tomcat特定类进行筛选,来决定FilterDef和FilterMap类加载的使用,最后形成的Demo如下:




getStand的源码如下:



经测试,可用于常规的tomcat6、7(某凌)、8、9版本。
当所有的tomcat版本都测试完毕之后,发现这个通用方法对某远并不适用,原因是致远这里的ServerPort会永远等于-1。
解决方案是使用原版的feihong-jndi将TomcatMemShell2中的filter类替换为自定义的filter类即可。
在一些版本的测试中,发现原版的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的高低版本中找到引用。
对于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

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


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

修改com.feihong.ldap.utils.Cache

修改com.feihong.ldap.controllers.BasicController


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





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