From a8e8d66d0c0098e87416024870d098654dd5cae0 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 8 Oct 2019 08:50:58 +0300 Subject: [PATCH 01/19] Save the resulting infringing data, to be researched locally --- .gitignore | 3 +++ checks/check_extra741 | 2 -- checks/check_extra742 | 2 -- checks/check_extra759 | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..5acbc7c360 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +secrets-* + diff --git a/checks/check_extra741 b/checks/check_extra741 index f4e54d2cca..03f954212f 100644 --- a/checks/check_extra741 +++ b/checks/check_extra741 @@ -43,8 +43,6 @@ extra741(){ rm -f $EC2_USERDATA_FILE else textFail "$regx: Potential secret found in $instance" "$regx" - # delete file to not leave trace, user must look at the instance User Data - rm -f $EC2_USERDATA_FILE fi else mv $EC2_USERDATA_FILE $EC2_USERDATA_FILE.gz ; gunzip $EC2_USERDATA_FILE.gz diff --git a/checks/check_extra742 b/checks/check_extra742 index f34afff487..9888c595fc 100644 --- a/checks/check_extra742 +++ b/checks/check_extra742 @@ -41,8 +41,6 @@ extra742(){ rm -f $CFN_OUTPUTS_FILE else textFail "$regx: Potential secret found in stack $stack Outputs" "$regx" - # delete file to not leave trace, user must look at the CFN Stack - rm -f $CFN_OUTPUTS_FILE fi else textInfo "$regx: CloudFormation stack $stack has not Outputs" "$regx" diff --git a/checks/check_extra759 b/checks/check_extra759 index dbd0763291..f171e346a0 100644 --- a/checks/check_extra759 +++ b/checks/check_extra759 @@ -39,8 +39,6 @@ extra759(){ rm -f $LAMBDA_FUNCTION_VARIABLES_FILE else textFail "$regx: Potential secret found in Lambda function $lambdafunction variables" "$regx" - # delete file to not leave trace, user must look at the function - rm -f $LAMBDA_FUNCTION_VARIABLES_FILE fi else textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx" From 8a4362e2e09f24f27789ca74c02ca56b27dd3a7a Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Mon, 4 Nov 2019 23:32:28 +0200 Subject: [PATCH 02/19] Save the secrets found to the account folder for analysis --- checks/check_extra741 | 5 +++-- checks/check_extra742 | 3 ++- checks/check_extra759 | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/checks/check_extra741 b/checks/check_extra741 index 03f954212f..d815bd1ad2 100644 --- a/checks/check_extra741 +++ b/checks/check_extra741 @@ -56,11 +56,12 @@ extra741(){ fi else textPass "$regx: No secrets found in $instance User Data or it is empty" "$regx" - fi + rm -f $EC2_USERDATA_FILE + fi done else textInfo "$regx: No EC2 instances found" "$regx" fi done - rm -rf $SECRETS_TEMP_FOLDER +# rm -rf $SECRETS_TEMP_FOLDER } diff --git a/checks/check_extra742 b/checks/check_extra742 index 9888c595fc..c67018348f 100644 --- a/checks/check_extra742 +++ b/checks/check_extra742 @@ -44,11 +44,12 @@ extra742(){ fi else textInfo "$regx: CloudFormation stack $stack has not Outputs" "$regx" + rm -f $CFN_OUTPUTS_FILE fi done else textInfo "$regx: No CloudFormation stacks found" "$regx" fi done - rm -rf $SECRETS_TEMP_FOLDER +# rm -rf $SECRETS_TEMP_FOLDER } diff --git a/checks/check_extra759 b/checks/check_extra759 index f171e346a0..c9287822ab 100644 --- a/checks/check_extra759 +++ b/checks/check_extra759 @@ -42,11 +42,12 @@ extra759(){ fi else textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx" + rm -f $LAMBDA_FUNCTION_VARIABLES_FILE fi done else textInfo "$regx: No Lambda functions found" "$regx" fi done - rm -rf $SECRETS_TEMP_FOLDER +# rm -rf $SECRETS_TEMP_FOLDER } From 15f1c1a2360dee6cd32581e6f9b09e5af0998388 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 5 Nov 2019 09:38:39 +0200 Subject: [PATCH 03/19] Fix the check extra760 to not delete the secrets folder --- checks/check_extra760 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/checks/check_extra760 b/checks/check_extra760 index eac466e72f..d56055d385 100644 --- a/checks/check_extra760 +++ b/checks/check_extra760 @@ -44,12 +44,13 @@ extra760(){ else textFail "$regx: Potential secret found in Lambda function $lambdafunction code" "$regx" # delete files to not leave trace, user must look at the function - rm -fr $LAMBDA_FUNCTION_FOLDER +# rm -fr $LAMBDA_FUNCTION_FOLDER fi done else textInfo "$regx: No Lambda functions found" "$regx" + rm -fr $LAMBDA_FUNCTION_FOLDER fi done - rm -fr $SECRETS_TEMP_FOLDER +# rm -fr $SECRETS_TEMP_FOLDER } From 70a98ec21d5de152c6c0ba5a84c2295b8bf756a5 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Sat, 16 Nov 2019 20:12:58 -0800 Subject: [PATCH 04/19] Create credentials file & run.sh --- Dockerfile | 8 ++++++++ credentials.py | 26 ++++++++++++++++++++++++++ run.sh | 14 ++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 Dockerfile create mode 100644 credentials.py create mode 100755 run.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..335937224f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM python +MAINTAINER Bridgecrew + +RUN apt-get update && apt-get upgrade -y && apt-get install jq -y && pip install awscli detect-secrets + +RUN curl -sL https://github.com/bridgecrewio/prowler/archive/master.tar.gz | tar xz + +RUN run.sh diff --git a/credentials.py b/credentials.py new file mode 100644 index 0000000000..3f1fd9f46c --- /dev/null +++ b/credentials.py @@ -0,0 +1,26 @@ +import boto3 +import sys +import json +import os + +sts = boto3.client('sts') +aws_lambda = boto3.client('lambda', region_name='us-west-2') + +aws_account_id = sys.argv[1] + +if aws_account_id is None: + raise ValueError('Must provide an AWS account ID') + +account_details_response = aws_lambda.invoke( + FunctionName='customers-api', + Payload=json.dumps({"path": "/invoke/getTenantDetails", "body": {"awsAccountId": sys.argv[1]}, "headers": {"Content-Type": "application/json"}}).encode('utf-8') +) + +account_details = json.loads(json.loads(account_details_response['Payload'].read())['body']) + +print('Got account details, assuming role') + +temporary_creds = sts.assume_role(RoleArn=account_details['cross_account_role_arn'], ExternalId=account_details['external_id'], RoleSessionName='prowler')['Credentials'] + +os.environ['AWS_ACCESS_KEY_ID2'] = temporary_creds['AccessKeyId'] +os.environ['AWS_SECRET_ACCESS_KEY2'] = temporary_creds['SecretAccessKey'] diff --git a/run.sh b/run.sh new file mode 100755 index 0000000000..e24c258248 --- /dev/null +++ b/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +AccountId=$1 +Alias=$2 + +echo $AWS_ACCESS_KEY_ID +python3 credentials.py $AccountId + +echo $AWS_ACCESS_KEY_ID +echo $AWS_ACCESS_KEY_ID2 +echo "Running prowler on $Alias" +./prowler -c extra741 -M json > output.json + +unset AWS_ACCESS_KEY_ID +echo $AWS_ACCESS_KEY_ID From ef26086330d67eb82512f8368d696639da6e7dec Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Sat, 16 Nov 2019 20:46:30 -0800 Subject: [PATCH 05/19] Update dockerfile and scripts --- Dockerfile | 8 +++++--- credentials.py | 4 ++-- run.sh | 12 +++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 335937224f..cc2ac80e0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,10 @@ FROM python MAINTAINER Bridgecrew -RUN apt-get update && apt-get upgrade -y && apt-get install jq -y && pip install awscli detect-secrets +RUN apt-get update && apt-get upgrade -y && apt-get install jq -y && pip install awscli detect-secrets boto3 -RUN curl -sL https://github.com/bridgecrewio/prowler/archive/master.tar.gz | tar xz +RUN curl -sL https://github.com/bridgecrewio/prowler/archive/feature/dockerized_prowler.tar.gz | tar xz -RUN run.sh +WORKDIR "./prowler-feature-dockerized_prowler" + +ENTRYPOINT ["./run.sh"] diff --git a/credentials.py b/credentials.py index 3f1fd9f46c..e6233e8614 100644 --- a/credentials.py +++ b/credentials.py @@ -22,5 +22,5 @@ print('Got account details, assuming role') temporary_creds = sts.assume_role(RoleArn=account_details['cross_account_role_arn'], ExternalId=account_details['external_id'], RoleSessionName='prowler')['Credentials'] -os.environ['AWS_ACCESS_KEY_ID2'] = temporary_creds['AccessKeyId'] -os.environ['AWS_SECRET_ACCESS_KEY2'] = temporary_creds['SecretAccessKey'] +os.environ['AWS_ACCESS_KEY_ID'] = temporary_creds['AccessKeyId'] +os.environ['AWS_SECRET_ACCESS_KEY'] = temporary_creds['SecretAccessKey'] diff --git a/run.sh b/run.sh index e24c258248..b0d8f699ae 100755 --- a/run.sh +++ b/run.sh @@ -1,14 +1,8 @@ #!/bin/bash -AccountId=$1 -Alias=$2 -echo $AWS_ACCESS_KEY_ID -python3 credentials.py $AccountId +python3 credentials.py $ACCOUNT_ID -echo $AWS_ACCESS_KEY_ID -echo $AWS_ACCESS_KEY_ID2 -echo "Running prowler on $Alias" +echo "Running prowler on $ALIAS" ./prowler -c extra741 -M json > output.json -unset AWS_ACCESS_KEY_ID -echo $AWS_ACCESS_KEY_ID +aws s3api put-object --bucket $BUCKET --key prowler/$ACCOUNT_ID/results \ No newline at end of file From 592af2b5b555e825be1ffd09c74c4492d678aef1 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Sat, 16 Nov 2019 21:01:23 -0800 Subject: [PATCH 06/19] Make image smaller --- Dockerfile | 4 ++-- run.sh | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index cc2ac80e0f..c7f5eb5987 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM python +FROM python:3.7-slim MAINTAINER Bridgecrew -RUN apt-get update && apt-get upgrade -y && apt-get install jq -y && pip install awscli detect-secrets boto3 +RUN apt-get update && apt-get upgrade -y && apt-get install jq curl -y && pip install awscli detect-secrets boto3 RUN curl -sL https://github.com/bridgecrewio/prowler/archive/feature/dockerized_prowler.tar.gz | tar xz diff --git a/run.sh b/run.sh index b0d8f699ae..ae134f5cd7 100755 --- a/run.sh +++ b/run.sh @@ -5,4 +5,6 @@ python3 credentials.py $ACCOUNT_ID echo "Running prowler on $ALIAS" ./prowler -c extra741 -M json > output.json +cat output.json + aws s3api put-object --bucket $BUCKET --key prowler/$ACCOUNT_ID/results \ No newline at end of file From fd993435301d924ad30156b9ab8f49a6f233eebc Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 19 Nov 2019 22:22:15 -0800 Subject: [PATCH 07/19] Create a script which assumes the role, runs prowler, and uploads to s3 --- Dockerfile | 10 ---------- bridgecrew-prowler/Dockerfile | 14 ++++++++++++++ bridgecrew-prowler/run.sh | 20 ++++++++++++++++++++ run.sh | 10 ---------- 4 files changed, 34 insertions(+), 20 deletions(-) delete mode 100644 Dockerfile create mode 100644 bridgecrew-prowler/Dockerfile create mode 100755 bridgecrew-prowler/run.sh delete mode 100755 run.sh diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c7f5eb5987..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3.7-slim -MAINTAINER Bridgecrew - -RUN apt-get update && apt-get upgrade -y && apt-get install jq curl -y && pip install awscli detect-secrets boto3 - -RUN curl -sL https://github.com/bridgecrewio/prowler/archive/feature/dockerized_prowler.tar.gz | tar xz - -WORKDIR "./prowler-feature-dockerized_prowler" - -ENTRYPOINT ["./run.sh"] diff --git a/bridgecrew-prowler/Dockerfile b/bridgecrew-prowler/Dockerfile new file mode 100644 index 0000000000..03a35a3027 --- /dev/null +++ b/bridgecrew-prowler/Dockerfile @@ -0,0 +1,14 @@ +FROM toniblyx/prowler +MAINTAINER Bridgecrew + +USER root + +WORKDIR "./prowler" + +COPY "./run.sh" "." + +RUN chown -R prowler /prowler/ + +USER prowler + +ENTRYPOINT ["./run.sh"] diff --git a/bridgecrew-prowler/run.sh b/bridgecrew-prowler/run.sh new file mode 100755 index 0000000000..dde80afc10 --- /dev/null +++ b/bridgecrew-prowler/run.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +echo "Assuming role for ${ACCOUNT_ID}" +creds=$(aws sts assume-role --role-arn ${ROLE_ARN} --external-id ${EXTERNAL_ID} --role-session-name prowler | jq '.Credentials') +export AWS_ACCESS_KEY_ID=$(echo $creds | jq ".AccessKeyId" | sed 's/\"//g') +export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq ".SecretAccessKey" | sed 's/\"//g') +export AWS_SESSION_TOKEN=$(echo $creds | jq ".SessionToken" | sed 's/\"//g') + +echo "Running prowler on ${ACCOUNT_ID}" +./prowler -c extra741,extra742,extra759 -M json > output.json + +echo "Results:" +cat output.json + +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset AWS_SESSION_TOKEN + +echo "Uploading result to s3://${BUCKET}/prowler/${ACCOUNT_ID}/output.json" +aws s3api put-object --bucket ${BUCKET} --key prowler/$ACCOUNT_ID/output.json --body output.json \ No newline at end of file diff --git a/run.sh b/run.sh deleted file mode 100755 index ae134f5cd7..0000000000 --- a/run.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -python3 credentials.py $ACCOUNT_ID - -echo "Running prowler on $ALIAS" -./prowler -c extra741 -M json > output.json - -cat output.json - -aws s3api put-object --bucket $BUCKET --key prowler/$ACCOUNT_ID/results \ No newline at end of file From d6c2f3c78b3d8bfebd00a02878dfce92514709da Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Thu, 21 Nov 2019 11:14:24 -0800 Subject: [PATCH 08/19] Expose checks parameter --- bridgecrew-prowler/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridgecrew-prowler/run.sh b/bridgecrew-prowler/run.sh index dde80afc10..7c450d23fd 100755 --- a/bridgecrew-prowler/run.sh +++ b/bridgecrew-prowler/run.sh @@ -7,7 +7,7 @@ export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq ".SecretAccessKey" | sed 's/\"// export AWS_SESSION_TOKEN=$(echo $creds | jq ".SessionToken" | sed 's/\"//g') echo "Running prowler on ${ACCOUNT_ID}" -./prowler -c extra741,extra742,extra759 -M json > output.json +./prowler ${CHECKS} -M json > output.json echo "Results:" cat output.json From 662f287dd6739cd6d8e5e0d95537f4ca4b7b6493 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Thu, 21 Nov 2019 12:39:22 -0800 Subject: [PATCH 09/19] Add ECS task definition environment variables check --- checks/check_extra763 | 55 ++++++++++++++++++++++++++++++++++++++++++ groups/group11_secrets | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 checks/check_extra763 diff --git a/checks/check_extra763 b/checks/check_extra763 new file mode 100644 index 0000000000..2798330424 --- /dev/null +++ b/checks/check_extra763 @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +CHECK_ID_extra763="7.63" +CHECK_TITLE_extra763="[extra763] Find secrets in ECS task definitions variables (Not Scored) (Not part of CIS benchmark)" +CHECK_SCORED_extra763="NOT_SCORED" +CHECK_TYPE_extra763="EXTRA" +CHECK_ALTERNATE_check763="extra763" + +extra763(){ + SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM" + if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then + # this folder is deleted once this check is finished + mkdir $SECRETS_TEMP_FOLDER + fi + + textInfo "Looking for secrets in ECS task definitions' environment variables across all regions... " + for regx in $REGIONS; do + LIST_OF_TASK_DEFINITIONS=$($AWSCLI ecs list-task-definitions $PROFILE_OPT --region $regx --query taskDefinitionArns[*] --output text) + if [[ $LIST_OF_TASK_DEFINITIONS ]]; then + for taskDefinition in $LIST_OF_TASK_DEFINITIONS;do + IFS='/' read -r -a splitArn <<< "$taskDefinition" + TASK_DEFINITION=${splitArn[1]} + TASK_DEFINITION_ENV_VARIABLES_FILE="$SECRETS_TEMP_FOLDER/extra763-$TASK_DEFINITION-$regx-variables.txt" + TASK_DEFINITION_ENV_VARIABLES=$($AWSCLI ecs $PROFILE_OPT --region $regx describe-task-definition --task-definition $taskDefinition --query 'taskDefinition.containerDefinitions[*].environment' --output text > $TASK_DEFINITION_ENV_VARIABLES_FILE) + if [ -s $TASK_DEFINITION_ENV_VARIABLES_FILE ];then + # Implementation using https://github.com/Yelp/detect-secrets + FINDINGS=$(secretsDetector file $TASK_DEFINITION_ENV_VARIABLES_FILE) + if [[ $FINDINGS -eq 0 ]]; then + textPass "$regx: No secrets found in ECS task definition $TASK_DEFINITION variables" "$regx" + # delete file if nothing interesting is there + rm -f $TASK_DEFINITION_ENV_VARIABLES_FILE + else + textFail "$regx: Potential secret found in ECS task definition $TASK_DEFINITION variables" "$regx" + fi + else + textInfo "$regx: ECS task definition $TASK_DEFINITION has no variables" "$regx" + rm -f $TASK_DEFINITION_ENV_VARIABLES_FILE + fi + done + else + textInfo "$regx: No ECS task definitions found" "$regx" + fi + done +# rm -rf $SECRETS_TEMP_FOLDER +} diff --git a/groups/group11_secrets b/groups/group11_secrets index ec0b971f70..09451fda7d 100644 --- a/groups/group11_secrets +++ b/groups/group11_secrets @@ -15,7 +15,7 @@ GROUP_ID[11]='secrets' GROUP_NUMBER[11]='11.0' GROUP_TITLE[11]='Look for keys secrets or passwords around resources - [secrets] **' GROUP_RUN_BY_DEFAULT[11]='N' # but it runs when execute_all is called (default) -GROUP_CHECKS[11]='extra741,extra742,extra759,extra760' +GROUP_CHECKS[11]='extra741,extra742,extra759,extra760,extra763' # requires https://github.com/Yelp/detect-secrets # `pip install detect-secrets` From 9d07391a61c6656ea1fae931cb0376650b321ff0 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 26 Nov 2019 17:14:07 +0200 Subject: [PATCH 10/19] Fix unnecessary `` --- checks/check_extra768 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra768 b/checks/check_extra768 index 66ea5a7bb2..bb57bb87c1 100644 --- a/checks/check_extra768 +++ b/checks/check_extra768 @@ -1,6 +1,6 @@ #!/usr/bin/env bash -``# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente +# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy From fcf28dfa70fb93df9f61393b8dff2cc5fc14729e Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Thu, 12 Dec 2019 09:42:42 +0200 Subject: [PATCH 11/19] Add trail count to check21 and fail if no trail exist --- checks/check21 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/checks/check21 b/checks/check21 index 64b82ebcce..383578e3d2 100644 --- a/checks/check21 +++ b/checks/check21 @@ -15,19 +15,25 @@ CHECK_TYPE_check21="LEVEL1" CHECK_ALTERNATE_check201="check21" check21(){ + trail_count=0 # "Ensure CloudTrail is enabled in all regions (Scored)" for regx in $REGIONS; do LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].Name' --output text --no-include-shadow-trails) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do - MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail) - if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then - textFail "$trail trail in $regx is not enabled in multi region mode" - else - textPass "$trail trail in $regx is enabled for all regions" - fi + trail_count=$((trail_count + 1)) + MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail) + if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then + textFail "$trail trail in $regx is not enabled in multi region mode" + else + textPass "$trail trail in $regx is enabled for all regions" + fi done fi done + + if [[ $trail_count == 0 ]]; then + textFail "No CloudTrail trails were found in the account" + fi } From ae20d9c5b770ac593e64fa399fde55312d97ae1c Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Sun, 15 Dec 2019 15:53:46 +0200 Subject: [PATCH 12/19] Fix check26 - get the account ID from sts --- checks/check26 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/checks/check26 b/checks/check26 index 133a4176d8..41281b5646 100644 --- a/checks/check26 +++ b/checks/check26 @@ -18,10 +18,10 @@ check26(){ # "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" CLOUDTRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].Name' --output text| tr '\011' '\012' | awk -F: '{print $1}') + ACCOUNT_ID=$($AWSCLI sts get-caller-identity --output json $PROFILE_OPT --region $REGION --query "Account" | tr -d '"') if [[ $CLOUDTRAILS ]];then for trail in $CLOUDTRAILS; do - CLOUDTRAIL_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $4 }' | head -n 1) CLOUDTRAIL_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $5 }' | head -n 1) CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].[Name, S3BucketName]' --output text | tr '\011' ':' | grep "$trail" | awk -F: '{ print $2 }' ) @@ -29,12 +29,12 @@ check26(){ bucket=$CLOUDTRAILBUCKET if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_ID" ];then CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) - fi - if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then - textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail" - elif [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_ID" ];then - textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail" - else + if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then + textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail" + else + textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail" + fi + else textInfo "CloudTrail S3 bucket $bucket for trail $trail is not in current account" fi From 72976cc225a706c96e1cb496c939b0b19acf4ac4 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Sun, 15 Dec 2019 18:50:48 +0200 Subject: [PATCH 13/19] Feature/check public write buckets (#5) * Copy checks to overwrite the old checks in the docker * Initial commit * Change to check 771 --- bridgecrew-prowler/Dockerfile => Dockerfile | 2 +- checks/check_extra771 | 39 +++++++++++++++++++++ bridgecrew-prowler/run.sh => run.sh | 0 3 files changed, 40 insertions(+), 1 deletion(-) rename bridgecrew-prowler/Dockerfile => Dockerfile (89%) create mode 100644 checks/check_extra771 rename bridgecrew-prowler/run.sh => run.sh (100%) diff --git a/bridgecrew-prowler/Dockerfile b/Dockerfile similarity index 89% rename from bridgecrew-prowler/Dockerfile rename to Dockerfile index 03a35a3027..0b78f7ab1b 100644 --- a/bridgecrew-prowler/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ MAINTAINER Bridgecrew USER root WORKDIR "./prowler" - +COPY "checks" "checks" COPY "./run.sh" "." RUN chown -R prowler /prowler/ diff --git a/checks/check_extra771 b/checks/check_extra771 new file mode 100644 index 0000000000..943bbffe61 --- /dev/null +++ b/checks/check_extra771 @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +CHECK_ID_extra771="7.71" +CHECK_TITLE_extra771="[extra771] Check if S3 buckets have policies which allow WRITE access (Not Scored) (Not part of CIS benchmark)" +CHECK_SCORED_extra771="NOT_SCORED" +CHECK_TYPE_extra771="EXTRA" +CHECK_ALTERNATE_check771="extra771" + +extra771(){ + LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --query Buckets[*].Name --output text|xargs -n1) + if [[ $LIST_OF_BUCKETS ]]; then + for bucket in $LIST_OF_BUCKETS;do + BUCKET_POLICY_STATEMENTS=$($AWSCLI s3api $PROFILE_OPT get-bucket-policy --bucket $bucket --output json --query Policy 2>&1) + if [[ $BUCKET_POLICY_STATEMENTS == *GetBucketPolicy* ]]; then + textInfo "Bucket policy does not exist for bucket $bucket" + else + BUCKET_POLICY_BAD_STATEMENTS=$(echo $BUCKET_POLICY_STATEMENTS | jq --arg arn "arn:aws:s3:::$bucket" 'fromjson | .Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and (.Action|startswith("s3:Put") or startswith("s3:*")))') + if [[ $BUCKET_POLICY_BAD_STATEMENTS != "" ]]; then + textFail "Bucket $bucket allows public write: $BUCKET_POLICY_BAD_STATEMENTS" + else + textPass "Bucket $bucket has S3 bucket policy which does not allow public write access" + fi + fi + done + + else + textInfo "No S3 Buckets found" + fi +} diff --git a/bridgecrew-prowler/run.sh b/run.sh similarity index 100% rename from bridgecrew-prowler/run.sh rename to run.sh From 70879ba1e03ee7d5e5d59f94fd049620e08e4847 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Wed, 18 Dec 2019 13:23:51 +0200 Subject: [PATCH 14/19] Small check fixes --- checks/check_extra716 | 3 ++- checks/check_extra731 | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/checks/check_extra716 b/checks/check_extra716 index b0b51b85bf..ea45909862 100644 --- a/checks/check_extra716 +++ b/checks/check_extra716 @@ -37,8 +37,9 @@ extra716(){ textPass "$regx: $domain is in a VPC" "$regx" fi done + else + textInfo "$regx: No Elasticsearch Service domain found" "$regx" fi - textInfo "$regx: No Elasticsearch Service domain found" "$regx" rm -fr $TEMP_POLICY_FILE done } diff --git a/checks/check_extra731 b/checks/check_extra731 index 911108ab29..0baa1b6e25 100644 --- a/checks/check_extra731 +++ b/checks/check_extra731 @@ -32,9 +32,9 @@ extra731(){ if [[ $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION \ | jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' ) - textFail "$regx: SNS topic policy with public access: $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" "$SHORT_TOPIC" "$regx" + textFail "$regx: SNS topic $SHORT_TOPIC's policy with public access: $SNS_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS" "$SHORT_TOPIC" "$regx" else - textPass "$regx: SNS topic policy with public access but has a Condition" "$SHORT_TOPIC" "$regx" + textPass "$regx: SNS topic $SHORT_TOPIC's policy with public access but has a Condition" "$SHORT_TOPIC" "$regx" fi else textPass "$regx: SNS topic without public access" "$SHORT_TOPIC" "$regx" From 78c9d3a41c5a08812078c2e8f058ad58ed896036 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 24 Dec 2019 18:02:56 +0200 Subject: [PATCH 15/19] Lengthen the session to 3 hours --- run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run.sh b/run.sh index 7c450d23fd..f655b5895e 100755 --- a/run.sh +++ b/run.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "Assuming role for ${ACCOUNT_ID}" -creds=$(aws sts assume-role --role-arn ${ROLE_ARN} --external-id ${EXTERNAL_ID} --role-session-name prowler | jq '.Credentials') +creds=$(aws sts assume-role --role-arn ${ROLE_ARN} --external-id ${EXTERNAL_ID} --role-session-name prowler --duration-seconds 10800 | jq '.Credentials') export AWS_ACCESS_KEY_ID=$(echo $creds | jq ".AccessKeyId" | sed 's/\"//g') export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq ".SecretAccessKey" | sed 's/\"//g') export AWS_SESSION_TOKEN=$(echo $creds | jq ".SessionToken" | sed 's/\"//g') From 4f721cf2435b0a18d8498c64b32fa60991de9b97 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Tue, 24 Dec 2019 22:15:11 +0200 Subject: [PATCH 16/19] Use credentials file instead of assume role and stuff --- run.sh | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/run.sh b/run.sh index f655b5895e..8f93055280 100755 --- a/run.sh +++ b/run.sh @@ -1,20 +1,19 @@ #!/bin/bash -echo "Assuming role for ${ACCOUNT_ID}" -creds=$(aws sts assume-role --role-arn ${ROLE_ARN} --external-id ${EXTERNAL_ID} --role-session-name prowler --duration-seconds 10800 | jq '.Credentials') -export AWS_ACCESS_KEY_ID=$(echo $creds | jq ".AccessKeyId" | sed 's/\"//g') -export AWS_SECRET_ACCESS_KEY=$(echo $creds | jq ".SecretAccessKey" | sed 's/\"//g') -export AWS_SESSION_TOKEN=$(echo $creds | jq ".SessionToken" | sed 's/\"//g') +mkdir ~/.aws +cat << AWS_CREDS > ~/.aws/credentials +[default] +credential_source = EcsContainer +role_arn = ${ROLE_ARN} +external_id = ${EXTERNAL_ID} + +AWS_CREDS echo "Running prowler on ${ACCOUNT_ID}" -./prowler ${CHECKS} -M json > output.json +./prowler -p default "${CHECKS}" -M json > output.json echo "Results:" cat output.json -unset AWS_ACCESS_KEY_ID -unset AWS_SECRET_ACCESS_KEY -unset AWS_SESSION_TOKEN - echo "Uploading result to s3://${BUCKET}/prowler/${ACCOUNT_ID}/output.json" -aws s3api put-object --bucket ${BUCKET} --key prowler/$ACCOUNT_ID/output.json --body output.json \ No newline at end of file +aws s3api put-object --bucket "${BUCKET}" --key prowler/"${ACCOUNT_ID}"/output.json --body output.json \ No newline at end of file From 8393383830b419476e77cc1d4167351ef00a75f4 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Thu, 26 Dec 2019 16:41:25 +0200 Subject: [PATCH 17/19] Fix credentials --- checks/check_extra768 | 4 ++-- run.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/checks/check_extra768 b/checks/check_extra768 index 2c10e1dde1..b969702017 100644 --- a/checks/check_extra768 +++ b/checks/check_extra768 @@ -33,8 +33,8 @@ extra768(){ TASK_DEFINITION_ENV_VARIABLES_FILE="$SECRETS_TEMP_FOLDER/extra768-$TASK_DEFINITION-$regx-variables.txt" TASK_DEFINITION_ENV_VARIABLES=$($AWSCLI ecs $PROFILE_OPT --region $regx describe-task-definition --task-definition $taskDefinition --query 'taskDefinition.containerDefinitions[*].environment' --output text > $TASK_DEFINITION_ENV_VARIABLES_FILE) if [ -s $TASK_DEFINITION_ENV_VARIABLES_FILE ];then - # Implementation using https://github.com/Yelp/detect-secrets - FINDINGS=$(secretsDetector file $TASK_DEFINITION_ENV_VARIABLES_FILE) + # Implementation using https://github.com/Yelp/detect-secrets + FINDINGS=$(secretsDetector file $TASK_DEFINITION_ENV_VARIABLES_FILE) if [[ $FINDINGS -eq 0 ]]; then textPass "$regx: No secrets found in ECS task definition $TASK_DEFINITION variables" "$regx" # delete file if nothing interesting is there diff --git a/run.sh b/run.sh index 8f93055280..f14ac9e6e4 100755 --- a/run.sh +++ b/run.sh @@ -2,7 +2,7 @@ mkdir ~/.aws cat << AWS_CREDS > ~/.aws/credentials -[default] +[${ACCOUNT_ID}] credential_source = EcsContainer role_arn = ${ROLE_ARN} external_id = ${EXTERNAL_ID} @@ -10,7 +10,7 @@ external_id = ${EXTERNAL_ID} AWS_CREDS echo "Running prowler on ${ACCOUNT_ID}" -./prowler -p default "${CHECKS}" -M json > output.json +./prowler -p "${ACCOUNT_ID}" "${CHECKS}" -M json > output.json echo "Results:" cat output.json From 0c8ad299c505de9a6836dd6d44efec611e73f6c9 Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Mon, 20 Jan 2020 17:34:35 -0800 Subject: [PATCH 18/19] Catch the case of setting the port range to 0-65535 --- checks/check_extra748 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra748 b/checks/check_extra748 index dff4bf9fcb..50056980b5 100644 --- a/checks/check_extra748 +++ b/checks/check_extra748 @@ -18,7 +18,7 @@ CHECK_ALTERNATE_check748="extra748" extra748(){ for regx in $REGIONS; do - SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==null && ToPort==null)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) + SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`0` && ToPort==`65535`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do textFail "$regx: Found Security Group: $SG open to 0.0.0.0/0" "$regx" From 17210ab2087830e420c416f2c532db8c3d68ec2a Mon Sep 17 00:00:00 2001 From: Nimrod Kor Date: Thu, 30 Jan 2020 20:32:14 +0200 Subject: [PATCH 19/19] Fix /* in bucket ARN --- checks/check_extra764 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks/check_extra764 b/checks/check_extra764 index a479531e87..e6a4b95086 100644 --- a/checks/check_extra764 +++ b/checks/check_extra764 @@ -34,7 +34,7 @@ extra764(){ fi # https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-policy-for-config-rule/ - CHECK_BUCKET_STP_POLICY_PRESENT=$(cat $TEMP_STP_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}/*" '.Statement[]|select((((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Action=="s3:*" and (.Resource|type == "array") and (.Resource|map({(.):0})[]|has($arn)) and (.Resource|map({(.):0})[]|has($arn+"/*")) and .Condition.Bool."aws:SecureTransport" == "false")') + CHECK_BUCKET_STP_POLICY_PRESENT=$(cat $TEMP_STP_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}" '.Statement[]|select((((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Action=="s3:*" and (.Resource|type == "array") and (.Resource|map({(.):0})[]|has($arn)) and (.Resource|map({(.):0})[]|has($arn+"/*")) and .Condition.Bool."aws:SecureTransport" == "false")') if [[ $CHECK_BUCKET_STP_POLICY_PRESENT ]]; then textPass "Bucket $bucket has S3 bucket policy to deny requests over insecure transport" else