Compact
true
担当領域:セキュリティ技術センター リスクハンティング・アナリシスグループ
高性能計算(HPC)分野のソフトウェア開発業務を経て、現在はペネトレーションテスト、脆弱性診断などに従事。SANS SEC575 メダル保持。CISSP/情報処理安全確保支援士(RISS)/OffSec(OSCP,OSWA,OSDA,OSWP)/CHFI/情報処理技術者試験(NW,DB,ES)/GIAC(GREM,GMOB)/AWS認定(SAA,SCS)を保持。Hack The Box - Guruランク。

NECサイバーセキュリティ戦略統括部 セキュリティ技術センターの岩川、S田(ハンドルネーム)、田中です。

XZ Utilsに対してバックドアが仕掛けられたとして、2024年3月の報告[1]から大きな話題となり様々なところで取り上げられました。本記事では、今回仕掛けられたバックドアから、今後同様のバックドアが仕掛けれられた場合にどのように検知するかに焦点を当てて考察をします。

XZ Utilsのバックドアの概要

XZ Utilsでは、xzそしてlzmaフォーマットをサポートするデータ圧縮ライブラリliblzmaとコマンドラインツールを提供します[2]。
2024年3月29日に、不正に変更された liblzma ライブラリが生成されliblzmaライブラリをリンクするソフトウェアが影響を受けるとしてCVE-2024-3094[3]が発行されました。
XZ Utils 5.6.0(2024-02-24)そして5.6.1(2024-03-09) release tarballに、不正な共同メンテナーによって仕掛けられたバックドアが含まれることが、通知されています[4]。
本バックドアを悪用することで攻撃者が sshd 認証を突破してリモートからのシステムに不正アクセスを可能にする可能性があります[5]。

バックドアの挙動と検知について

本節ではバックドアの検知においてポイントとなるXZ Utilsのバックドアの挙動と検知方法について考察をしていきます。

ビルドプロセス

ビルドプロセスにおいて、build-to-host.m4ファイルとbad-3-corrupt_lzma2.xzに仕掛けられた不正なコードを起点とし最終的にバックドアを持つオブジェクトファイルが生成されliblzma ライブラリにリンクされることでバックドアが混入します。バックドアを混入させるフローは他の記事ですでに詳しく解説がされています[6]。本節では検知するためにポイントとなる挙動をピックアップします。

ビルドする際には、まずconfigureスクリプトを実行します。configureを実行すると、処理の中でbuild-to-host.m4が実行され、テストファイルであるbad-3-corrupt_lzma2.xzから不正なスクリプトが抽出されます。

バックドアが仕掛けられた場合(xz-utils_5.6.1.orig.tar.xz)とバックドアが仕掛けられていない場合(xz-utils_5.6.1+really5.4.5.orig.tar.xz)のビルドにおいて、テストファイルへのアクセスについて監視して比較を実施しました。snapshot.debian.org[7]からバックドアが仕掛けられたソースコードは手に入ります。テストファイルへのアクセスは、inotify[8]を使い監視を実施しました。

$ tar xJf xz-utils_5.6.1+really5.4.5.orig.tar.xz
$ cd xz-5.4.5/
# configure実行前にinotifywaitを実行した状態にする
$ ./configure

# configure実行後のinotifywaitの出力
$ cd xz-5.4.5/
$ inotifywait -m tests/files/
Setting up watches.
Watches established.


$ tar xJf xz-utils_5.6.1.orig.tar.xz
$ cd xz-5.6.1/
# configure実行前にinotifywaitを実行した状態にする
$ ./configure

# configure実行後のinotifywaitの出力
$ inotifywait -m tests/files/
Setting up watches.
Watches established.
(省略)
tests/files/ OPEN bad-3-corrupt_lzma2.xz
tests/files/ ACCESS bad-3-corrupt_lzma2.xz
tests/files/ CLOSE_NOWRITE,CLOSE bad-3-corrupt_lzma2.xz
tests/files/ OPEN unsupported-1-v234.lz
tests/files/ ACCESS unsupported-1-v234.lz
tests/files/ CLOSE_NOWRITE,CLOSE unsupported-1-v234.lz
tests/files/ OPEN bad-1-v1-magic-2.lz
tests/files/ ACCESS bad-1-v1-magic-2.lz
tests/files/ CLOSE_NOWRITE,CLOSE bad-1-v1-magic-2.lz
(省略)
1
1

バックドアが仕掛けられた場合(xz-utils_5.6.1.orig.tar.xz)はconfigureスクリプトを実行した際にテストファイルへのアクセスが複数見られますが、バックドアが仕掛けられていない場合(xz-utils_5.6.1+really5.4.5.orig.tar.xz)はアクセスが発生していません。このことから、ビルドプロセスにおいてどのファイルにアクセスするかはバックドアを検知する一つのチェック項目となると考えることができます。ビルドにおいて必要のないファイルにアクセスする場合は不審な点として検知ができます。検知とは異なる観点となりますが、ビルドに必要ないファイルは除いた環境でビルドを実施することで今回のようなバックドアの混入は防げたと考えられます。

sshdの応答時間

バックドアが仕掛けられたことにより、sshの応答時間が遅くなります。報告者はマイクロベンチマークを実施していた際に、sshdプロセスのCPUリソースの消費量から異常に気付きバックドアの発見に至ったと述べています[9]。

簡易的にバックドアが仕掛けられたライブラリと仕掛けられていないライブラリで比較を実施しました。snapshot.debian.org[7]からバックドアが仕掛けられたライブラリが含まれるパッケージは手に入ります。

検証はkali-linux-2024.2の仮想マシン[10]上で実施しました。以下のようにliblzma5_5.6.1-1_amd64.deb(バックドア有)とliblzma-dev_5.6.1+really5.4.5-1_amd64.deb(バックドア無)をそれぞれインストールして、インストール後にhyperfine[11]を使いsshの実行時間の1000回の平均値を計測しました。

$ sudo apt install -y --allow-downgrades ./liblzma5_5.6.1+really5.4.5-1_amd64.deb
$ sudo systemctl restart ssh
$ hyperfine -i --runs 1000 "ssh -o PasswordAuthentication=no root@192.168.124.128"
Benchmark 1: ssh -o PasswordAuthentication=no root@192.168.124.128
  Time (mean ± σ):     137.5 ms ±   2.1 ms    [User: 57.0 ms, System: 3.6 ms]
  Range (min … max):   133.4 ms … 144.1 ms    1000 runs

  Warning: Ignoring non-zero exit code.

$ sudo apt install -y --allow-downgrades ./liblzma5_5.6.1-1_amd64.deb
$ sudo systemctl restart ssh
$ hyperfine -i --runs 1000 "ssh -o PasswordAuthentication=no root@192.168.124.128"
Benchmark 1: ssh -o PasswordAuthentication=no root@192.168.124.128
  Time (mean ± σ):     319.8 ms ±   2.1 ms    [User: 56.9 ms, System: 3.6 ms]
  Range (min … max):   314.0 ms … 331.7 ms    1000 runs

  Warning: Ignoring non-zero exit code.
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
1
1

不要なサービスを停止するなどしていないため厳密な比較・計測ではありませんが、バックドアを仕掛けられた場合は319.8ms、仕掛けられていない場合は137.5msとなり約180ms遅くなることが確認出来ました。
このことから応答時間・実行時間の変化はバックドアを検知する一つのチェック項目となると考えることができます。正規の修正による実行時間の変化はありますが、マイナーアップデートで大きな修正はしていないはずなのに実行時間が大きく変化した場合は注意をする必要があります。

valgrindのエラー

バックドアが仕掛けられたことにより、解析ツールvalgrind[12]においてエラーが出力されるようになります。XZ Utils 5.6.0のliblzmaライブラリの_get_cpuid関数においてエラーが発生します。_get_cpuid関数はバックドアのエントリに該当します。XZ Utils 5.6.1では攻撃者により、攻撃コードを更新されてエラーが発生しないように修正されています[13]。

snapshot.debian.org[7]からliblzma5_5.6.0-0.1_amd64.debとliblzma5_5.6.1-1_amd64.debを入手してそれぞれインストールして、インストール後にvalgrindをsshdに対して実行してみました。

$ sudo apt install -y --allow-downgrades ./liblzma5_5.6.0-0.1_amd64.deb
$ valgrind /sbin/sshd
==43552== Memcheck, a memory error detector
==43552== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==43552== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info
==43552== Command: /sbin/sshd
==43552==
==43552== Invalid write of size 8
==43552==    at 0x5476D45: ??? (in /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.0)
==43552==    by 0x545981B: ??? (in /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.0)
==43552==    by 0x6A00000006: ???
==43552==    by 0x542D53F: ???
==43552==    by 0xCC9AEC43AAC1AFFF: ???
==43552==    by 0x1FFEFFFAAF: ???
==43552==    by 0x400F072: elf_machine_rela (dl-machine.h:300)
==43552==    by 0x400F072: elf_dynamic_do_Rela (do-rel.h:147)
==43552==    by 0x400F072: _dl_relocate_object (dl-reloc.c:301)
==43552==    by 0x56740AF: ??? (in /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.34.0)
==43552==    by 0x56740AF: ??? (in /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.34.0)
==43552==    by 0x569CCEF: ???
==43552==    by 0x1FFEFFFA3F: ???
==43552==    by 0x561C28F: ???
==43552==  Address 0x1ffeffeb38 is on thread 1's stack
==43552==  136 bytes below stack pointer
==43552==
sshd: no hostkeys available -- exiting.
==43552==
==43552== HEAP SUMMARY:
==43552==     in use at exit: 14,731 bytes in 158 blocks
==43552==   total heap usage: 7,591 allocs, 7,433 frees, 943,333 bytes allocated
==43552==
==43552== LEAK SUMMARY:
==43552==    definitely lost: 837 bytes in 4 blocks
==43552==    indirectly lost: 5,735 bytes in 99 blocks
==43552==      possibly lost: 0 bytes in 0 blocks
==43552==    still reachable: 8,159 bytes in 55 blocks
==43552==         suppressed: 0 bytes in 0 blocks
==43552== Rerun with --leak-check=full to see details of leaked memory
==43552==
==43552== For lists of detected and suppressed errors, rerun with: -s
==43552== ERROR SUMMARY: 112 errors from 1 contexts (suppressed: 0 from 0)

$ sudo apt install -y --allow-downgrades ./liblzma5_5.6.1-1_amd64.deb
$ valgrind /sbin/sshd
==44127== Memcheck, a memory error detector
==44127== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==44127== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info
==44127== Command: /sbin/sshd
==44127==
sshd: no hostkeys available -- exiting.
==44127==
==44127== HEAP SUMMARY:
==44127==     in use at exit: 14,731 bytes in 158 blocks
==44127==   total heap usage: 7,591 allocs, 7,433 frees, 943,333 bytes allocated
==44127==
==44127== LEAK SUMMARY:
==44127==    definitely lost: 837 bytes in 4 blocks
==44127==    indirectly lost: 5,735 bytes in 99 blocks
==44127==      possibly lost: 0 bytes in 0 blocks
==44127==    still reachable: 8,159 bytes in 55 blocks
==44127==         suppressed: 0 bytes in 0 blocks
==44127== Rerun with --leak-check=full to see details of leaked memory
==44127==
==44127== For lists of detected and suppressed errors, rerun with: -s
==44127== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
1
1

5.6.0においてはエラーが発生して、5.6.1ではエラーが発生しないことが確認できました。
valgrindでエラーが発生しないかはバックドアを検知するための一つのチェック項目となると考えることができます。5.6.1では修正されましたが5.6.0ではエラー発生していたため異常を検知するためのひとつのきっかけになると考えられます。

GOTエントリの上書き

Linuxサーバーはsshdプロセスを稼働させ、SSHコネクションを受け付けます。XZ Utilsバックドアが混入したsshdプログラムでは、共有ライブラリであるlibcrypto.soに含まれる正規の関数の呼び出しを置き換え、不正な関数を呼び出すことで、攻撃者は認証を回避し、任意のコマンド実行が可能となります。具体的には、下記3つの関数の呼び出しを置き換えています[14]。本節では、このうちRSA_public_decrypt関数について取り上げます。

以降の説明では、バイナリにシンボル情報がなく関数名が定義されていないものに関しては、説明の便宜上、独自に関数名を定義して説明をしているのでご注意ください。

正規のRSA_public_decrypt関数は共有ライブラリであるlibcrypto.soに含まれる関数で、sshdはlibcrypto.soを動的にリンクしています。今回のバックドアにおいて実装された不正な処理は共有ライブラリliblzma.soに含まれており、sshdはliblzma.soとも動的にリンクしています。

ライブラリ関数の呼び出しをハイジャックするには、GOT(Global Offsets Table)のエントリを書き換えるGOT Overwriteという手法が有力です。ライブラリ関数の呼び出しに際して参照されるGOTのエントリに存在する正常なアドレス値を不正なアドレス値に書き換えることで、不正に関数を呼び出すことができます。

しかしながら、多くの場合にLinuxの実行ファイル(ELFファイル)はRelocation Read-Only(RELRO)機構[15]によりGOT Overwrite攻撃から守られています。RELRO機構にはいくつかの保護レベルが存在し、最も強力なFull RELRO環境下の場合、GOT領域はプログラム実行時に書き込みが不可となるため、一般にはGOT Overwrite攻撃の実現は困難になります。

checksec[16]によりRELRO機構のどの保護レベルが有効となっているかを確認できます。

$ checksec /usr/sbin/sshd
[*] '/usr/sbin/sshd'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled
1
1

XZ Utilsバックドアは、GNU indirect function(ifunc)機構[17]とRun-time Dynamic Linker Audit(rtld-audit)機構[18]を活用することにより、Full RELRO環境下のプログラムバイナリに対してもGOT Overwrite攻撃を実現したと考えられます。

GNU indirect function(ifunc)機構[17]は、同一の関数に複数の実装を用意しておき、実際に使用する実装をプログラムのロード時に決定する機能です。これにより、例えばCPUアーキテクチャの違いなど、環境ごとにバイナリを作成する必要がなくなります。使用する実装を決定する処理はresolverと呼ばれ、プログラムのロード時に実行されます。

Run-time Dynamic Linker Audit(rtld-audit)[18]は、GNU dynamic linker(ld-linux.so)が提供する機構です。この機構を利用することで、実行中のプロセスに対し、共有ライブラリのロードやシンボル解決などの動的リンクに関するイベントを監視および操作できます。

Full RELRO環境下の実行ファイルに対してもGOT Overwrite攻撃を実現するため、今回のバックドアは上述の2つの機構を活用し、sshdのGOT領域がread-onlyになる前にRSA_public_decrypt関数のGOTエントリを上書きし、不正なevil_RSA_public_decrypt関数を指すようにしています。

この攻撃手法を説明する前に、実行ファイルにifuncが存在する場合、そしてFull RELRO環境における動的リンカ(ld-linux.so)による処理の順序について簡単に説明します。下図は処理の概要図となります。

50

処理の概要は次の通りです。

(1) 実行ファイルおよび共有ライブラリをメモリ上にロードする。
(2) 共有ライブラリにifuncが含まれる場合、ifunc resolverを実行する。
(3) 実行ファイルと共有ライブラリのシンボルを即時バインドする。
(4) 実行ファイルのGOT領域をread-onlyに設定し、不正な上書きから保護する。

Full RELRO環境下では、プログラム実行時にGOT領域をread-onlyに設定する必要があるため、動的リンカは遅延バインドではなく、プログラムのロード時に即時バインド処理を実施します。また、共有ライブラリにifunc 属性を持つ関数が含まれている場合は、動的リンカはどの実装を使うのかをバインド処理前に決定しておく必要があるため、はじめにifunc resolverを実行します。
resolverの処理が完了し、ifunc属性を持つ関数に対して実際に使用する実装が決定すると、動的リンカは共有ライブラリの関数シンボルとアドレスのバインド処理を行ない、GOTエントリを埋め、最終的にGOT領域をread-onlyに設定します。

そして今回のバックドアは、全体として下記のような手順でGOT Overwrite攻撃を実現しています。

(1) sshdがロードされ、共有ライブラリliblzma.so に含まれるifunc resolverが実行される。
(2) ifunc resolver処理において、liblzma.so のGOTエントリを一時的に上書きし、cpuid関数の代わりに不正なevil_cpuid関数を呼び出す。
(3) evil_cpuid関数は、動的リンカld-linux.soに含まれるrtld-auditのフック関数la_symbind64に後述の(5)の処理を含むrtld_hook_main関数を登録する。
(4) 動的リンカによりsshdと共有ライブラリのシンボルが即時バインドされ、la_symbind64に設定されているrtld_hook_main関数が呼ばれる。
(5) sshdのGOT領域にあるRSA_public_decrypt関数のエントリに書かれた正常なアドレス値を、evil_RSA_public_decrypt関数を指すアドレス値に書き換える。

上記手順の詳細な処理内容を以下で説明します。

(1)ifunc resolverの実行
今回のバックドアが含まれるliblzma.soには、lzma_crc32関数とlzma_crc64関数がifunc属性を持ち、resolverを利用しています。

$ readelf -Ws liblzma.so.5.6.0 | grep IFUNC
   104: 0000000000006bf0 114 IFUNC GLOBAL DEFAULT 13 lzma_crc32@@XZ_5.0
   106: 0000000000006ff0 114 IFUNC GLOBAL DEFAULT 13 lzma_crc64@@XZ_5.0
1
1

(2)ifunc resolverからevil_cpuid関数の不正な呼び出し
XZ Utilsバックドアはその処理においてさまざまな難読化、ステルス化を施しています。その1つとして、ifunc resolverの中で、本来の__get_cpuid関数呼び出しが不正な_get_cpuid関数呼び出しに置き換えられています。この不正な_get_cpuid関数はliblzma_la-crc64-fast.oに含まれており、このオブジェクトファイルはビルド時にliblzma.soに静的にリンクされます。
ここで注意すべきは、ifunc resolverの実行時点ではGOT領域がまだread-onlyになっていない点です。_get_cpuid関数からの一連の処理において、cpuid関数呼び出しの直前に一時的にcpuid関数のGOTエントリを上書きし、cpuid関数の代わりにevil_cpuid関数を不正に呼び出します。下記は、liblzma.soのGOT領域で、cpuid関数のエントリが書き換えられていることを計測したものになります。

// liblzma.soのGOTエントリ(書き換え前)
$ (gdb) x/8x 0x7f5c57a35fd8
0x7f5c57a35fd8: 0xf0  0xe6  0x9f  0x57  0x5c  0x7f  0x00  0x00
 → 0x7f5c579fe6f0 : cpuid関数のアドレスを指す

// liblzma.soのGOTエントリ(書き換え後)
$ (gdb) x/8x 0x7f5c57a35fd8
0x7f5c57a35fd8: 0x50  0xbf  0xa1  0x57  0x5c  0x7f  0x00  0x00
 → 0x7f5c57a1bf50 : evil_cpuid関数のアドレスを指す
1
1

(3)rtld-auditのフック関数la_symbind64にrtld_hook_main関数を登録
evil_cpuid関数からの一連の処理において、動的リンカld-linux.soに含まれるrtld-audit機能の1つであるla_symbind64にrtld_hook_main関数を設定します。これにより、動的リンカが共有ライブラリ関数のシンボルのバインド処理を実施する度に、監査処理であるrtld_hook_main関数が呼び出されることになります。

下図はここまでの処理の動作概要図となります。

50

(4)動的リンカによるシンボルの即時バインド
先に説明した通り、Full RELRO環境下では、実行時にGOT領域をread-onlyに設定する必要があるため、動的リンカはifuncの解決が終わるとシンボルの即時バインド処理を実施します。そしてこのバインド処理において、rtld-audit機能の1つであるla_symbind64に設定されているrtld_hook_main関数が呼ばれることになります。

(5)rtld_hook_main関数はRSA_public_decryptのGOTエントリを上書きする
rtld_hook_main関数は、動的リンカによりRSA_public_decrypt関数のシンボルがバインドされた場合に、sshdのGOT領域にあるRSA_public_decrypt関数のエントリを不正なevil_RSA_public_decrypt関数のアドレスに上書きします。
動的リンカによりすべてのシンボルのバインド処理が完了したのち、sshdのGOT領域はread-onlyに設定されます。以後、sshdによるRSA_public_decrypt関数の呼び出しは、実際にはevil_RSA_public_decrypt関数が呼び出されることになります。

下図は(4),(5)の処理を加えた全体の動作概要図となります。

50

なお、ifunc resolverからの一連の処理において、sshdのGOTエントリを書き換えたとしても、その後の動的リンカによるシンボルの即時バインド処理により、正規のRSA_public_decrypt関数のアドレスに上書きされてしまいます。つまり、GOTエントリの書き換えは、動的リンカによるシンボルの即時バインド処理が完了した直後からGOT領域がread-onlyに設定される前までに実施する必要があるため、XZ Utilsバックドアはrtld-audit機能を利用したと推測されます[19]。

Full RELRO環境下でのGOT Overwrite攻撃は、今回のバックドアで実装された手法のように処理が複雑な一方で、実現できる手法は限られます。Full RELRO機構はプログラム実行時に実行ファイルのGOT領域をread-onlyにするため、攻撃者はGOT領域がread-onlyに設定される前に標的のGOTエントリを書き換えておく必要があります。したがって、実行時にGOTエントリが不正な値を指していないかを確認することが有効な検知の方法となります。
実際にデバッガを用いて、バックドアが混入していないsshdと、バックドアが混入しているsshdに対して、RSA_public_decrypt関数を示すはずのGOTエントリがどのような値になっているのかを確認してみます。

// sshdのGOTエントリ(正規版)
$ (gdb) x/100gx 0x00005646d022a0b0
(省略)
  0x5646d022a208 <socket@got.plt>:               0x00007f09a0b28f70
  0x5646d022a210 <RSA_public_decrypt@got.plt>:  0x00007f09a0f47850
  0x5646d022a218 <fork@got.plt>:                 0x00007f09a0af5560
(省略)
// RSA_public_decryptエントリが指す関数のアセンブリを確認
$ (gdb) x/100bi 0x00007f09a0f47850
  0x7f09a0f47850 <RSA_public_decrypt  >: endbr64
  0x7f09a0f47854 <RSA_public_decrypt+4>: mov  0x18(%rcx), %rax
  0x7f09a0f47858 <RSA_public_decrypt+8>: jmp  *0x10(%rax)
→ RSA_public_decrypt関数(thunk関数)を確認

// sshdのGOTエントリ(バックドア混入版)
$ (gdb) x/100gx 0x000055f0ee32f0c0
(省略)
  0x55f0ee32f210 <socket@got.plt>:               0x00007ff6d8328b70
  0x55f0ee32f218 <RSA_public_decrypt@got.plt>:  0x00007ff6d8975a90
  0x55f0ee32f220 <fork@got.plt>:                 0x00007ff6d82f1420
(省略)
// RSA_public_decryptエントリが指す関数のアセンブリを確認
(gdb) x/100bi 0x00007ff6d8975a90
  0x7ff6d8975a90:  endbr64
  0x7ff6d8975a94:  push  %r14
  0x7ff6d8975a96:  push  %r13
  0x7ff6d8975a98:  push  %r12
  0x7ff6d8975a9a:  mov   %rsi, %r12
  0x7ff6d8975a9d:  push  %rbp
  0x7ff6d8975a9e:  sub   $0x28, %rsp
  0x7ff6d8975aa2:  mov   0x25577(%rip), %rsi  # 0x7ff6d899b020
  0x7ff6d8975aa9:  test  %rsi, %rsi
  0x7ff6d8975aac:  je    0x7ff6d8975b0f
(省略)
→ RSA_public_decrypt関数でない関数(evil_RSA_public_decrypt関数)を確認
1
1

上記から、GOTエントリはバックドアが仕掛けられていない場合はRSA_public_decrypt関数を指しますが、バックドアが仕掛けられている場合はRSA_public_decrypt関数を指していないことが確認できました。このことからGOTに格納されているアドレスが正規の関数を指しているかを確認することはバックドアを検知する一つのチェック項目になると考えることができます。

まとめ

本ブログで紹介したXZ Utilsの挙動と検知するためのチェック項目をまとめると以下になります。

項番
1
1
挙動の概要
1
1
チェック項目
1
1
1
1
1
ビルドプロセスにおいてテストファイルから攻撃コードが抽出される。
1
1
inotifyコマンド等でビルドプロセス時のファイルへのアクセスを監視。ビルドプロセスにおいて不要なファイルにアクセスしないかを確認
1
1
2
1
1
プログラムの実行時間が増加する。
1
1
hyperfineコマンド等で実行時間を計測。実行時間に大きな変化がないかを確認
1
1
3
1
1
Valgrind による検査でエラーが発生する
1
1
Valgrind によりプログラムを検査。エラーが発生する場合はエラー発生箇所を確認
1
1
4
1
1
プログラム実行時にGOTに格納されているアドレスが不正なアドレスに上書きされる
1
1
gdbを使いGOTに格納されているアドレスを確認。不正なアドレスが登録されていないかを確認。
1
1

XZ Utilsのバックドアは、検知を回避するために非常に複雑で高度な方法を使用していました。このような攻撃は今後も発生する可能性があるため今回得られた知見をセキュリティ検査や開発時のCI/CDでのチェック項目に含めることでバックドアの検知に活かしていくことが重要と考えます。

参考文献

[1] oss-security - backdoor in upstream xz/liblzma leading to ssh server compromise (openwall.com)
https://www.openwall.com/lists/oss-security/2024/03/29/4

[2] GitHub - tukaani-project/xz: XZ Utils
https://github.com/tukaani-project/xz

[3] NVD - CVE-2024-3094 (nist.gov)
https://nvd.nist.gov/vuln/detail/CVE-2024-3094

[4] XZ Utils (tukaani.org)
https://tukaani.org/xz/#_introduction

[5] Urgent security alert for Fedora 41 and Fedora Rawhide users (redhat.com)
https://www.redhat.com/en/blog/urgent-security-alert-fedora-41-and-rawhide-users

[6] research!rsc: The xz attack shell script (swtch.com)
https://research.swtch.com/xz-script

[7] xz-utils - snapshot.debian.org
https://snapshot.debian.org/package/xz-utils/

[8] GitHub - inotify-tools/inotify-tools: inotify-tools is a C library and a set of command-line programs providing a simple interface to inotify.
https://github.com/inotify-tools/inotify-tools

[9] AndresFreundTec: "I was doing some micro-benchma…" - Mastodon
https://mastodon.social/@AndresFreundTec/112180406142695845

[10] Get Kali | Kali Linux
https://cdimage.kali.org/kali-2024.2/kali-linux-2024.2-vmware-amd64.7z

[11] GitHub - sharkdp/hyperfine: A command-line benchmarking tool
https://github.com/sharkdp/hyperfine

[12] Valgrind Home
https://valgrind.org/

[13] git.tukaani.org - xz.git/commitdiff
https://git.tukaani.org/?p=xz.git;a=commitdiff;h=74b138d2a6529f2c07729d7c77b1725a8e8b16f1

[14] Kaspersky analysis of the backdoor in XZ | Securelist
https://securelist.com/xz-backdoor-story-part-1/112354/

[15] Hardening ELF binaries using Relocation Read-Only (RELRO)
https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro

[16] GitHub - slimm609/checksec.sh: Checksec.sh
https://github.com/slimm609/checksec.sh

[17] Common Function Attributes (Using the GNU Compiler Collection (GCC))
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html

[18] rtld-audit(7) - Linux manual page
https://man7.org/linux/man-pages/man7/rtld-audit.7.html

[19] Deep Dive into XZ Utils Backdoor - Columbia Engineering, Advanced Systems Programming Guest Lecture – YouTube
https://www.youtube.com/watch?v=Q6ovtLdSbEA

執筆者プロフィール

執筆者
original
50

岩川 健人(いわかわ けんと)
セキュリティ技術センター リスクハンティング・アナリシスグループ

高性能計算(HPC)分野のソフトウェア開発業務を経て、現在はペネトレーションテスト、脆弱性診断などに従事。
SANS SEC575 メダル保持。
CISSP/情報処理安全確保支援士(RISS)/OffSec(OSCP,OSWA,OSDA,OSWP)/CHFI/情報処理技術者試験(NW,DB,ES)/GIAC(GREM,GMOB)/AWS認定(SAA,SCS)を保持。
Hack The Box - Guruランク。

執筆者
original
50

S田(えすだ)※ハンドルネーム
セキュリティ技術センター リスクハンティング・アナリシスグループ

ソフトウェアの不正機能検査技術の研究開発業務を経て、現在はペネトレーションテスト、脆弱性診断、ソフトウェア検査などに従事。

執筆者
original
50

田中 大樹(たなか だいき)
セキュリティ技術センター リスクハンティング・アナリシスグループ

ソフトウェアの不正機能検査システムの研究開発業務を経て、現在はペネトレーションテスト、脆弱性診断、ソフトウェア検査などに従事。