mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
274 lines
6.9 KiB
Bash
Executable File
274 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
# Prowler is a tool that provides automate auditing and hardening guidance of an AWS account.
|
||
# It is based on AWS-CLI commands. It follows guidelines present in the CIS Amazon
|
||
# Web Services Foundations Benchmark at:
|
||
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
|
||
|
||
# This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
|
||
# International Public License. The link to the license terms can be found at
|
||
# https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
|
||
#
|
||
# Author: Toni de la Fuente - @ToniBlyx - https://blyx.com/contact
|
||
|
||
# Prowler - Iron Maiden
|
||
#
|
||
# Walking through the city, looking oh so pretty
|
||
# I've just got to find my way
|
||
# See the ladies flashing
|
||
# All there legs and lashes
|
||
# I've just got to find my way...
|
||
|
||
OPTRED="[1;31m"
|
||
OPTNORMAL="[0;39m"
|
||
|
||
# Set the defaults for these getopts variables
|
||
REGION="us-east-1"
|
||
FILTERREGION=""
|
||
MAXITEMS=100
|
||
MONOCHROME=0
|
||
MODE="text"
|
||
SEP=','
|
||
KEEPCREDREPORT=0
|
||
EXITCODE=0
|
||
SCRIPT_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" )
|
||
TITLE_ID=""
|
||
TITLE_TEXT="CALLER ERROR - UNSET TITLE"
|
||
|
||
# Command usage menu
|
||
usage(){
|
||
echo "
|
||
USAGE:
|
||
`basename $0` [ -p <profile> -r <region> -h ]
|
||
|
||
Options:
|
||
-p <profile> specify your AWS profile to use (i.e.: default)
|
||
-r <region> specify an AWS region to direct API requests to
|
||
(i.e.: us-east-1), all regions are checked anyway
|
||
-c <check_id> specify a check number or group from the AWS CIS benchmark
|
||
(i.e.: "check11" for check 1.1, "check3" for entire section 3, "level1" for CIS Level 1 Profile Definitions or "forensics-ready")
|
||
-f <filterregion> specify an AWS region to run checks against
|
||
(i.e.: us-west-1)
|
||
-m <maxitems> specify the maximum number of items to return for long-running requests (default: 100)
|
||
-M <mode> output mode: text (defalut), mono, csv (separator is ","; data is on stdout; progress on stderr)
|
||
-k keep the credential report
|
||
-n show check numbers to sort easier
|
||
(i.e.: 1.01 instead of 1.1)
|
||
-l list all available checks only (does not perform any check)
|
||
-e exclude extras
|
||
-b do not print Prowler banner
|
||
-h this help
|
||
"
|
||
exit
|
||
}
|
||
|
||
while getopts ":hlkp:r:c:f:m:M:enb" OPTION; do
|
||
case $OPTION in
|
||
h )
|
||
usage
|
||
EXITCODE=1
|
||
exit $EXITCODE
|
||
;;
|
||
l )
|
||
PRINTCHECKSONLY=1
|
||
;;
|
||
k )
|
||
KEEPCREDREPORT=1
|
||
;;
|
||
p )
|
||
PROFILE=$OPTARG
|
||
;;
|
||
r )
|
||
REGION=$OPTARG
|
||
;;
|
||
c )
|
||
CHECKNUMBER=$OPTARG
|
||
;;
|
||
f )
|
||
FILTERREGION=$OPTARG
|
||
;;
|
||
m )
|
||
MAXITEMS=$OPTARG
|
||
;;
|
||
M )
|
||
MODE=$OPTARG
|
||
;;
|
||
n )
|
||
NUMERAL=1
|
||
;;
|
||
b )
|
||
BANNER=1
|
||
;;
|
||
e )
|
||
EXTRAS=1
|
||
;;
|
||
: )
|
||
echo ""
|
||
echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument"
|
||
usage
|
||
EXITCODE=1
|
||
exit $EXITCODE
|
||
;;
|
||
? )
|
||
echo ""
|
||
echo "$OPTRED ERROR!$OPTNORMAL Invalid option"
|
||
usage
|
||
EXITCODE=1
|
||
exit $EXITCODE
|
||
;;
|
||
esac
|
||
done
|
||
|
||
. include/colors
|
||
. include/os_detector
|
||
. include/aws_profile_loader
|
||
. include/awscli_detector
|
||
. include/outputs
|
||
. include/csv_header
|
||
. include/banner
|
||
. include/whoami
|
||
. include/credentials_report
|
||
|
||
# Get a list of all available AWS Regions
|
||
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \
|
||
--output text \
|
||
$PROFILE_OPT \
|
||
--region $REGION \
|
||
--region-names $FILTERREGION)
|
||
|
||
callCheck(){
|
||
if [[ $CHECKNUMBER ]];then
|
||
execute_check $CHECKNUMBER
|
||
# case "$CHECKNUMBER" in
|
||
# check11|check101 ) execute_check check11;;
|
||
# check12|check102 ) execute_check check12;;
|
||
# * )
|
||
# textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n";
|
||
# esac
|
||
cleanTemp
|
||
exit $EXITCODE
|
||
fi
|
||
}
|
||
|
||
# List only check tittles
|
||
if [[ $PRINTCHECKSONLY == "1" ]]; then
|
||
prowlerBanner
|
||
show_all_titles
|
||
exit $EXITCODE
|
||
fi
|
||
|
||
# Load all of the groups of checks inside groups folder named as "group*"
|
||
for group in $(ls groups/group*); do
|
||
. "$group"
|
||
done
|
||
|
||
# Load all of the checks inside checks folder named as "check*"
|
||
# this includes also extra checks since they are "check_extraNN"
|
||
for checks in $(ls checks/check*); do
|
||
. "$checks"
|
||
done
|
||
|
||
# Function to show the title of the check
|
||
# using this way instead of arrays to keep bash3 (osx) and bash4(linux) compatibility
|
||
show_check_title() {
|
||
# This would just call textTitle
|
||
local check_id=CHECK_ID_$1
|
||
local check_title=CHECK_TITLE_$1
|
||
local check_scored=CHECK_SCORED_$1
|
||
local check_type=CHECK_TYPE_$1
|
||
textTitle "${!check_id}" "${!check_title}" "${!check_scored}" "${!check_type}"
|
||
}
|
||
|
||
# Function to show the title of a group, by numeric id
|
||
show_group_title() {
|
||
# when csv mode is used, no group tittle is shown
|
||
if [[ "$MODE" != "csv" ]]; then
|
||
textTitle "${GROUP_NUMBER[$1]}" "${GROUP_TITLE[$1]}" "NOT_SCORED" "SUPPORT"
|
||
fi
|
||
}
|
||
|
||
# Function to execute the check
|
||
execute_check() {
|
||
# See if this is an alternate name for a check
|
||
# for example, we might have been passed 1.01 which is another name for 1.1
|
||
local alternate_name_var=CHECK_ALTERNATE_$1
|
||
local alternate_name=${!alternate_name_var}
|
||
|
||
if [ ${alternate_name} ];then
|
||
show_check_title ${alternate_name}
|
||
${alternate_name}
|
||
else
|
||
# Check to see if this is a real check
|
||
local check_id_var=CHECK_ID_$1
|
||
local check_id=${!check_id_var}
|
||
if [ ${check_id} ]; then
|
||
show_check_title $1
|
||
$1
|
||
else
|
||
textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n";
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Function to execute all checks in a group
|
||
execute_group() {
|
||
show_group_title $1
|
||
# run the checks in the group
|
||
IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$1]}
|
||
for i in ${CHECKS[@]}; do
|
||
execute_check $i
|
||
done
|
||
}
|
||
|
||
# Function to execute group by name
|
||
execute_group_by_name() {
|
||
for i in ${#GROUP_NAME[@]}; do
|
||
if [ "${GROUP_NAME[$i]}" == "$1" ]; then
|
||
execute_group $i
|
||
fi
|
||
done
|
||
}
|
||
|
||
# Function to execute all checks in all groups
|
||
execute_all() {
|
||
for i in ${#GROUP_TITLE[@]}; do
|
||
if [ "${GROUP_RUN_BY_DEFAULT[$i]}" == "Y" ]; then
|
||
execute_group $i
|
||
fi
|
||
done
|
||
}
|
||
|
||
# Function to show the titles of everything
|
||
show_all_titles() {
|
||
for i in ${#GROUP_TITLE[@]}; do
|
||
show_group_title $i
|
||
# Display the title of the checks
|
||
IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$i]}
|
||
for j in "${CHECKS[@]}"; do
|
||
show_check_title $j
|
||
done
|
||
done
|
||
}
|
||
|
||
### All functions defined above ... run the workflow
|
||
if [[ $MODE != "csv" ]]; then
|
||
prowlerBanner
|
||
printColorsCode
|
||
fi
|
||
getWhoami
|
||
genCredReport
|
||
saveReport
|
||
|
||
#callCheck
|
||
|
||
execute_all
|
||
|
||
|
||
# if [[ ! $EXTRAS ]]; then
|
||
# textTitle "7" "$TITLE7" "NOT_SCORED" "SUPPORT"
|
||
# execute_group 7
|
||
# fi
|
||
|
||
cleanTemp
|
||
exit $EXITCODE
|