Dome9 ShiftLeft (Preview) を試した~アプリもIaCテンプレートもコンテナイメージもスキャンする~

2020.12.13

中山です

SaaSを使っていると、いきなり新機能が使えるようになるってありますよね。

ある日、Dome9にログインしたら見慣れないアイコンが・・・

開くとこんな感じでした。

見たところ、以下の対象のセキュリティスキャンを実行するコマンドラインツールのようです。

  • アプリケーションソースコード
  • IaCテンプレート
  • コンテナイメージ

なお、CI/CDパイプラインに組み込んで利用することを想定しているようです。

この機能についてはGitHubのパブリックリポジトリでも概要が紹介されていますので、気になる方は覗いてみてください。

dome9/shiftleft

製品ドキュメントにも機能の説明が記載されています。

CloudGuard Dome9 Help

ということで、早速使ってみました。

この記事を執筆している2020年12月13日時点で、この機能はプレビューとして提供されています。一般提供開始時には異なる名称や機能として提供される場合があります。

やってみた

最初にご紹介したウィザードから続けてやっていきます。 なお、検証はCloud9の環境(Amazon Linux 2)で行いますが、WindowsおよびMacもサポートしています。

ツールのダウンロード

まず、ツールをダウンロードして実行できるようにします。 (ウィザードで表示された手順から少し修正してます)

wget https://shiftleft-prod.s3.amazonaws.com/blades/shiftleft/bin/linux/amd64/0.0.29/shiftleft
chmod +x shiftleft
mkdir ~/bin
mv shiftleft ~/bin/

また、Dome9のAPIキーを発行して環境変数に設定します。

export CHKP_CLOUDGUARD_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export CHKP_CLOUDGUARD_SECRET=xxxxxxxxxxxxxxxxxxxx

最後にバージョンだけ確認します。

shiftleft --version
0.0.29

これで準備完了です。

この感じであればCodeBuildでの利用も簡単そうですね。

ツールのインストール手順はこちらでも確認できます。

Installing shiftleft

アプリケーションコードの分析

最初にアプリケーションコードのスキャンを実行してみたいと思います。

ちなみに、shiftleftの各機能は「ブレード」と呼ばれ、どのブレードを利用するかはサブコマンドで指定します。 現在は3つですが、今後増えていくのでしょうか?(わくわく)

アプリケーションを用意する

まずは、スキャン対象のアプリケーションを用意します。

今回はこちらのやられアプリを使ってみました。

yahoo / webseclab

git clone https://github.com/yahoo/webseclab.git

分析

ブレードの利用方法はこんな感じです。

$ shiftleft code-scan -h
NAME
        code-scan

DESCRIPTION
        source-code/image security and visibility into the risk analysis of projects

SYNOPSIS
        code-scan [OPTIONS] [ -s <source_folder> | -i <image_path> ]

OPTIONS

  -s, -src, --source <string>           path to source code directory
  -i, -img, --image <string>            path to docker image tar format
  -t, -T, --timeout <int>               scan timeout in seconds (default: 600 for source code, 900 for docker image)
  -x, -X, --exclude <string>            path to exclude from scanning (.gitignore syntax)
  -d, -D, --debug                       debug output
  -j, -J, --json                        json output
  -nc, -NC, --no-cache                  do not take results from previous scans
  -np, -NP, --no-proxy                  do not use system proxy settings
  -nb, -NB, --no-blame                  do not use Git blame on scanned files
  -v, -V, --version                     show version
  -h, -H, --help                        show help

EXAMPLE
        code-scan -x tests/** -x static/** -s /home/project
        code-scan -t 900 -i /home/image.tar

で、結果がこちら。

$ shiftleft code-scan -s webseclab/
INFO   [08-12-2020 15:57:34.424] SourceGuard Scan Started!                    
INFO   [08-12-2020 15:57:35.087] Project name: webseclab path: webseclab      
INFO   [08-12-2020 15:57:35.087] Scan ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx
INFO   [08-12-2020 15:57:48.462] Scanning ...                                 
INFO   [08-12-2020 15:57:55.562] Analyzing ...                                
Action: BLOCK
Code Findings:
        - ID: 50000000-0000-0000-0000-000000000024
          Name: "hardcoded_ip"
          Description: "Hardcoded IP addresses is vulnerable to Denial of Service attack on the service at this address or spoof the IP address. Such an attack is always possible, but in the case of a hardcoded IP address the fix will be much slower, which will increase an attack's impact. make the IP address configurable."
                - SHA: cc88a39b3b39aca4926108cfb5492f3c1c1278085b2ca2875a83ab212eb1bfe8 Path: webseclab/static.go
                        - SHA: c3865f1c8aa8044d29476161d5269cc47ccf92419b6b01d4ec611562123af228
                          Payload: xx.xx.xx.xx
                          Lines: [1072]
                - SHA: af9a718122857ba25e35e2e387cde52a3fc6db6a00c5b4e94d4bf306fa3f6bc7 Path: webseclab/ip.go
                        - SHA: 0a2fdab236cc01d85d7444687df7e6e1b87fe6d3ba786899329e856e56c99894
                          Payload: yy.yy.yy.yy
                          Lines: [35 41]
                - SHA: d6fae90675bcc4fc8782e38d96c3c5e9999347d06ca42467fa2a61f1f9060fb9 Path: webseclab/ip_test.go
                        - SHA: e14759884124ebda2598c31b6398bac1aa681bec9f10cf3937df021c72e9ea91
                          Payload: zz.zz.zz.zz
                          Lines: [96]
Content Findings:
        - ID: 10000000-0000-0000-0000-000000000017
          Name: "username"
          Description: "Possible hardcoded username"
                - SHA: 71491b47c58c935cd368f069492ca042f7eec72f9d72684c8e50a3c6e07417ab Path: webseclab/templates/xss/reflect/js6_bug7208690
                        - SHA: bc3e897431b03f17bef1b5e8b7c8bcb341b6e6f6ee9c3fa65d1e42f6878d734b
                          Payload: userid="dsa***
                          Lines: [43 43]
                        - SHA: a8767bc9be3bed443fd3f74fd635cb4b0bbd53aaca5a755373bf3834a8b423b1
                          Payload: user="Dmi***
                          Lines: [102]
                - SHA: cc88a39b3b39aca4926108cfb5492f3c1c1278085b2ca2875a83ab212eb1bfe8 Path: webseclab/static.go
                        - SHA: bc3e897431b03f17bef1b5e8b7c8bcb341b6e6f6ee9c3fa65d1e42f6878d734b
                          Payload: userid="dsa***
                          Lines: [455 455 1154 1154]
                        - SHA: a8767bc9be3bed443fd3f74fd635cb4b0bbd53aaca5a755373bf3834a8b423b1
                          Payload: user="Dmi***
                          Lines: [514 1213]
                        - SHA: 307b0f96553e1150a2726be2e075b9befc0d16c98050c87efea6ecbb46c5c6aa
                          Payload: userid="dan***
                          Lines: [1070 1070]
                        - SHA: 0b359fc357e5de4e721b51ce774daeeb7acd0f3e6bad65f067ad79c82fcc2bcf
                          Payload: user="Dan***
                          Lines: [1072]
                - SHA: 71491b47c58c935cd368f069492ca042f7eec72f9d72684c8e50a3c6e07417ab Path: webseclab/templates/xss/reflect/js3_bug7208690
                        - SHA: bc3e897431b03f17bef1b5e8b7c8bcb341b6e6f6ee9c3fa65d1e42f6878d734b
                          Payload: userid="dsa***
                          Lines: [43 43]
                        - SHA: a8767bc9be3bed443fd3f74fd635cb4b0bbd53aaca5a755373bf3834a8b423b1
                          Payload: user="Dmi***
                          Lines: [102]
                - SHA: 48474b70035adce4b2e21adc319dd11251c62f56d69c0fd740cee4acf31d14e1 Path: webseclab/templates/xss/reflect/js3_search_fp
                        - SHA: 307b0f96553e1150a2726be2e075b9befc0d16c98050c87efea6ecbb46c5c6aa
                          Payload: userid="dan***
                          Lines: [2 2]
                        - SHA: 0b359fc357e5de4e721b51ce774daeeb7acd0f3e6bad65f067ad79c82fcc2bcf
                          Payload: user="Dan***
                          Lines: [4]
Please see full analysis: https://portal.checkpoint.com/Dashboard/SourceGuard#/scan/sourcecode/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxx

IPアドレスやユーザー名のハードコードを指摘してくれています。

詳細なレポートも参照できるようですが、現在は見ることができないようでした。

IaCテンプレートの分析

次にTerraformのテンプレートをスキャンしてみます。

Terraformテンプレートを用意する

今回はこちらのテンプレートを利用してみました。

hashicorp / terraform-elasticache-example

git clone https://github.com/hashicorp/terraform-elasticache-example.git

分析

ブレードの使い方はこんな感じです。

$ shiftleft iac-assessment -h                                                                                                                       
INFO   [08-12-2020 16:29:36.073] blade iac-assessment updated (0.0.13)        
Usage of iac-assessment:
  -D, --debug                    debug output flag
  -d, --directory string         work dir (default is temp dir)
  -h, --help                     show usage
  -j, --json                     replace stdout human readble output with json format
  -l, --logic string             rule logic to run on (cannot be used combined with ruleset ID)
  -p, --path string              path to terraform project directory or execution-plan file (required)
  -r, --ruleset int              ruleset ID to run on (cannot be used combined with rule logic)
  -s, --severity-level string    severity level [Unknown Low Medium High Critical] (default "Low")
  -t, --severity-threshold int   severity-level threshold (default 1)
  -v, --variable string:string   key:value variables to be replaced inside the terraform files
  -V, --version                  show version

スキャンの際にはRuleset IDを指定する必要があり、どのようなルールセットがあるかはコンソールから確認できます。

RulesetのIDはRulesetの詳細画面で確認できます。

で、結果はこんな感じです。 (結果は一部省略しています)

$ shiftleft iac-assessment -p terraform-elasticache-example/ -r -64
[
    {
        "error": null,
        "testedCount": 0,
        "relevantCount": 0,
        "nonComplyingCount": 0,
        "exclusionStats": {
            "testedCount": 0,
            "relevantCount": 0,
            "nonComplyingCount": 0
        },
        "entityResults": [],
        "rule": {
            "name": "Ensure the default security group of every VPC restricts all traffic",
            "severity": "High",
            "logic": "aws_default_security_group should not have ingress with [cidr_blocks contain ['0.0.0.0:0'] or ipv6_cidr_blocks contain ['::/0']] or egress with [cidr_blocks contain ['0.0.0.0:0'] or ipv6_cidr_blocks contain ['::/0']]",
            "description": "A VPC comes with a default security group whose initial settings deny all inbound traffic, allow all outbound traffic, and allow all traffic between instances assigned to the security group. If you dont specify a security group when you launch an instance, the instance is automatically assigned to this default security group. Security groups provide stateful filtering of ingress/egress network traffic to AWS resources. It is recommended that the default security group restrict all traffic.",
            "remediation": "1. Run aws ec2 describe-security-groups\n2. For each group allow ingress for 0.0.0.0/0 or egress for 0.0.0.0/0\n2.1 Run aws ec2 delete-security-configuration --group-ids <group>",
            "complianceTag": "Network Security",
            "domain": "",
            "priority": "",
            "controlTitle": "",
            "ruleId": "D9.TF.AWS.NET.03",
            "logicHash": "/IPcpUSwgQ/STK1z16mk5Q",
            "isDefault": false
        },
        "testPassed": true
    },
.
.
.
.
.
    {
        "error": null,
        "testedCount": 1,
        "relevantCount": 1,
        "nonComplyingCount": 0,
        "exclusionStats": {
            "testedCount": 0,
            "relevantCount": 0,
            "nonComplyingCount": 0
        },
        "entityResults": [],
        "rule": {
            "name": "Ensure no security groups allow ingress from 0.0.0.0/0 to SSH (TCP:22)",
            "severity": "High",
            "logic": "aws_security_group should not have ingress with [(from_port<=22 and to_port>=22) and (cidr_blocks contain ['0.0.0.0:0'] or ipv6_cidr_blocks contain ['::/0'])]",
            "description": "Security groups provide stateful filtering of ingress and egress network traffic to AWS resources. It is recommended that no security group allows unrestricted ingress access to remote server administration ports, such as SSH to port 22.",
            "remediation": "1. Run aws ec2 describe-security-groups\n2. For each group allow ingress for 0.0.0.0/0 to SSH (port 22/tcp)\n2.1 Run aws ec2 delete-security-configuration --group-ids <group>",
            "complianceTag": "Network Security",
            "domain": "",
            "priority": "",
            "controlTitle": "",
            "ruleId": "D9.TF.AWS.NET.01",
            "logicHash": "JiUx1BQqhxV9pvK+hiNJCw",
            "isDefault": false
        },
        "testPassed": true
    },
.
.
.
.
.
    {
        "error": null,
        "testedCount": 1,
        "relevantCount": 1,
        "nonComplyingCount": 0,
        "exclusionStats": {
            "testedCount": 0,
            "relevantCount": 0,
            "nonComplyingCount": 0
        },
        "entityResults": [],
        "rule": {
            "name": "Ensure routing tables for security groups peering are \"least access\"",
            "severity": "High",
            "logic": "aws_security_group should not have ingress.cidr_block contain-any ['0.0.0.0/0'] and (ingress.from_port=0 and ingress.to_port=65535)",
            "description": "Once a VPC peering connection is established, routing tables must be updated to establish any connections between the peered security groups. These routes can be as specific as desired - even peering a security groups to only a single host on the other side of the connection.",
            "remediation": "1. Run aws ec2 describe-security-groups\n2. For each group allow ingress for 0.0.0.0/0 and ports from 0 to 65535\n2.1 Run aws ec2 delete-security-configuration --group-ids <group>",
            "complianceTag": "Network Security",
            "domain": "",
            "priority": "",
            "controlTitle": "",
            "ruleId": "D9.TF.AWS.NET.04",
            "logicHash": "rwHuW7JlcUDnYiAQL+lhPA",
            "isDefault": false
        },
        "testPassed": true
    },
.
.
.
.
.
    {
        "error": null,
        "testedCount": 1,
        "relevantCount": 1,
        "nonComplyingCount": 0,
        "exclusionStats": {
            "testedCount": 0,
            "relevantCount": 0,
            "nonComplyingCount": 0
        },
        "entityResults": [],
        "rule": {
            "name": "Ensure no security groups allow ingress from 0.0.0.0/0 to RDP (TCP:3389)",
            "severity": "High",
            "logic": "aws_security_group should not have ingress with [(from_port<=3389 and to_port>=3389) and (cidr_blocks contain ['0.0.0.0:0'] or ipv6_cidr_blocks contain ['::/0'])]",
            "description": "Security groups provide stateful filtering of ingress and egress network traffic to AWS resources. It is recommended that no security group allows unrestricted ingress access to remote server administration ports, such as RDP to port 3389.",
            "remediation": "1. Run aws ec2 describe-security-groups\n2. For each group allow ingress for 0.0.0.0/0 to SSH (port 3389/tcp)\n2.1 Run aws ec2 delete-security-configuration --group-ids <group>",
            "complianceTag": "Network Security",
            "domain": "",
            "priority": "",
            "controlTitle": "",
            "ruleId": "D9.TF.AWS.NET.02",
            "logicHash": "TBUK7gxQYDkAhAmTQGesng",
            "isDefault": false
        },
        "testPassed": true
    },
.
.
.
.
.
    {
        "error": null,
        "testedCount": 0,
        "relevantCount": 0,
        "nonComplyingCount": 0,
        "exclusionStats": {
            "testedCount": 0,
            "relevantCount": 0,
            "nonComplyingCount": 0
        },
        "entityResults": [],
        "rule": {
            "name": "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket",
            "severity": "High",
            "logic": "aws_s3_bucket should not have logging isEmpty()",
            "description": "S3 Bucket Access Logging generates a log that contains access records for each request made to your S3 bucket. An access log record contains details about the request, such as the request type, the resources specified in the request worked, and the time and date the request was processed. It is recommended that bucket access logging be enabled on the CloudTrail S3 bucket.",
            "remediation": "1. Get the name of the S3 bucket that CloudTrail is logging to aws cloudtrail describe-trails --query trailList[*].S3BucketName\n2. Ensure Bucket Logging is enabled aws s3api get-bucket-logging --bucket <s3_bucket_for_cloudtrail>\n3. Ensure command does not returns empty output.",
            "complianceTag": "Logging",
            "domain": "",
            "priority": "",
            "controlTitle": "",
            "ruleId": "D9.TF.AWS.LOG.05",
            "logicHash": "Z841/7ZEPBuqMMOomARnLg",
            "isDefault": false
        },
        "testPassed": true
    }
]

今回スキャンしたテンプレートではルールセットに反する設定は含まれていませんでしたが、こんな感じで評価を行うことができます。

コンテナイメージの分析

最後にコンテナイメージのスキャンを実行します。

まず、ブレードの使い方を確認します。

$ shiftleft image-scan -h
NAME
        image-scan

DESCRIPTION
        source-code/image security and visibility into the risk analysis of projects

SYNOPSIS
        image-scan [OPTIONS] [ -s <source_folder> | -i <image_path> ]

OPTIONS

  -s, -src, --source <string>           path to source code directory
  -i, -img, --image <string>            path to docker image tar format
  -t, -T, --timeout <int>               scan timeout in seconds (default: 600 for source code, 900 for docker image)
  -x, -X, --exclude <string>            path to exclude from scanning (.gitignore syntax)
  -d, -D, --debug                       debug output
  -j, -J, --json                        json output
  -nc, -NC, --no-cache                  do not take results from previous scans
  -np, -NP, --no-proxy                  do not use system proxy settings
  -nb, -NB, --no-blame                  do not use Git blame on scanned files
  -v, -V, --version                     show version
  -h, -H, --help                        show help

EXAMPLE
        image-scan -x tests/** -x static/** -s /home/project
        image-scan -t 900 -i /home/image.tar

このブレードではローカルのDockerイメージをスキャンするようです。

コンテナイメージを用意する

ここでは、こちらのやられアプリを利用します。

bkimminich / juice-shop

ローカルに保存します。 ディスクの容量にはご注意ください。

docker save bkimminich/juice-shop -o juice-shop.tar

分析

スキャンしてみた結果がこちらになります。 (一部省略しています)

$ shiftleft image-scan -i juice-shop.tar -t 1800
INFO   [08-12-2020 17:14:56.395] SourceGuard Scan Started!                    
INFO   [08-12-2020 17:14:59.284] Project name: juice-shop path: /tmp/SourceGuard302921902 
INFO   [08-12-2020 17:14:59.284] Scan ID: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz-zzzzzz 
Action: BLOCK
Content Findings:
        - ID: 10000000-0000-0000-0000-000000000004
          Name: "aws_secret_access_key"
          Description: "Possible AWS secret access key"
                - SHA: 55650c62ef91cc1f928129b8d4060d899faf4d1d648b7ef4a49448fdc03562fa Path: /tmp/juice-shop/frontend/dist/frontend/vendor-es5.js
                        - SHA: c93f89907a369302d16739ae8a885da65c2b6ecac41992a7b2072f869df47278
                          Payload: key:"_getAn***
                          Lines: [1]
Packages Findings:
        - Package Path: /usr/local/lib/node_modules/npm/node_modules/libnpm/package.json
          Package Manager: npm
          Severity: CRITICAL
                - bin-links ^ 1.1.2
                  Severity: CRITICAL
                  Line: 35
                  CVEs Findings:
                        - ID: CWE-434
                        Description: The software allows the attacker to upload or transfer files of dangerous types that can be automatically processed within the product's environment.
                        Severity: CRITICAL
                        Last Modified: 0001-01-01T00:00:00Z
                        - ID: CWE-61
                        Description: The software, when opening a file or directory, does not sufficiently account for when the file is a symbolic link that resolves to a target outside of the intended control sphere. This could allow an attacker to cause the software to operate on unauthorized files.
                        Severity: HIGH
                        Last Modified: 0001-01-01T00:00:00Z
                        - ID: CWE-119
                        Description: The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.
                        Severity: HIGH
                        Last Modified: 0001-01-01T00:00:00Z
        - Package Path: /juice-shop/package-lock.json
          Package Manager: npm
          Severity: CRITICAL
                - yargs-parser  13.1.2
                  Severity: MEDIUM
                  Line: 8115
                  CVEs Findings:
                        - ID: CVE-2020-7608
                        Description: yargs-parser could be tricked into adding or modifying properties of Object.prototype using a "__proto__" payload.
                        Severity: MEDIUM
                        Last Modified: 2020-06-05T15:23:00Z
                - class-utils  0.3.6
                  Severity: UNKNOWN
                  Line: 1579
                  CVEs Findings:
                        - ID: CVE-2008-5856
                        Description: Directory traversal vulnerability in scripts/export.php in ClaSS before 0.8.61 allows remote attackers to read arbitrary files via directory traversal sequences in the ftype parameter.
                        Severity: UNKNOWN
                        Last Modified: 2017-09-29T01:32:00Z
.
.
.
.
.
        - Package Path: /usr/local/lib/node_modules/npm/node_modules/boxen/package.json
          Package Manager: npm
          Severity: 
Please see full analysis: https://portal.checkpoint.com/Dashboard/SourceGuard#/scan/image/zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz-zzzzzz

アクセスキーのハードコードの他、パッケージの脆弱性についての指摘が表示されました。

まとめ

とりあえず機能をひと通り利用してみました。 CI/CD環境への組み込みは非常に簡単に実現できそうですね。 肝心の検出精度やどのような脆弱性の検出をサポートしているかについては情報があまりなかったので、一般提供開始時に改めて確認してみたいと思います。

インフラのプロビジョニングやアプリケーションのデプロイ前に脆弱性を検出するというアプローチは今後より一般化していくと思います。 機会があればこういったアプローチを提案に盛り込んでいきたいと思います。

現場からは以上です。