diff --git a/checks/check121 b/checks/check121 index 636cae73..3fbd5535 100644 --- a/checks/check121 +++ b/checks/check121 @@ -25,10 +25,10 @@ check121(){ LIST_USERS_KEY1_ACTIVE=$(for user in $LIST_USERS_KEY1_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$4,$9 }'|grep "true true$"|awk '{ print $1 }'|sed 's/[[:blank:]]+/,/g' ; done) if [[ $LIST_USERS_KEY1_ACTIVE ]]; then for user in $LIST_USERS_KEY1_ACTIVE; do - textFail "$user has never used Access Key 1" + textFail "User $user has never used access key 1" done else - textPass "No users found with Access Key 1 never used" + textPass "No users found with access key 1 never used" fi # List of USERS with KEY2 last_used_date as N/A LIST_USERS_KEY2_NA=$(for user in $LIST_USERS; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$16 }'|grep N/A |awk '{ print $1 }' ; done) @@ -36,9 +36,9 @@ check121(){ LIST_USERS_KEY2_ACTIVE=$(for user in $LIST_USERS_KEY2_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$4,$14 }'|grep "true true$" |awk '{ print $1 }' ; done) if [[ $LIST_USERS_KEY2_ACTIVE ]]; then for user in $LIST_USERS_KEY2_ACTIVE; do - textFail "$user has never used Access Key 2" + textFail "User $user has never used access key 2" done else - textPass "No users found with Access Key 2 never used" + textPass "No users found with access key 2 never used" fi } diff --git a/checks/check13 b/checks/check13 index 9f8f5a4c..929a6aa2 100644 --- a/checks/check13 +++ b/checks/check13 @@ -17,25 +17,5 @@ CHECK_ASFF_RESOURCE_TYPE_check13="AwsIamUser" CHECK_ALTERNATE_check103="check13" check13(){ - # "Ensure credentials unused for 90 days or greater are disabled (Scored)" - COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }') - # Only check Password last used for users with password enabled - if [[ $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED ]]; then - for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do - DATEUSED=$($AWSCLI iam list-users --query "Users[?UserName=='$i'].PasswordLastUsed" --output text $PROFILE_OPT --region $REGION | cut -d'T' -f1) - if [ "$DATEUSED" == "" ] - then - textFail "User \"$i\" has not logged in during the last 90 days" - else - HOWOLDER=$(how_older_from_today $DATEUSED) - if [ $HOWOLDER -gt "90" ];then - textFail "User \"$i\" has not logged in during the last 90 days" - else - textPass "User \"$i\" found with credentials used in the last 90 days" - fi - fi - done - else - textPass "No users found with password enabled" - fi + check_creds_used_in_last_days 90 } diff --git a/checks/check14 b/checks/check14 index 21e2be49..438b8364 100644 --- a/checks/check14 +++ b/checks/check14 @@ -30,7 +30,7 @@ check14(){ HOWOLDER=$(how_older_from_today $DATEROTATED1) if [ $HOWOLDER -gt "90" ];then - textFail " $user has not rotated access key1 in over 90 days" + textFail "$user has not rotated access key 1 in over 90 days" C14_NUM_USERS1=$(expr $C14_NUM_USERS1 + 1) fi done @@ -48,7 +48,7 @@ check14(){ DATEROTATED2=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep "^${user},"| awk -F, '{ print $15 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') HOWOLDER=$(how_older_from_today $DATEROTATED2) if [ $HOWOLDER -gt "90" ];then - textFail " $user has not rotated access key2 in over 90 days" + textFail "$user has not rotated access key 2 in over 90 days" C14_NUM_USERS2=$(expr $C14_NUM_USERS2 + 1) fi done diff --git a/checks/check_extra774 b/checks/check_extra774 index 64c096c3..b88bfad8 100644 --- a/checks/check_extra774 +++ b/checks/check_extra774 @@ -11,37 +11,12 @@ # CONDITIONS OF ANY KIND, either express or implied. See the License for the # specific language governing permissions and limitations under the License. CHECK_ID_extra774="7.74" -CHECK_TITLE_extra774="[extra774] Check if user have unused console login" +CHECK_TITLE_extra774="[extra774] Ensure credentials unused for 30 days or greater are disabled" CHECK_SCORED_extra774="NOT_SCORED" CHECK_TYPE_extra774="EXTRA" CHECK_ASFF_RESOURCE_TYPE_extra774="AwsIamUser" CHECK_ALTERNATE_check774="extra774" extra774(){ - MAX_DAYS=-30 - LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4,$5 }' |grep true | awk '{ print $1 }') - - for i in $LIST_USERS_WITH_PASSWORD_ENABLED; do - user=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$5 }' |grep "^$i " |awk '{ print $1 }') - last_login_date=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$5 }' |grep "^$i " |awk '{ print $2 }') - - # If the user has never logged into the console, their last login date is 'no_information'. See: - # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html#id_credentials_understanding_the_report_format - if [[ "${last_login_date}" == "no_information" ]]; then - user_created_date=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$3 }' |grep "^$i " |awk '{ print $2 }') - days_since_user_created=$(how_many_days_from_today ${user_created_date%T*}) - if [ "$days_since_user_created" -lt "$MAX_DAYS" ];then - textFail "User $user has never used console login since they were created over ${MAX_DAYS#-} days ago" - else - textInfo "User $user has not used console login since they were created" - fi - else - days_not_in_use=$(how_many_days_from_today ${last_login_date%T*}) - if [ "$days_not_in_use" -lt "$MAX_DAYS" ];then - textFail "User $user has not used console login for more than ${MAX_DAYS#-} days" - else - textPass "User $user has used console login in the past ${MAX_DAYS#-} days" - fi - fi - done + check_creds_used_in_last_days 30 } diff --git a/include/check_creds_last_used b/include/check_creds_last_used new file mode 100644 index 00000000..4f8633b3 --- /dev/null +++ b/include/check_creds_last_used @@ -0,0 +1,143 @@ +#!/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. + +# Set of functions for checking credential usage, following CIS 1.3 "Ensure credentials unused for 90 days or greater are disabled" rules +# but support a custom time-range to allow for stricter policies, e.g. extra774 + +# CSV Report Column Numbering +# See also https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html#id_credentials_understanding_the_report_format +# 1 - user +# 2 - arn +# 3 - user_creation_time +# 4 - password_enabled +# 5 - password_last_used +# 6 - password_last_changed +# 7 - password_next_rotation +# 8 - mfa_active +# 9 - access_key_1_active +# 10 - access_key_1_last_rotated +# 11 - access_key_1_last_used_date +# 12 - access_key_1_last_used_region +# 13 - access_key_1_last_used_service +# 14 - access_key_2_active +# 15 - access_key_2_last_rotated +# 16 - access_key_2_last_used_date +# 17 - access_key_2_last_used_region +# 18 - access_key_2_last_used_service +# 19 - cert_1_active +# 20 - cert_1_last_rotated +# 21 - cert_2_active +# 22 - cert_2_last_rotated + +# Check both passwords and access keys - e.g. CIS rule +check_creds_used_in_last_days() { + local max_days=$1 + + check_passwords_used_in_last_days "$max_days" + check_access_keys_used_in_last_days "$max_days" +} + +check_passwords_used_in_last_days() { + local max_days=$1 + + local user + local users_with_password_enabled + local last_login_date + local days_since_password_last_changed + local days_password_not_in_use + users_with_password_enabled=$(awk -F, '{ print $1,$4 }' "$TEMP_REPORT_FILE" | grep " true$" | awk '{ print $1 }') + # Only check password last used date for users with password enabled + if [[ $users_with_password_enabled ]]; then + for user in $users_with_password_enabled; do + last_login_date=$(awk -F, '{ print $1,$5 }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }') + + # If the user has never logged into the console, their last login date is 'no_information'. See: + # https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html#id_credentials_understanding_the_report_format + if [[ "${last_login_date}" == "no_information" ]]; then + user_password_changed_date=$(awk -F, '{ print $1,$6 }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }') + days_since_password_last_changed=$(how_older_from_today "${user_password_changed_date%T*}") + + # "When password_enabled is set to TRUE and password_last_used is set to no_information, ensure password_last_changed is less than X days ago" + if [[ "$days_since_password_last_changed" -ge "$max_days" ]]; then + textFail "User $user has never logged into the console since creation and their password not changed in the past ${max_days} days" + else + textInfo "User $user has not logged into the console since creation" + fi + else + days_password_not_in_use=$(how_older_from_today "${last_login_date%T*}") + + # "For each user having password_enabled set to TRUE, ensure password_last_used_date is less than X days ago." + if [[ "$days_password_not_in_use" -ge "$max_days" ]]; then + textFail "User $user has not logged into the console in the past ${max_days} days" + else + textPass "User $user has logged into the console in the past ${max_days} days" + fi + fi + done + else + textPass "No users found with password enabled" + fi +} + +check_access_keys_used_in_last_days() { + local max_days=$1 + + check_access_key_used_in_last_days "$max_days" 1 9 10 11 + check_access_key_used_in_last_days "$max_days" 2 14 15 16 +} + +check_access_key_used_in_last_days() { + local max_days=$1 + local access_key_name=$2 + local access_key_active_col=$3 + local access_key_last_rotated_col=$4 + local access_key_last_used_col=$5 + + local user + local users_with_access_key_enabled + local access_key_last_used_date + local access_key_last_rotated_date + local days_since_access_key_rotated + local days_since_access_key_used + users_with_access_key_enabled=$(awk -F, -v i="$access_key_active_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep " true$" | awk '{ print $1 }') + # Only check access key last used date for users with this access key enabled + if [[ $users_with_access_key_enabled ]]; then + for user in $users_with_access_key_enabled; do + access_key_last_used_date=$(awk -F, -v i="$access_key_last_used_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }') + + if [[ "${access_key_last_used_date}" == "N/A" ]]; then + access_key_last_rotated_date=$(awk -F, -v i="$access_key_last_rotated_col" '{ print $1,$i }' "$TEMP_REPORT_FILE" | grep "^$user " | awk '{ print $2 }') + days_since_access_key_rotated=$(how_older_from_today "${access_key_last_rotated_date%T*}") + + # "When a user having an access_key_x_active (where x is 1 or 2) to TRUE and corresponding access_key_x_last_used_date is set to N/A, + # ensure access_key_x_last_rotated is less than X days ago" + if [[ "$days_since_access_key_rotated" -ge "$max_days" ]]; then + textFail "User $user has never used access key $access_key_name since creation and not rotated it in the past ${max_days} days" + else + textInfo "User $user has not used access key $access_key_name since creation" + fi + else + days_since_access_key_used=$(how_older_from_today "${access_key_last_used_date%T*}") + + # "For each user having an access_key_1_active or access_key_2_active to TRUE, ensure the corresponding access_key_n_last_used_date is less than X days ago" + if [[ "$days_since_access_key_used" -ge "$max_days" ]]; then + textFail "User $user has not used access key $access_key_name in the past ${max_days} days" + else + textPass "User $user has used access key $access_key_name in the past ${max_days} days" + fi + fi + done + else + textPass "No users found with access key $access_key_name enabled" + fi +} diff --git a/prowler b/prowler index 10a02791..aa933066 100755 --- a/prowler +++ b/prowler @@ -203,6 +203,7 @@ trap "{ rm -f /tmp/prowler*.policy.*; }" EXIT . $PROWLER_DIR/include/scoring . $PROWLER_DIR/include/python_detector . $PROWLER_DIR/include/secrets_detector +. $PROWLER_DIR/include/check_creds_last_used . $PROWLER_DIR/include/check3x . $PROWLER_DIR/include/assume_role . $PROWLER_DIR/include/connection_tests