一、漏洞背景
1.JNDI的基本应用
JNDI是Java Naming and Directory Interface(JAVA命名和目录接口)的英文简写,它是为JAVA应用程序提供命名和目录访问服务的API(Application Programing Interface,应用程序编程接口)。
2.命名的概念与应用
JNDI中的命名(Naming),就是将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。在真实的项目应用中,通常是由系统程序或框加程序先将资源对象绑定到JNDI环境中,以后在该系统或框架中运行的模块程序就可以从JNDI环境中查找这些资源对象了。例如,Tomcat服务器在启动时可以创建一个连接到某种数据库系统的数据源(DataSource)对象,并将该数据源(DataSource)对象绑定到JNDI环境中,以后在这个Tomcat服务器中运行的Servlet和JSP程序就可以从JNDI环境中查询出这个数据源(DataSource)对象进行使用,而不用关心数据源(DataSource)对象是如何创建出来的,这种方式极大地增强了系统的可维护性,当数据库系统的连接参数发生变更时,这只是Tomcat系统管理员一个人要关心的事情,而与所有的应用程序开发人员无关。
容器环境(Context)本身也是一个Java对象,它也可以通过一个名称绑定到另一个容器环境(Context)中。将一个Context对象绑定到另外一个Context对象中,这就形成了一种父子级联关系,多个Context对象最终可以级联成一种树状结构,树中的每个Context对象中都可以绑定若干个Java对象
二、漏洞分析
1.环境搭建
靶场采用 https://github.com/QAX-A-Team/WeblogicEnvironment 一键速成
工具采用 https://github.com/welk1n/JNDI-Injection-Exploit/blob/master/README-CN.md
准备好Burpsuite和IDEA就可以愉快的进行远程调试了~
2.漏洞调试
在public JndiBindingHandle(String objectIdentifier) 和 public JndiBindingHandle(String context, String binding, String server)中可以看到一些初始化操作。
返回newInstance(args) 是实例化
此时key的值是POC中的_nfpb,
当_nfpb不满足结尾是handle的条件,来到检测他的下一位是否满足结尾是handle的条件。
此时,_pageLable也不满足,则继续检查下一位key JNDIBindingPageGeneral.
这里满足了结尾是handle为结尾的条件
往下走,这里根据request请求的参数生产handle对象,通过(Handle)ConvertUtils.convert(HttpParsing.unescape((String)queryMap.get(key), enc), Handle.class);
ObjectType在这里需要被过滤
每个handle都有类型,但是这里objectType 不是null 且 handle被实例化所以这里其实是没做过滤的,我们的JNDIBindingPortlethandle的值就被存进handle了。所以这个方法中就可以在为handle结尾的key中传入任意的值。
在handle不为空的情况下进去看,
在这里取第0位的Component,” ldap://x.x.x”
出来在getBinding做了判断,当context不为空的时候,返回context + . +getBinding() 的值,而getBinding()取的是getComponent(1); 出来以后 ,name 的值就有了
来到JNDIBindingAction.execute的代码,当serverMBean不为空的时候,lookup函数可以被利用。
这里的context, biding, server连起来拼成了ldap://x.x.x.x/classname 的地址,servername则是 AdminServer。ser
ServerName 在这里就可以建立Connect,比如RMI连接
c不为空的时候,c会去查找context 拼接’.’拼接bindName,原本POC里面的 ; 在这里被替换成了点
进入上面提到的lookup函数后,有一个run方法,其中TreeNode childNode = new TreeNode(name + “Node”, name, this.contextPath + “/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=” + bindingHandle, root);
进入后在toString()方法中生成字符串做了格式转换,可以放到URL中。
将特殊符号 %做转换
得出来的treeNode 为/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle%28%22ejb.mgmt%3BMEJB%3BAdminServer%22%29,
urldeocde完为/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle(“ejb.mgmt;MEJB;AdminServer”) 将生成好的treenode加到列表里
在setComponents(String[] components)可以看到sb 的取值由context, binding,server构成,而在一系列append后,sb的取值最终变成context;binding;server的结构,也就是ldap://x.x.x;x/class;AdminServer
3.漏洞复现