GuardDuty finding format をざっくり読み解く

記事タイトルとURLをコピーする

こんにちは。自称ソフトウェアエンジニアの橋本 (@hassaku_63)です。

今回の記事は GuardDuty が検出した結果を表現する Finding のデータフォーマットを読み解いていこうと思います。

GuardDuty のマネジメントコンソールではサンプルの検出結果を一括生成してくれる機能があります。とりあえずこの機能を使って生成したデータを tsv に変換して眺めてみたのですが、ほぼ初見だった私にはさっぱりわかりませんでした。

私自身セキュリティ分野が門外漢なので...という事情もあるのですが、それだと話が進まないので、以下の公式ドキュメントを参照しつつ自分なりにイベントデータを解釈できる状態になるように、自分用のメモがてら調べたことをかいつまんでいきます。

この記事の主な出典は以下の公式ドキュメントです。これを読んで中身が理解できる人は、この記事を読む必要がありません。対象読者外です。

https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-format.html

docs.aws.amazon.com

前提

Finding の内訳次第で JSON の構造も変動します。個別のサービス(例えば EC2, S3, Kubenetes など)に踏み込む話はせず、共通プロパティと思われる部分の把握に焦点を当てます。

また、筆者自身は GuardDuty や GuardDuty が検出可能な対象リソース・サービスのすべてに関して、十分な知識や実務経験を有しているわけではありません。そのため本記事には誤った解釈に基づく説明が含まれる可能性があることをご承知ください。また、この記事は執筆時点 (2025/04/25) の情報をもとにしています。正確な情報を得る場合は、本記事ではなく各種一次情報や AWS サポートへの問い合わせ等の適切な方法でご確認をいただけると幸いです。

本記事のフィードバックについては @hassaku_63 までご連絡いただけると幸いです。可能な限り加筆・訂正します。

Example data

サンプルデータ生成で作らせた Finding から、EC2 に関するものをピックしました。後述のセクションをざっと眺めてから、改めてこのサンプルデータを眺めてみてください。

「種別」に相当する分類軸を把握するのであれば、おそらく $.type の値を見ると良いです。今回は代表的なコンピューティングサービスである EC2 に関する検出結果の一つである Backdoor:EC2/DenialOfService.Tcp を一例としてとりあげます。このイベント種別は、アカウント内の特定の EC2 インスタンスが、TCP プロトコルを使った外部サービスへの DoS 攻撃に利用されている可能性があることを示唆するものです。

ちなみに、$.Service.AdditionalInfo.Value に sample=true が入っているなら、それは AWS が生成したサンプルイベントであり、「本物の検出結果」ではないことを意味します。

{
  "AccountId": "111122223333",
  "Arn": "arn:aws:guardduty:us-west-2:111122223333:detector/eeca5fb122ae0416177ed5dbf5b52b1c/finding/edbe339b588a41adaf3d5c3ff620c350",
  "CreatedAt": "2025-02-01T05:29:19.512Z",
  "Description": "The EC2 instance i-99999999 is behaving in a manner that may indicate it is being used to perform a Denial of Service (DoS) attack using the TCP protocol.",
  "Id": "edbe339b588a41adaf3d5c3ff620c350",
  "Partition": "aws",
  "Region": "us-west-2",
  "Resource": {
    "InstanceDetails": {
      "AvailabilityZone": "generated-az-1a",
      "IamInstanceProfile": {
        "Arn": "arn:aws:iam::111122223333:instance-profile/generated",
        "Id": "GeneratedFindingInstanceProfileId"
      },
      "ImageDescription": "GeneratedFindingInstanceImageDescription",
      "ImageId": "ami-99999999",
      "InstanceId": "i-99999999",
      "InstanceState": "running",
      "InstanceType": "m3.xlarge",
      "OutpostArn": "arn:aws:outposts:us-west-2:123456789000:outpost/op-0fbc006e9abbc73c3",
      "LaunchTime": "2016-08-02T02:05:06.000Z",
      "NetworkInterfaces": [
        {
          "Ipv6Addresses": [],
          "NetworkInterfaceId": "eni-abcdef00",
          "PrivateDnsName": "GeneratedFindingPrivateDnsName1",
          "PrivateIpAddress": "10.0.0.1",
          "PrivateIpAddresses": [
            {
              "PrivateDnsName": "GeneratedFindingPrivateName1",
              "PrivateIpAddress": "10.0.0.1"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName2",
              "PrivateIpAddress": "10.0.0.2"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName3",
              "PrivateIpAddress": "10.0.0.3"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName4",
              "PrivateIpAddress": "10.0.0.4"
            }
          ],
          "PublicDnsName": "GeneratedFindingPublicDNSName1",
          "PublicIp": "198.51.100.1",
          "SecurityGroups": [
            {
              "GroupId": "GeneratedFindingSecurityId1",
              "GroupName": "GeneratedFindingSecurityGroupName1"
            },
            {
              "GroupId": "GeneratedFindingSecurityId2",
              "GroupName": "GeneratedFindingSecurityGroupName2"
            },
            {
              "GroupId": "GeneratedFindingSecurityId3",
              "GroupName": "GeneratedFindingSecurityGroupName3"
            },
            {
              "GroupId": "GeneratedFindingSecurityId4",
              "GroupName": "GeneratedFindingSecurityGroupName4"
            }
          ],
          "SubnetId": "GeneratedFindingSubnetId1",
          "VpcId": "vpc-generatedvpcid1"
        },
        {
          "Ipv6Addresses": [],
          "NetworkInterfaceId": "eni-abcdef01",
          "PrivateDnsName": "GeneratedFindingPrivateDnsName2",
          "PrivateIpAddress": "10.0.0.2",
          "PrivateIpAddresses": [
            {
              "PrivateDnsName": "GeneratedFindingPrivateName1",
              "PrivateIpAddress": "10.0.0.1"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName2",
              "PrivateIpAddress": "10.0.0.2"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName3",
              "PrivateIpAddress": "10.0.0.3"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName4",
              "PrivateIpAddress": "10.0.0.4"
            }
          ],
          "PublicDnsName": "GeneratedFindingPublicDNSName2",
          "PublicIp": "198.51.100.2",
          "SecurityGroups": [
            {
              "GroupId": "GeneratedFindingSecurityId1",
              "GroupName": "GeneratedFindingSecurityGroupName1"
            },
            {
              "GroupId": "GeneratedFindingSecurityId2",
              "GroupName": "GeneratedFindingSecurityGroupName2"
            },
            {
              "GroupId": "GeneratedFindingSecurityId3",
              "GroupName": "GeneratedFindingSecurityGroupName3"
            },
            {
              "GroupId": "GeneratedFindingSecurityId4",
              "GroupName": "GeneratedFindingSecurityGroupName4"
            }
          ],
          "SubnetId": "GeneratedFindingSubnetId2",
          "VpcId": "vpc-generatedvpcid2"
        },
        {
          "Ipv6Addresses": [],
          "NetworkInterfaceId": "eni-abcdef02",
          "PrivateDnsName": "GeneratedFindingPrivateDnsName3",
          "PrivateIpAddress": "10.0.0.3",
          "PrivateIpAddresses": [
            {
              "PrivateDnsName": "GeneratedFindingPrivateName1",
              "PrivateIpAddress": "10.0.0.1"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName2",
              "PrivateIpAddress": "10.0.0.2"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName3",
              "PrivateIpAddress": "10.0.0.3"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName4",
              "PrivateIpAddress": "10.0.0.4"
            }
          ],
          "PublicDnsName": "GeneratedFindingPublicDNSName3",
          "PublicIp": "198.51.100.3",
          "SecurityGroups": [
            {
              "GroupId": "GeneratedFindingSecurityId1",
              "GroupName": "GeneratedFindingSecurityGroupName1"
            },
            {
              "GroupId": "GeneratedFindingSecurityId2",
              "GroupName": "GeneratedFindingSecurityGroupName2"
            },
            {
              "GroupId": "GeneratedFindingSecurityId3",
              "GroupName": "GeneratedFindingSecurityGroupName3"
            },
            {
              "GroupId": "GeneratedFindingSecurityId4",
              "GroupName": "GeneratedFindingSecurityGroupName4"
            }
          ],
          "SubnetId": "GeneratedFindingSubnetId3",
          "VpcId": "vpc-generatedvpcid3"
        },
        {
          "Ipv6Addresses": [],
          "NetworkInterfaceId": "eni-abcdef03",
          "PrivateDnsName": "GeneratedFindingPrivateDnsName4",
          "PrivateIpAddress": "10.0.0.4",
          "PrivateIpAddresses": [
            {
              "PrivateDnsName": "GeneratedFindingPrivateName1",
              "PrivateIpAddress": "10.0.0.1"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName2",
              "PrivateIpAddress": "10.0.0.2"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName3",
              "PrivateIpAddress": "10.0.0.3"
            },
            {
              "PrivateDnsName": "GeneratedFindingPrivateName4",
              "PrivateIpAddress": "10.0.0.4"
            }
          ],
          "PublicDnsName": "GeneratedFindingPublicDNSName4",
          "PublicIp": "198.51.100.4",
          "SecurityGroups": [
            {
              "GroupId": "GeneratedFindingSecurityId1",
              "GroupName": "GeneratedFindingSecurityGroupName1"
            },
            {
              "GroupId": "GeneratedFindingSecurityId2",
              "GroupName": "GeneratedFindingSecurityGroupName2"
            },
            {
              "GroupId": "GeneratedFindingSecurityId3",
              "GroupName": "GeneratedFindingSecurityGroupName3"
            },
            {
              "GroupId": "GeneratedFindingSecurityId4",
              "GroupName": "GeneratedFindingSecurityGroupName4"
            }
          ],
          "SubnetId": "GeneratedFindingSubnetId4",
          "VpcId": "vpc-generatedvpcid4"
        }
      ],
      "ProductCodes": [
        {
          "Code": "GeneratedFindingProductCodeId1",
          "ProductType": "marketplace"
        },
        {
          "Code": "GeneratedFindingProductCodeId2",
          "ProductType": "marketplace"
        },
        {
          "Code": "GeneratedFindingProductCodeId3",
          "ProductType": "marketplace"
        },
        {
          "Code": "GeneratedFindingProductCodeId4",
          "ProductType": "marketplace"
        },
        {
          "Code": "GeneratedFindingProductCodeId5",
          "ProductType": "marketplace"
        }
      ],
      "Tags": [
        {
          "Key": "GeneratedFindingInstanceTag1",
          "Value": "GeneratedFindingInstanceValue1"
        },
        {
          "Key": "GeneratedFindingInstanceTag2",
          "Value": "GeneratedFindingInstanceTagValue2"
        },
        {
          "Key": "GeneratedFindingInstanceTag3",
          "Value": "GeneratedFindingInstanceTagValue3"
        },
        {
          "Key": "GeneratedFindingInstanceTag4",
          "Value": "GeneratedFindingInstanceTagValue4"
        },
        {
          "Key": "GeneratedFindingInstanceTag5",
          "Value": "GeneratedFindingInstanceTagValue5"
        },
        {
          "Key": "GeneratedFindingInstanceTag6",
          "Value": "GeneratedFindingInstanceTagValue6"
        },
        {
          "Key": "GeneratedFindingInstanceTag7",
          "Value": "GeneratedFindingInstanceTagValue7"
        },
        {
          "Key": "GeneratedFindingInstanceTag8",
          "Value": "GeneratedFindingInstanceTagValue8"
        },
        {
          "Key": "GeneratedFindingInstanceTag9",
          "Value": "GeneratedFindingInstanceTagValue9"
        }
      ]
    },
    "ResourceType": "Instance"
  },
  "SchemaVersion": "2.0",
  "Service": {
    "Action": {
      "ActionType": "NETWORK_CONNECTION",
      "NetworkConnectionAction": {
        "Blocked": false,
        "ConnectionDirection": "OUTBOUND",
        "LocalPortDetails": {
          "Port": 24198,
          "PortName": "Unknown"
        },
        "Protocol": "TCP",
        "LocalIpDetails": {
          "IpAddressV4": "10.0.0.23",
          "IpAddressV6": "1234:5678:90ab:cdef:1234:5678:90ab:cde1"
        },
        "LocalNetworkInterface": "eni-abcdef00",
        "RemoteIpDetails": {
          "City": {
            "CityName": "GeneratedFindingCityName"
          },
          "Country": {
            "CountryName": "GeneratedFindingCountryName"
          },
          "GeoLocation": {
            "Lat": 0,
            "Lon": 0
          },
          "IpAddressV4": "198.51.100.0",
          "IpAddressV6": "1234:5678:90ab:cdef:1234:5678:90ab:cde0",
          "Organization": {
            "Asn": "-1",
            "AsnOrg": "GeneratedFindingASNOrg",
            "Isp": "GeneratedFindingISP",
            "Org": "GeneratedFindingORG"
          }
        },
        "RemotePortDetails": {
          "Port": 80,
          "PortName": "HTTP"
        }
      }
    },
    "Archived": false,
    "Count": 2,
    "DetectorId": "eeca5fb122ae0416177ed5dbf5b52b1c",
    "EventFirstSeen": "2025-02-01T05:29:19.000Z",
    "EventLastSeen": "2025-04-24T08:30:43.000Z",
    "ResourceRole": "ACTOR",
    "ServiceName": "guardduty",
    "AdditionalInfo": {
      "Value": "{\"sample\":true}",
      "Type": "default"
    }
  },
  "Severity": 8,
  "Title": "The EC2 instance i-99999999 is behaving in a manner that may indicate it is being used to perform a Denial of Service (DoS) attack using the TCP protocol.",
  "Type": "Backdoor:EC2/DenialOfService.Tcp",
  "UpdatedAt": "2025-04-24T08:30:43.702Z"
}

$.type で表現されている主要属性

$.type で表現されている情報は、以下の複数の情報を組み合わせた複合的な属性であることが示されています。

ThreatPurpose:ResourceTypeAffected/ThreatFamilyName.DetectionMechanism!Artifact

上記でとりあげたサンプルイベントの type は Backdoor:EC2/DenialOfService.Tcp でした。上記のフォーマット規則に照らすと以下のような情報を持っていることがわかります。

Attribute Value
ThreatPurpose Backdoor
ResourceTypeAffected EC2
ThreatFamilyName DenialOfService
DetectionMechanism Tcp
Artifact なし

それぞれの属性の意味を見ていきましょう。

公式ドキュメントの和訳版をまるごと引用し、表形式に変換して以下に示します。この中でも、Artifact はオプショナルです。

なお、途中記述が不自然な箇所がありますが、これは公式ドキュメントの文章に準拠したものです。

Attribute Description
ThreatPurpose 攻撃型または潜在的な攻撃型の脅威の主な目的についての説明です。GuardDuty 脅威の目的の一覧表については、次のセクションを参照してください。
ResourceTypeAffected この検出結果でどの AWS リソースが攻撃対象の候補として特定されたかを示します。現在、GuardDuty は「GuardDuty のアクティブな検出結果タイプ」にリストされているリソースタイプの検出結果を生成できます。
ThreatFamilyName GuardDuty で検出された全体的な脅威または潜在的な悪意のあるアクティビティの説明です。例えば、NetworkPortUnusual の値は、GuardDuty の検出結果で識別された EC2 インスタンスに、識別された特定のリモートポートでの通信履歴がないことを示します。
DetectionMechanism どの GuardDuty が検出結果を検出した方法を説明します。これは、一般的な検出結果タイプの変動や、GuardDuty が特定のメカニズムを使用して検出した検出結果を示すために使用できます。例えば、Backdoor:EC2/DenialOfService.Tcp は、TCP 上でサービス拒否 (DoS) が検出されたことを示します。UDP バリアントは Backdoor:EC2/DenialOfService.Udp です。

.Custom の値は、GuardDuty がカスタム脅威リストに基づいて検出結果を検出したことを示します。詳細については、「信頼できる IP と 脅威リスト」を参照してください。
.Reputation の値は、GuardDuty がドメインレピュテーションスコアモデルを使用して検出結果を検出したことを示します。詳細については、「がクラウドの最大のセキュリティ脅威 AWS を追跡し、それらをシャットダウンするのに役立つ方法」を参照してください。
Artifact 悪意のあるアクティビティで使用されたツールが所有する特定のリソースを示します。例えば、検出結果タイプ CryptoCurrency:EC2/BitcoinTool.B!DNS の DNS は、Amazon EC2 インスタンスがビットコインに関連する既知のドメインと通信していることを示します。

GuardDuty によって脅威が検出されてしまった場合、初動としてはまず起きてしまった事象を概要レベルでも把握することが必要でしょう。対応方法を協議するためには対象の識別と発生している問題の特定が必要ですから、見るべき軸はおそらく次の4点です*1*2

  • 影響を受けた対象の特定
  • どのような攻撃を受けてしまったのか?
  • 攻撃を受けた結果、対象リソースがどうなってしまったのか?
  • 対象リソースが侵害され、不審な挙動をしている場合は、その挙動を示している「経路」あるいは「手段」に相当する情報

type の内訳である各属性は、これらを把握できるだけの情報量を持っています。素人なりにざっくり表現するのであれば、以下のように理解すると良いでしょう。

  • ThreatPurpose ... どのようなモチベーションの攻撃を受けたのか?(例示したイベントでは Backdoor に相当)
  • ResourceTypeAffected ... 攻撃対象とされた対象リソースの種類(インスタンスID のような、具体的なエンティティを示す情報ではない。また AWS サービスの名称であるとも限らない。例示したイベントでは EC2 に相当する)
  • ThreatFamilyName ... 攻撃を受けた結果として、どのような(脅威となる)挙動が検出されたのか(例示したイベントでは DenialOfService つまり DoS 攻撃に相当)
  • DetectionMechanism ... 脅威となる挙動は、どのような手段で検出されたのか(例示したイベントでは Tcp に相当)

ResourceTypeAffected が若干曲者です。カッコ内でも述べているように、ここの値は AWS サービスの名称とは限りません。例えば、Runtime のような値が入る場合があります。この値の場合、実際に影響を受けたリソースは EC2 インスタンス以外にも ECS/EKS のクラスタやコンテナである場合があります。そのへんの対応表は公式ドキュメントに説明がありますので、以下の表を確認すると良いでしょう。

docs.aws.amazon.com

ThreatPurpose

特に咀嚼しておきたい情報だと思ったので、ThreatPurpose 属性についてはもう少し掘り下げます。

ref: https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-format.html#guardduty_threat_purposes

検出された脅威の目的を示すもので、攻撃の種類、あるいは潜在的な攻撃の段階を示します。

Backdoor のような値は攻撃の種類を示しますが、Impact のような一部の値は MITRE ATT&CK tactics の定義に一致し、攻撃者が行う攻撃サイクルのなんらかのフェーズを示します。

ここで、MITRE ATT&CK の用語についても簡単に説明しておきます。
 "tactics" というのは攻撃者が戦略的に達成したいゴールがなんなのかを説明するものです。"technique", "sub-technique" はそのゴールを達成するために攻撃者が取る方法を説明するための用語で、"technique" はゴールを達成するための手段 (how) で、"sub-technique" はその詳細にあたります。"tactics" はそれらの手段 (how) に対応する why がなんなのか?ということの説明にあたります。詳しくは以下のドキュメントもあわせて参照いただくと良いでしょう。

attack.mitre.org

attack.mitre.org

ここまでの情報や単語から推察するに、GuardDuty の "Threat Purpose" は、MITRE ATT&CK で言うところの "tactics" とほぼ同じニュアンスの情報を表現しようとしている属性である、と理解できそうです。

執筆時点で GuardDuty がサポートする値は次の通りです。ここでは候補値だけを羅列しています。この中で MITRE ATT&CK tactics の定義に基づいていると明言されている項目は冒頭に (★) を付けました。それぞれの解説は各自公式情報を見ていただくようお願いします。もしかすると別で後日解説記事を出すかもしれません。

  • Backdoor
  • Behavior
  • (★) CredentialAccess
  • Cryptocurrency
  • (★) DefenseEvasion
  • (★) Discovery
  • (★) Execution
  • (★) Exfiltration
  • (★) Impact
  • (★) InitialAccess
  • Pentest
  • (★) Persistence
  • Policy
  • (★) PrivilegeEscalation
  • Recon
  • Stealth
  • Trojan
  • UnauthorizedAccess

Purpose の種類によっては、現在見ている Finding で特定された対象リソースから連鎖して、別のリソース(あるいは CloudTrail ログ)を確認するなどの派生的なアクションが必要になる場合があります。このことから、私たちが実務で検出された GuardDuty Finding に対処するにあたっては、ある1個の Finding を見て対象リソースが判明したからといって、私たちが見るべき・対処すべき箇所がそのリソースだけに限られるわけではない、という認識を持つ必要があります。

この認識の正しさを説明する一例としては、"Persistence" がわかりやすいです。公式ドキュメントにも記載があるように、この Purpose が検出されるケース想定の中には、「IAM User が侵害された場合に、攻撃者が窃取したユーザーの権限を用いて、侵害の経路を継続的に確保するために別の IAM User を作成する」といった行動をした場合が挙げられています。このようなケースでは、検出された Finding が直接示している対象リソースを見るだけでは対応としておそらく不十分でしょう。不審な IAM User や Role が作られていないか、実際にどのような API コールが行われていたのか、などを総なめで点検する必要も出てくるかと思います。

こうした具体例からも、Finding の中身をしっかり人間が解釈して、その対応方法を検出対象リソースに縛られず判断していく必要がある(場合がある)、ということは言えそうです。そして、その対応の緊急度や濃淡も、影響を受けるシステムが自社にとってどれほど重要であるかの位置付けによって大きく変動しうるわけで。簡単に「自動化」したり「安価な外注先に委託」できるような、単純な話ではなさそうですね。結局は稼働するシステムや発生したインシデントが、自社にとってどのようなインパクトを持つのかで対応の事情が変わるわけで、本質的なところは当事者たる人間による判断が必要になるということです*3。セキュリティの実務の複雑さ・難しさを感じますね。

まとめ

イベントフォーマットの中で、なんとなく見る価値が高そうな情報は絞れてきたかな?という感覚です。MITRE ATT&CK はこれまで名前だけ把握していたのですが、これまでロクにキャッチアップできていないのが実情でした。この機会に、ざらっとでも(せめて GaurdDuty ドキュメントからリンクされている情報源は)眺めてみようかなと思いました。

当社の人間としては、クラウド (AWS) の専門家として、お客様をよりよいクラウドジャーニーに導いていける高品質のサービスを、確固たる技術力で提供していきたいと考えています。私も当社のサービス提供の当事者であり、キャッチアップを兼ねて今回のような記事を書くに至ったのですが、まあ、やはりセキュリティ分野の基礎教養に関する勉強不足は否めないなと思いました。AWS サービスを使うかどうかにかかわらず、その基礎となる背景を知らねば、実際の現場で起こる問題をどう捉え、どう動くべきか?を判断・提案することは難しいですね。

引き続きやっていきたいと思います。

*1:この4点に加えて、GaurdDuty が付与してくれる severity の値や title, description のテキストも非常に重要な情報源となるでしょう。ただ、これらの値はほぼ誰でも最初に見る場所でしょうし、解釈も容易なプロパティであると思いますので説明は省きます

*2:この辺は本来経済産業省や日本 CSIRT 協議会などが出している情報を根拠としたいところですが、今回は GaurdDuty のイベント仕様が何を言っているのか俯瞰したい狙いの記事であるため、そのあたりの裏取りを後回しにしています

*3:と、私なりには整理しています

橋本 拓弥(記事一覧)

マネージドサービス部

内製開発中心にやってます。普段はサーバーレス関連や CDK を触ることが多いです