群里大佬们打哈哈的内容,菜鸡拿出来整理学习一下,炒点冷饭。

主要包含以下三个部分:

  1. jndi注入原理
  2. jndi注入与反序列化
  3. jndi注入与jdk版本

jndi注入原理:

JNDI(Java Name and Dictionary Interface Java名称与目录接口),一套JavaEE的标准,类似Windows注册表。

结构如下:

key:路径+名称
value:存的数据(在jndi中存的是对象Object)

jndi是java用于访问目录和命名服务的 API。使用jndi进行查询本来是一个正常的功能,但由于实现时没有考虑安全问题,如果查询了恶意对象就会导致被攻击。但是不是所有攻击都能够导致RCE(比如dnslog2333)

JNDI查询分为以下两个步骤:

1、客户端请求一个命名服务并获取对象

2、客户端解析这个对象

这两个步骤都有可能导致漏洞的发生,jndi支持LDAP、RMI、DNS、CORBA四种可能导致危险的协议,每种都对应了不同的实现方式,支持绑定的对象也包含了引用对象、反序列化对象、属性对象等等,所以攻击手段和漏洞都很多。

最常用也是最危险的攻击有jndi+rmi和jndi+ldap,corba也可以用来命令执行(但是修复得比较早,而且用corba的基本都可以用rmi)

JNDI+RMI

关键代码位于RegistryContext#lookup

public Object lookup(Name name) throws NamingException {
if (name.isEmpty()) {
return (new RegistryContext(this));
}
Remote obj;
try {
obj = registry.lookup(name.get(0));
//这里可以看到远程对象是通过rmi原生的lookup获取到的
} catch (NotBoundException e) {
throw (new NameNotFoundException(name.get(0)));
} catch (RemoteException e) {
throw (NamingException)wrapRemoteException(e).fillInStackTrace();
}
return (decodeObject(obj, name.getPrefix(1)));

可以看到远程对象是通过rmi原生的lookup获取到的,而rmi是通过反序列化获取到的远程对象,这时如果客户端系统里有gadget组件,这一步的反序列化就能导致代码执行了。

第二步,在decodeObject里面对获取到的obj进行了解析,逻辑位于RegistryContext#decodeObject

private Object decodeObject(Remote r, Name name) throws NamingException {
try {
Object obj = (r instanceof RemoteReference)
? ((RemoteReference)r).getReference(): (Object)r;
/*
* Classes may only be loaded from an arbitrary URL codebase when
* the system property com.sun.jndi.rmi.object.trustURLCodebase
* has been set to "true".
*/
//这里注释写得很清楚
// Use reference if possible
Reference ref = null;
if (obj instanceof Reference) {
ref = (Reference) obj;
} else if (obj instanceof Referenceable) {
ref = ((Referenceable)(obj)).getReference();
}
if (ref != null && ref.getFactoryClassLocation() != null &&
!trustURLCodebase) {
throw new ConfigurationException(
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
}
return NamingManager.getObjectInstance(obj, name, this,
environment);

注释注明了,如果com.sun.jndi.rmi.object.trustURLCodebase为true就可以通过codebase加载

任意远程类,导致代码执行。这个校验是在jdk8u121开启的,并且是加在RegistryContext里面的,也就是只对了jndi的rmi实现作了限制,所以安全人员后续才会发掘出ldap的利用。

Object obj = (r instanceof RemoteReference) ? ((RemoteReference)r).getReference(): (Object)r;

这段代码判断传入的对象,是否满足RemoteReference接口,如果有则getReference()获取reference对象,然后进入getObjectInstance函数。

具体利用流程如下:

1、目标代码中调用了InitialContext.lookup(URI),URI为用户可控的;

2、攻击者设置uri为恶意rmi服务地址;

3、攻击者设置rmi server向目标返回一个reference引用对象,reference对象中指定了一个精心构造的Factory类;

4、目标进行lookup操作远程对象时,获取到动态加载并实例化了这个Factory类,接着调用factory.getObjectInstance()加载外部远程对象实例;

5、攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到RCE的效果;

getObjectInstance 主函数当中使用此类的getInstance()函数,即可得到系统当前已经实例化的该类对象,若当前系统还没有实例化过这个类的对象,则调用此类的构造函数。

可以引出后续的两种命令执行利用方式:

if (ref != null) {
            String f = ref.getFactoryClassName();
            if (f != null) {
                // if reference identifies a factory, use exclusively
 
 
 
 
                factory = getObjectFactoryFromReference(ref, f); //触发点1
                if (factory != null) {
                    return factory.getObjectInstance(ref, name, nameCtx,
                                                     environment); //触发点2
                }
                // No factory found, so return original refInfo.
                // Will reach this point if factory class is not in
                // class path and reference does not contain a URL for it
                return refInfo;

第一种是getObjectFactoryFromReference(),在这个函数中会通过获取到对应的恶意class对象,在开启trustURLCodebase时可以通过URLClassloader加载远程类并进行实例化,通过class.newInstance()触发恶意构造函数:

return (clas != null) ? (ObjectFactory) clas.newInstance() : null;

第二种是通过实例化的类,调用起getObjectInstance函数,来执行恶意代码:

只要攻击者实现ObjectFactory接口,重写getObjectInstance,即可执行恶意代码。

public class Exec implements ObjectFactory {
    public Exec(){}
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        System.out.println("factory.getObjectInstance hook!");
        return null;
    }
}

综上所述,RMI的RCE利用从某种意义上说并不是利用反序列化导致的代码执行,只是利用反序列化来加载恶意远程对象。

JNDI+LDAP

核心逻辑在ldapCtx#c_lookup

protected Object c_lookup(Name name, Continuation cont)
throws NamingException {
cont.setError(this, name);
Object obj = null;
Attributes attrs;
try {
......
if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
// 序列化对象或序列化引用
obj = Obj.decodeObject(attrs);
}
if (obj == null) {
obj = new LdapCtx(this, fullyQualifiedName(name));
}
} catch (LdapReferralException e) {
......
try {
return DirectoryManager.getObjectInstance(obj, name,
this, envprops, attrs);
......
}

只看关键代码,这里主要分为两个步骤:

首先通过Obj.deocodeObject从ldap获取字符串,解码出一个ldap对象,然后通过DirectoryManager.getObjectInstance解析,这里和RMI是一样的逻辑,只是没有rmi关于trusrURLCodebase的校验。

所以jndi+ldap获取对象的方式和rmi差不多,都是通过反序列化获取的。然后解析对象调用的是DirectoryManager.getObjectInstance,其实和NamingManager.getObjectInstance基本是一样的。decodeRefernce,原生反序列化,但是如果com.sun.jndi.ldap.object.trustURLCodebase开启,会调一个重写的resolveClass进行远程类加载。

1、解析对象时调用getObjectFactoryFromReference,在开启com.sun.jndi.ldap.object.trustURLCodebase时进行远程类加载

2、和rmi一样用本地工厂类,但ldap服务端不能像rmi一样直接绑远程对象,需要绑序列化后的数据。

JNDI注入与jdk版本

jdk针对jndi注入的利用有两次修复,8u121对RMI和corba的jndi注入进行限制,com.sun.jndi.ldap.object.trustURLCodebase 限制了这两种服务加载远程工厂类。

8u191禁用了ldap的远程类加载。

至此,高版本可用的jndi注入还有:加载本地工厂类,打本地反序列化链,

前者是tomcat8/9才引入的,后者需要本地反序列化链。

总结:

1、jndi注入的原理

一般说的jndi注入原理是远程类加载。其他攻击方法还有本地工厂类代码执行、反序列化。

2、jndi注入与反序列化

jndi注入依赖反序列化来传递对象,但常说的jndi注入代码执行并不是由反序列化链导致的。同样jndi注入也可以转化成通常说的反序列化攻击。

3、jndi注入与jdk升级

jdk升级只能修复jndi远程类加载的攻击方式,高版本依然有加载本地工厂类和反序列化本地利用链的攻击方式。

Java反序列化中jndi注入的高版本jdk绕过的更多相关文章

  1. Java安全之JNDI注入

    Java安全之JNDI注入 文章首发:Java安全之JNDI注入 0x00 前言 续上篇文内容,接着来学习JNDI注入相关知识.JNDI注入是Fastjson反序列化漏洞中的攻击手法之一. 0x01 ...

  2. 从高版本JDK换成低版本JDK报错Unsupported major.minor version 52.0的解决方案

    从高版本JDK换成低版本JDK报错Unsupported major.minor version 52.0 java.lang.UnsupportedClassVersionError: PR/Sor ...

  3. 解决:高版本jdk编译低版本代码时eclipse提示Access restriction:The type 'Unsafe' is not accessible due to restriction on required library

    在Eclipse中采用高版本jdk编译一些低版本的源码时,由于源码中使用了一些高版本中过时的API,可能就会报错,类似于: Access restriction:The type 'Unsafe' i ...

  4. 解决eclipse高版本JDK编译的项目到低版本JDK服务器上不能运行的问题

    错误提示信息:Unsupported major.minor version 52.0,意思是说,当前jdk的版本不支持更高版本jdk编译出来的class文件. 我的编译环境,eclipse使用的是j ...

  5. 从高版本JDK换成低版本JDK报错Unsupported major.minor version 52.0

    ava.lang.UnsupportedClassVersionError: PR/Sort : Unsupported major.minor version 52.0这个错误是由于高版本的java ...

  6. 【转】ubuntu 配置 java jdk1.8 环境,增加多版本 jdk 和切换方法

    一.安装java jdk1.8 1.添加软件源 sudo add-apt-repository ppa:webupd8team/java 2.更新软件源 sudo apt-get update 3.安 ...

  7. 一篇博客带你轻松应对java面试中的多线程与高并发

    1. Java线程的创建方式 (1)继承thread类 thread类本质是实现了runnable接口的一个实例,代表线程的一个实例.启动线程的方式start方法.start是一个本地方法,执行后,执 ...

  8. Java Spring 中你不知道的注入方式

    前言 在Spring配置文件中使用XML文件进行配置,实际上是让Spring执行了相应的代码,例如: 使用<bean>元素,实际上是让Spring执行无参或有参构造器 使用<prop ...

  9. Java Spring-Bean中属性注入

    2017-11-06 20:29:13 类属性的注入的三种方法 1.接口方法注入 public interface injection{ public void setName(String name ...

随机推荐

  1. Redis架构之哨兵机制与集群

    Redis架构之哨兵机制与集群 哨兵机制 1.介绍: Sentinel(哨兵)是redis高可用性解决方案:由一个或多个由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个 ...

  2. 网站制作工具之EditPlus的使用

    这里分享网站制作教程所使用到的软件,我个人开发使用的是EditPlus和Dreamweaver这两款软件.在百度搜索一下这两个软件,安装好后就可以使用了. EditPlus的使用方法 EditPlus ...

  3. 关于python文件写入问题

    第一种.用for循环不断打开文件写入关闭 测试代码数据如下: import time begin = time.perf_counter() def a(f, lis): f.write(lis + ...

  4. Docker实用篇

    Docker实用篇 0.学习目标 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦. 分布式系统中,依赖的组件非常多,不同组件之间 ...

  5. VS Code C++ 代码格式化(clang-format)

    --- # 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto Language: Cpp # BasedOnStyle ...

  6. KingbaseES 时间类型数据和oracle时间类型的区别

    Oracle日期时间类型有两类,一类是日期时间类型,包括Date, Timestamp with time zone, Timestamp with local time zone.另一类是Inter ...

  7. Taurus.MVC 微服务框架 入门开发教程:项目集成:3、客户端:其它编程语言项目集成:Java集成应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  8. R语言-tidyr和dplyr

    一.安装和加载 1.安装并加载tidyr和dplyr包 install.packages("tidyr") library(tidyr) install.packages(&quo ...

  9. Pytest fixture及conftest详解

    前言 fixture是在测试函数运行前后,由pytest执行的外壳函数.fixture中的代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集.配置测试前系统的初始状态.为批量测试提供数据源等 ...

  10. MasaFramework的MinimalAPI设计

    在以前的MVC引用程序中,控制器负责接收输入信息.执行.编排操作并返回响应,它是一个功能齐全的框架,它提供了过滤器.内置了模型绑定与验证,并提供了很多可扩展的管道,但它偏重,不像其它语言是通过更加简洁 ...