原创 | JNDI注入工具改造
点击蓝字
关注我们
(修改前的源码,因为feihong师傅的仓库已经不在了,这里使用的是Jeromeyoung师傅的源码进行改造)
https://github.com/Jeromeyoung/JNDIExploit-1
JNDI注入工具代码结构分析
1.controllers模块:负责LDAP请求的处理
2.enum模块:负责存储各种模板类型名称,如:反序列化的Gadget、内存马的类型
3.异常模块:负责处理可能抛出的异常
4.反序列化模块:各种反序列化链的处理
5.模板模块:命令执行、回显、DNS、内存马模板
6.工具类模块:主要是为了方便而编写的一些工具类
7.协议服务及启动模块:负责LDAP、HTTP协议的具体实现
8.下面先对启动及协议服务模块进行说明:
com.feihong.ldap.Starter类为整个工具的启动入口,从命令行接收参数传参到com.feihong.ldap.utils.Config类
Config类中的@Parameter注解为参数的说明及代表的意思,类似于py中的argparse,命令接收到参数,分别赋值给Config类的几个私有变量。
然后在之后的HTTPServer与LDAPServer中得到应用
关于LDAP服务端的编写和Demo可以参考以下链接:https://www.freebuf.com/vuls/253545.html,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文件落地的情况。
去除server console内容
在这个JNDI的注入工具中,会在注入内存马的时候,使用系统输出语句,在控制台打印出东西,无关紧要的东西,直接去掉就好。
冰蝎3.11内存马注入
改造前的filter类,获取session是通过ServletRequest的方式获取的
改造之后的filter类,不再使用自写的classLoader而是直接使用URLClassLoader,同理对其他的组件也进行类似的改造,如:spring的Interceptor,weblogic和jboss的filter
所有回显使用的是header头的WWW-Authenticate字段,而非cmd
1.利用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先知上已经给出了解决方案:https://xz.aliyun.com/t/9914
对tomcat特定类进行筛选,来决定FilterDef和FilterMap类加载的使用,最后形成的Demo如下:
getStand的源码如下:
经测试,可用于常规的tomcat6、7(某凌)、8、9版本。
当所有的tomcat版本都测试完毕之后,发现这个通用方法对某远并不适用,原因是致远这里的ServerPort会永远等于-1。
解决方案是使用原版的feihong-jndi将TomcatMemShell2中的filter类替换为自定义的filter类即可。
spring memShell的改进
在一些版本的测试中,发现原版的JNDIExploit的spring并不适用于所有的spring框架场景,对此做出补充(借鉴自lz2y与microworld师傅的成果)
以ruoyi cms后台的snake yaml为例,lz2y师傅这里已经说了具体的原因(https://xz.aliyun.com/t/10651),直接使用师傅的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 memShell的改进
对于jetty内存马的改进,主要是log4j对solr的影响,针对于实战的场景,参考Qu3een师傅的文章(https://tttang.com/archive/1386/)
实现的Demo如下,直接集成到JNDI注入工具中
修改com.feihong.ldap.utils.Cache
修改com.feihong.ldap.controllers.BasicController
font
修改com.feihong.ldap.enumtypes.PayloadType
修改com.feihong.ldap.utils.Cache
修改com.feihong.ldap.controllers.BasicController
增加tomcatValue内存马
在tomcat容器中,有些场景filter内存马并不如valve内存马,所以为了增加兼容性,这里增加valve内存马,实现的Demo大致如下:
同样的修改配置文件,增加到BasicController、TomcatBypassController、Cache、Config中。
参考链接
https://landgrey.me/blog/19/
https://github.com/Mr-xn/JNDIExploit-1
https://tttang.com/archive/1386/
https://mp.weixin.qq.com/s/5s9eyRpaP7WhVd2estwtKg
https://github.com/lz2y/yaml-payload-for-ruoyi
https://xz.aliyun.com/t/10651
往期推荐