Compare commits

..

215 Commits

Author SHA1 Message Date
Kay Agahd
6dee12450e [bugfix] check122 has to check not only string but also array values of the Action field (#2796) 2023-09-01 09:36:20 +02:00
Kay Agahd
eecb1dd8c3 fix(extra7131): exclude DocumentDB since AutoMinorVersionUpgrade is only for relational databases (#1714) 2023-01-18 17:19:03 +01:00
Nacho Rivera
74add0c151 fix(db connector): db connector validations (#1671) 2023-01-09 13:04:40 +01:00
Pepe Fagoaga
1cf86350bc feat(permissions): Update (#1444) 2022-12-20 09:40:19 +01:00
Sergio Garcia
e9b09790da feat(release): 2.12.1 2022-12-19 17:59:04 +01:00
Acknosyn
c74b4adf27 fix(): Fix CloudTrail trail S3 logging public bucket false positive result when trail bucket doesn't exist (#1505)
Co-authored-by: Francesco Badraun <francesco.badraun@zxsecurity.co.nz>
2022-12-14 16:32:38 +01:00
Kay Agahd
a769bb86d3 fix(check_extra723): Corrected some typos (#1511) 2022-11-22 08:55:38 +01:00
Nacho Rivera
f8a2527429 fix(README): include more details about db connector (#1507) 2022-11-21 09:38:04 +01:00
laura franzese
ae645718ad new copy pointing to prowlerpro (#1488) 2022-11-17 09:40:16 +01:00
Nacho Rivera
a0625dff2f fix(extra71): Modified wrong remediation (#1445) 2022-11-02 10:00:25 +01:00
Fennerr
37e9cbbabd fix(extra7195): Update title (#1440) 2022-10-31 14:33:25 +01:00
Pepe Fagoaga
8818f47333 fix(ecr): typo (#1438) 2022-10-27 19:47:06 +02:00
Pepe Fagoaga
3cffe72273 fix(ecr): Platform (#1437) 2022-10-27 19:30:59 +02:00
Nacho Rivera
135aaca851 fix(): delete old commented versions (#1436) 2022-10-27 16:19:33 +02:00
Nacho Rivera
cf8df051de feat(README): Include versions info (#1435)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-10-27 12:51:53 +02:00
Pepe Fagoaga
bef42f3f2d feat(release): Prowler 2.12.0 (#1434) 2022-10-27 12:10:37 +02:00
Nacho Rivera
3d86bf1705 fix(): Cloudtrail checks (#1433) 2022-10-27 11:47:00 +02:00
Olivier Gendron
5a43ec951a docs(spelling): Typo corrections (#1394) 2022-10-24 12:58:44 +02:00
Nacho Rivera
b0e6ab6e31 feat(stable tag): Inclusion of stable tag point to last release (#1419) 2022-10-20 08:01:00 +02:00
Nacho Rivera
b7fb38cc9e fix(extra7184): Error handling GetSnapshotLimits api call (#1411) 2022-10-17 14:03:55 +02:00
Nacho Rivera
f29f7fc239 fix(extra7183): Exception handling error UnsupportedOperationException (#1410) 2022-10-17 13:39:17 +02:00
Nacho Rivera
2997ff0f1c fix(extra77): Deleted resource id from exception results (#1409) 2022-10-17 13:17:51 +02:00
Nacho Rivera
11dc0aa5b2 feat(extra7111): Exception handling (#1408) 2022-10-17 12:51:09 +02:00
Sergio Garcia
8bddb9b265 fix(extra740): Remove additional info and fix max_items (#1405) 2022-10-14 11:37:31 +02:00
Sergio Garcia
689e292585 fix(region_bugs): Remove duplicate outputs (#1390) 2022-10-13 13:18:37 +02:00
Sergio Garcia
bff2aabda6 fix(missing permissions): Add missing permissions of checks (#1403) 2022-10-13 12:59:48 +02:00
Kay Agahd
4b29293362 fix(check_extra77): Add missing check_resource_id to the report (#1402) 2022-10-13 09:53:31 +02:00
Sergio Garcia
4e24103dc6 feat(slack): add Slack badge to README (#1401) 2022-10-13 09:42:06 +02:00
Sergio Garcia
3b90347849 fix(inventory): quick inventory input fixed (#1397)
Co-authored-by: sergargar <sergio@verica.io>
2022-10-10 17:21:46 +02:00
Pepe Fagoaga
6a7a037cec delete(shortcut.sh): Remove ScoutSuite (#1388) 2022-10-06 16:42:09 +02:00
Gábor Lipták
927c13b9c6 chore(actions): Bump Trufflehog to v3.13.0 (#1382) 2022-10-06 09:24:54 +02:00
Nacho Rivera
11cc8e998b fix(checks): Handle checks not returning result (#1383)
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-10-05 13:50:49 +02:00
Nacho Rivera
4a71739c56 Prwlr 879 fix prowler 2 x checks (#1380)
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-10-03 10:19:02 +02:00
Pepe Fagoaga
aedc5cd0ad fix(postgresql): Missing space (#1374) 2022-09-22 15:25:32 +02:00
Pepe Fagoaga
3d81307e56 fix(postgresql): Connector field (#1372) 2022-09-20 10:26:20 +02:00
Andrew Walker
918661bd7a Dockerfile build instructions (#1370) 2022-09-16 11:14:37 +02:00
Nacho Rivera
99f9abe3f6 feat(db-connector): Include UUID for findings ID (#1368) 2022-09-14 17:23:38 +02:00
Sergio Garcia
f2950764f0 feat(audit_id): add optional audit_id field to postgres connector (#1362)
Co-authored-by: sergargar <sergio@verica.io>
2022-09-13 13:29:19 +02:00
Pepe Fagoaga
d9777a68c7 chore(lint&test): Prowler 3.0 (#1357) 2022-09-01 16:37:10 +02:00
Richard Carpenter
2a4cc9a5f8 feat(group): CIS Critical Security Controls v8 (#1347)
Co-authored-by: sergargar <sergio@verica.io>
2022-08-31 15:14:04 +02:00
Ignacio Dominguez
1f0c210926 feat(extra7195): Added check for dependency confusion in codeartifact (#1329)
Co-authored-by: sergargar <sergio@verica.io>
2022-08-31 09:49:50 +02:00
JArmandoG
dd64c7d226 fix(check120): correct AWS support policy name (#1328)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-08-23 11:34:25 +01:00
JArmandoG
865f79f5b3 fix(quick_inventory): Handle math expression (#1283) 2022-08-05 12:55:07 +02:00
Pepe Fagoaga
1f8a4c1022 fix(credential_report): Do not generate for 117 and 118 (#1322) 2022-08-05 11:03:59 +02:00
Pepe Fagoaga
1e422f20aa fix(security-groups): Include TCP as the IpProtocol (#1323) 2022-08-05 11:02:35 +02:00
Pepe Fagoaga
29eda28bf3 docs(outputs): structure (#1313) 2022-08-04 10:05:08 +02:00
Pepe Fagoaga
f67f0cc66d chore(issues): Link Q&A (#1305) 2022-08-03 12:46:51 +02:00
Pepe Fagoaga
721cafa0cd fix(appstream): Handle timeout errors (#1296) 2022-08-02 12:30:53 +02:00
Kay Agahd
c1d60054e9 feat(extra780): Check for Cognito or SAML authentication on OpenSearch (#1291)
* extend check_extra780 to check for cognito or SAML authentication on opensearch

* chore(extra780): Error handling

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-08-02 09:51:38 +02:00
Pepe Fagoaga
b95b3f68d3 fix(permissions): Include missing appstream:DescribeFleets permission (#1278)
* fix(permissions): AWS AppStream

Include missing appstream:DescribeFleets permission

* fix(permissions): AWS AppStream
2022-08-02 09:47:04 +02:00
Jonathan Jenkyn
81b6e27eb8 feat(checks): Adding commands for checks 117 and 118 (#1289)
* Adding commands for checks 117 and 118

* fix(check118): Minor fixes and error handling

* fix(check117): Minor fixes and error handling

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-08-02 09:18:46 +02:00
William
d69678424b fix(extra712): changed Macie service detection (#1286)
* changed Macie service detection

* fix(regions): add region context and more.

Co-authored-by: sergargar <sergio@verica.io>
2022-07-28 13:53:54 -04:00
Pepe Fagoaga
a43c1aceec fix(check12): Improve remediation (#1281) 2022-07-26 14:37:35 -04:00
Pepe Fagoaga
f70cf8d81e fix(ci): Release edited (#1276) 2022-07-21 17:44:26 +02:00
Pepe Fagoaga
83b6c79203 fix(ci): Remove check-update (#1275) 2022-07-21 17:33:28 +02:00
Andrew
1192c038b2 docs(readme): Fix spelling errors (#1274) 2022-07-21 17:06:03 +02:00
Pepe Fagoaga
4ebbf6553e chore(release): 2.11.0 (#1272) 2022-07-21 10:48:32 +02:00
r8bhavneet
c501d63382 docs(readme): Fix spelling (#1271) 2022-07-21 10:42:40 +02:00
Toni de la Fuente
72d6d3f535 feat(inventory): Prowler quick inventory including IAM resources (#1258)
* chore(inventory): option included in main

* chore(inventory): quick inventory

* chore(inventory): functional version

* chore(inventory): functional version without echo

* Update include/quick_inventory

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* Update prowler

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>

* Added new line at report line

* Added more information from IAM

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-07-21 10:37:28 +02:00
Mitch
ddd34dc9cc fix(extra7173): Correct check and alternative name (#1270) 2022-07-20 08:36:34 +02:00
Sergio Garcia
03b1c10d13 fix(codebuild): expired token error using Instance Metadata
Co-authored-by: sergargar <sergio@verica.io>
2022-07-14 07:32:01 +02:00
Sergio Garcia
4cd5b8fd04 fix(codebuild): expired token error (#1262) 2022-07-12 07:38:44 +02:00
Phil Massyn
f0ce17182b feat(ecr_lifecycle): Check Lifecycle policy (#1260)
* Create checks_7194

ECR Repositories contain docker containers.  When automated processes create containers, the old ones tend to take up space.  With a lot of containers on the system, the account owner will be paying additional fees for images that are no longer in use.  By defining a lifecycle policy, a best practice is followed by reducing the total volume of data being consumed.

* Minor changes

* fix: Include bash header

Co-authored-by: n4ch04 <nachor1992@gmail.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-07-11 13:03:31 +02:00
Sergio Garcia
2a8a7d844b fix(apigatewayv2): handle BadRequestException (#1261)
Co-authored-by: sergargar <sergio@verica.io>
2022-07-11 12:21:39 +02:00
Pepe Fagoaga
ff33f426e5 docs(readme): Update inventory and checks (#1257)
* docs(readme): Update inventory and checks

* docs(readme): inventory path

Co-authored-by: Toni de la Fuente <toni@blyx.com>

* Update README.md

Co-authored-by: Toni de la Fuente <toni@blyx.com>

Co-authored-by: Toni de la Fuente <toni@blyx.com>
2022-07-08 12:42:46 +02:00
Toni de la Fuente
f691046c1f feat(inventory): Prowler quick inventory (#1245)
* chore(inventory): option included in main

* chore(inventory): quick inventory

* chore(inventory): functional version

* chore(inventory): functional version without echo

* Update include/quick_inventory

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* Update prowler

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>

* Added new line at report line

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-07-08 12:41:54 +02:00
Pepe Fagoaga
9fad8735b8 fix(Dockerfile): Prowler path (#1254) 2022-07-07 10:03:07 +02:00
Pepe Fagoaga
c632055517 fix(dockerfile): Python path (#1250) 2022-07-06 07:54:37 +02:00
Sergio Garcia
fd850790d5 fix(add-checks-regions): Missing regions in checks (#1247)
* add regions to checks

* add root as resource

Co-authored-by: sergargar <sergio@verica.io>
2022-07-04 09:46:08 +02:00
Sergio Garcia
912d5d7f8c fix(postgres): Fix postgres connector issues. (#1244)
* fix(postgres): Fix postgres connector issues.

* fix(postgres): Update documentation

Co-authored-by: sergargar <sergio@verica.io>
2022-06-30 18:12:33 +02:00
Pepe Fagoaga
d88a136ac3 feat(db-connector): Include env variables (#1236)
* feat(db-connector): Include env variables

* fix(typo)

* fix(psql-test): Remove PGPASSWORD
2022-06-30 08:43:41 +02:00
Pepe Fagoaga
172484cf08 feat(dockerfile): Include psql client in the Prowler scanner image (#1238)
* fix(dockerignore): Include files

* fix(dockerfile): Keep python2 and organize

* feat(db-connector): Include postgres dependencies

* feat(dockerfile): Include hadolint pre-commit
2022-06-30 08:28:29 +02:00
Pepe Fagoaga
821083639a fix(bckCredentials): Do nothing if no initial creds (#1239) 2022-06-29 16:52:08 +02:00
rajarshidas
e4f0f3ec87 feat(check): Ensure default internet access from Amazon AppStream fleet should be disabled. (#1233)
Co-authored-by: sergargar <sergio@verica.io>
2022-06-29 12:51:58 +02:00
rajarshidas
cc6302f7b8 feat(checks): Amazon AppStream checks (#1216)
Co-authored-by: sergargar <sergio@verica.io>
2022-06-29 12:31:42 +02:00
Bayron Carranza
c89fd82856 feat(check7164): 365 days or more in a Cloudwatch log retention should be consider PASS (#1240)
* 365 DAYS or More Retention log group in cloudwatch

* fix(extra7162): Fix comparison errors

Also include minor changes to texts

* fix(extra7162): Set as Pass log groups that never expires

* fix(typo)

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-06-28 08:58:41 +02:00
Pepe Fagoaga
0e29a92d42 fix(extra7162): Query AWS log groups using LOG_GROUP_RETENTION_PERIOD_DAYS (#1232) 2022-06-27 09:18:39 +02:00
Sergio Garcia
835d8ffe5d feat(Actions): Update refresh_aws_services_regions.yml (#1227) 2022-06-23 11:21:50 +02:00
Sergio Garcia
21ee2068a6 feat(actions): Create refresh_aws_services_regions.yml (#1225) 2022-06-23 11:07:26 +02:00
Sergio Garcia
0ad149942b fix(security_hub_integration): Treat failed findings as failed in Security Hub (#1219)
Co-authored-by: sergargar <sergio@verica.io>
2022-06-22 14:03:16 +02:00
Nacho Rivera
66305768c0 fix(instance metadata): Missing raw flag in JQ parser (#1214) 2022-06-21 10:14:12 +02:00
Sergio Garcia
05f98fe993 fix(junit_xml output): Fix XML output integration (#1210)
Co-authored-by: sergargar <sergio@verica.io>
2022-06-20 13:27:54 +02:00
rajarshidas
89416f37af feat(check): Directory Service - Ensure Radius server is using the recommended security protocol (#1203)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-06-20 11:37:02 +02:00
Pepe Fagoaga
7285ddcb4e feat(actions): Trigger (#1209) 2022-06-20 10:38:19 +02:00
Pepe Fagoaga
8993a4f707 fix(actions): Dockerfile path (#1208) 2022-06-20 09:22:40 +02:00
Sergio Garcia
633d7bd8a8 fix(instance-metadata): Credentials recovering (#1207)
* fix(instance-metadata): Credentials recovering

* fix(expr): Dockerfile to root and expr in SESSION_TIME_REMAINING.

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: sergargar <sergio@verica.io>
2022-06-17 14:23:56 +02:00
Pepe Fagoaga
3944ea2055 fix(session_duration): Use jq with TZ=UTC (#1195) 2022-06-15 13:25:43 +02:00
zsecducna
d85d0f5877 fix(extra767): Remove false positive (#1198)
* Remove fail positive

Exclude distributions that does not support `POST` requests

* fix(extra767): Overall changes

- Quoted and braced variables
- Fix DefaultCacheBehavior twice in a AWS CLI query
- Use regex =~ to match values

* fix(check767): Change textInfo for textPass

* fix(extra767): Include AWS CLI error handling

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-06-15 09:38:56 +02:00
Pepe Fagoaga
d32a7986a5 fix(shellcheck): Main variables (#1194) 2022-06-14 10:43:15 +02:00
Pepe Fagoaga
71813425bd fix(pre-commit): Recover shellcheck (#1193) 2022-06-14 07:46:12 +02:00
Pepe Fagoaga
da000b54ca refactor(Prowler): Main logic refactor (#1189)
* fix(aws_profile_loader): New functions

* fix(shellcheck): Temporary remove Shellcheck

* fix(aws_cli_detector): new function

* fix(jq_detector): New function

* fix(os_detector): New function

* fix(output_bucket): Output bucket input check in main

* fix(python_detector): deleted unused python detector

* fix(credentials): credentials check out of whoami

* [break]refactor(main)

* [BREAK] Get list of checks parsing all input options

* [break]refactor(main): execute checks functions

* [break]refactor(main): move functions to libs

* fix(validations): custom check validation and typos

* refactor(validate_options): Include comments

* fix(custom_checks): Minor fixes

* refactor(closing_files): include libraries

* refactor(loader): Include ignored checks

* refactor(main): Fix shellcheck

* refactor(loader): beautify

* refactor(monochrome): without variables

* refactor(modes): MODES array not needed

* refactor(whoami): get error from AWSCLI

* refactor(secrets-detector)

* refactor(secrets-detector)

* fix(html_scoring): html scoring was fixed.

* fix(load_checks_from_file)

* fix(color-code): Print if not mono

* fix(not extra): Fixed if EXCLUDE_CHECK_ID is empty

* fix(IFS): Restore default IFS once modes are parsed

* fix(bucket): validate before whoami

* fix(bucket): validate before whoami

Co-authored-by: n4ch04 <nachor1992@gmail.com>
Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Nacho Rivera <59198746+n4ch04@users.noreply.github.com>
2022-06-13 17:34:31 +02:00
Sergio Garcia
74a9b42d9f Update codebuild-prowler-audit-account-cfn.yaml (#1192) 2022-06-13 12:17:31 +02:00
Nacho Rivera
f9322ab3aa fix(outputs): Replace each comma occurrence before sending to csv file (#1188) 2022-06-08 09:19:50 +02:00
Pepe Fagoaga
5becaca2c4 fix(extra7187): Remove commas from the metadata (#1187) 2022-06-08 09:02:38 +02:00
Sergio Garcia
50a670fbc4 fix(codebuild_update): AWS CLI and permissions update. (#1183) 2022-06-07 14:49:22 +02:00
Sergio Garcia
48f405a696 fix(check119_remediation): Update check remediation text. (#1185) 2022-06-07 14:48:13 +02:00
Nacho Rivera
bc56c4242e refactor(outputs): Consolidate Prowler output functions (#1180)
* chore(db providers): db providers first version

* chore(db provider): added db provider setup into Readme

* fix(csv_line): csv_line out of conditional

* fix(README): text instead of varchar in table

* fix(help): help message extended

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>

* fix(typo): Update README.md

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(table): add if not exists

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(typo): Readme postgreSQL

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(db_connector): details to add a new provider

* fix(typo): Uppercase Prowler

Co-authored-by: Toni de la Fuente <toni@blyx.com>

* fix(prowler): deleted unused variable

* chore(checks): test db connector previous to send data

* chore(input tests): input tests moved to main

* fix(typo): Readme typos

* chore(table): table name from pgpass file

* fix(grep test): Added missing -E flag

* chore(table): check of table name and Readme

* chore(error colors): Added error colors

* chore(inputcheck): checks about mode and output inputs into main

* fix(inputs) custom output file name

* fix(outputs): comment profile

* chore(textXXX): both 3 textfunctions using general

* fix(allowlist): allowlist check included as function

* fix(headers): Add headers to certain output files

* fix(reformulate): change structure and delete comments

* fix(testing): Input test after load includes

* fix(variables): Added named vars

* fix(colors): Deleted unused colors

* fix(outputs): fine tuning

* fix(outputs): allowlist parameters read

* fix(allowlist): allowlist logic reformulated

* fix(REPREGION): REPREGION change by REGION_FROM_CHECK

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Toni de la Fuente <toni@blyx.com>
2022-06-06 12:56:21 +02:00
Pepe Fagoaga
1b63256b9c fix(assume_role): Use date instead of jq (#1181)
* fix(date): Use  instead of date

* fix(assume_role): Use date instead of jq

JQ parses datetimes using the local timezone and not UTC
2022-06-03 08:31:43 -07:00
Sergio Garcia
7930b449b3 fix(apigateway_iam): Error handling and permissions for extra745. (#1176)
* fix(apigateway_iam): Error handling and permissions for extra745.

* Update check_extra745

Co-authored-by: sergargar <sergio@verica.io>
2022-06-02 15:16:43 +02:00
Pepe Fagoaga
e5cd42da55 fix(typo): Max session duration error message (#1179) 2022-06-02 15:08:30 +02:00
Sergio Garcia
2a54bbf901 fix(SQS_encryption_type): Add SQS encryption types to extra728. (#1175)
* fix(SQS_encryption_type): Add SQS encryption types to extra728.

* Update check_extra728

* Update check_extra728

Co-authored-by: sergargar <sergio@verica.io>
2022-06-02 15:01:02 +02:00
Nacho Rivera
2e134ed947 feat(db_connector): Create a PostgreSQL connector for Prowler (#1171)
* chore(db providers): db providers first version

* chore(db provider): added db provider setup into Readme

* fix(csv_line): csv_line out of conditional

* fix(README): text instead of varchar in table

* fix(help): help message extended

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>

* fix(typo): Update README.md

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(table): add if not exists

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(typo): Readme postgreSQL

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* fix(db_connector): details to add a new provider

* fix(typo): Uppercase Prowler

Co-authored-by: Toni de la Fuente <toni@blyx.com>

* fix(prowler): deleted unused variable

* chore(checks): test db connector previous to send data

* chore(input tests): input tests moved to main

* fix(typo): Readme typos

* chore(table): table name from pgpass file

* fix(grep test): Added missing -E flag

* chore(table): check of table name and Readme

* chore(error colors): Added error colors

* fix(tablename): table name in readme

* fix(typo)

* fix(db_provider): Exact match

* fix(error): One line message

* chore(pgpass check): Check added for pgpass file

* fix(pgpass): pgpass file and permissions test

* fix(unused vars): Deleted unused vars

* fix(TOP_PID): Deleted TOP_PID unused var and comment

* chore(db tests): Credentials, database and table tests added

* fix(empty pgpass): Look for empty fields at pgpass file

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Toni de la Fuente <toni@blyx.com>
2022-06-02 13:15:14 +02:00
Sergio Garcia
ba727391db fix(runtimes_extra762): Detect nodejs versions correctly. (#1177)
Co-authored-by: sergargar <sergio@verica.io>
2022-06-02 13:14:22 +02:00
Sergio Garcia
d4346149fa fix(severity): High severity for check extra7185 (#1178) 2022-06-01 14:04:36 +02:00
Pepe Fagoaga
2637fc5132 feat(checks): New IAM privilege escalation check (#1168) 2022-06-01 13:58:31 +02:00
Sergio Garcia
ac5135470b fix(update_deprecate_runtimes): Deprecated runtimes for lambda were updated (#1170) 2022-05-31 17:03:11 +02:00
rajarshidas
613966aecf feat(check): Amazon WorkSpaces storage volumes are encrypted
If the value listed in the Volume Encryption column is Disabled, the selected AWS WorkSpaces instance volumes (root and user volumes) are not encrypted
2022-05-31 17:01:20 +02:00
Pepe Fagoaga
83ddcb9c39 feat(check): PublicAccessBlockConfiguration (#1167) 2022-05-31 16:54:05 +02:00
Lucas L Lopes
957c2433cf feat(checks): New checks for Directory Service (#1164) 2022-05-30 14:24:44 +02:00
Pepe Fagoaga
c10b367070 fix(actions): Bad PRO repository (#1163) 2022-05-25 12:47:22 +02:00
Pepe Fagoaga
432416d09e fix(checks): Severity for Lambda URL checks (#1162) 2022-05-25 12:22:42 +02:00
Pepe Fagoaga
dd7d25dc10 release: Prowler 2.10 (#1161) 2022-05-25 12:03:05 +02:00
Pepe Fagoaga
24c60a0ef6 fix(checks): Handle AWS Gov Cloud regions (#1160) 2022-05-25 12:01:58 +02:00
Andrea Di Fabio
f616c17bd2 feat(new): New custom check extra9999 to build a custom check on the fly (#1103) 2022-05-25 09:16:36 +02:00
Pepe Fagoaga
5628200bd4 fix(remediation): Fix remediation fields for checks (#1157) 2022-05-23 15:48:26 +02:00
Pepe Fagoaga
ae93527a6f fix(BucketLocation): Recover bucket policy using the right region endpoint (#1156) 2022-05-23 15:45:30 +02:00
Pepe Fagoaga
2939d5cadd feat(lambda-function): Checks for misconfigured function's URLs (#1148) 2022-05-23 10:46:19 +02:00
Pepe Fagoaga
e2c7bc2d6d fix(IllegalLocationConstraintException): Recover bucket policy using the right region endpoint (#1155) 2022-05-23 09:37:46 +02:00
Nacho Rivera
f4bae78730 Timestamp to date casting issues solved (#1154)
* fix(date): Deleted @ char before date argument

* fix(date): Use @ only when input is epoch
2022-05-23 09:28:56 +02:00
1vicente
d307898289 Update README.md (#1153)
pretty README.md
2022-05-19 12:14:11 +02:00
Pepe Fagoaga
879ac3ccb1 fix(actions): Ignore changes on Readme (#1149) 2022-05-17 16:09:55 +02:00
Sergio Garcia
cd41e73cbe fix(readme): Correct permissions for DynamoDB allowlist (#1147) 2022-05-17 12:33:49 +02:00
Pepe Fagoaga
47f1ca646e fix(typo): ArtifactBucket tags (#1145) 2022-05-17 09:08:11 +02:00
Charles Josiah Rusch Alandt
a18b18e530 K8s cronjob sample files (#1140) 2022-05-16 10:58:50 +02:00
Pepe Fagoaga
4d1ffbb652 fix(actions): tag and push (#1142) 2022-05-13 11:20:30 +02:00
Pepe Fagoaga
13423b137e fix(actions): Include AWS region (#1141)
* fix(actions): Include AWS regions

* fix(zip): Quiet output
2022-05-13 10:13:03 +02:00
Sergio Garcia
d60eea5e2f fix(copyToS3): Upload to S3 only when indicated (#1134)
Co-authored-by: sergargar <sergio@verica.io>
2022-05-12 17:30:49 +02:00
Pepe Fagoaga
39c7d3b69f fix(typo) (#1139) 2022-05-12 17:28:30 +02:00
Pepe Fagoaga
2de04f1374 fix(actions): Job permissions (#1138) 2022-05-12 17:24:15 +02:00
Pepe Fagoaga
5fb39ea316 fix(actions): Trigger on PR (#1136)
* fix(actions): Include checkout

* fix(actions): version name

* fix(actions): fix branch

* fix(actions): version name

* fix(actions): PR trigger
2022-05-12 17:20:11 +02:00
Pepe Fagoaga
55640ecad2 fix(actions): Github token permissions (#1135) 2022-05-12 16:46:06 +02:00
Pepe Fagoaga
69d3867895 feat(actions): Upload Prowler containers to registries (#1132)
* feat(actions): Upload Prowler latest to dockerhub

* feat(upload-container): Action to Public Registries

* feat(upload-container): Include env secrets

* feat(actions): Include Docker linters

* feat(linters): include pre-commit

* fix(names)
2022-05-12 16:37:46 +02:00
Sergio Garcia
210f44f66f fix(custom-file-in-bucket): Custom file names are also support for S3 output. (#1129) 2022-05-11 10:16:29 +02:00
Sergio Garcia
b78e4ad6a1 fix(allowlist_db): Improve DynamoDB regex for allowlisting. (#1127) 2022-05-06 13:46:53 +02:00
stof
4146566f92 feat(assume-role): Properly handle External ID variable 2022-05-05 16:10:52 +02:00
Sergio Garcia
4e46dfb068 feat(add_prowler_pro_banner): include Prowler Pro banner in README (#1119)
* feat(add_prowler_pro_banner): include Prowler Pro banner in README

Context
Include Prowler Pro banner in README.md

Description
Add Prowler Pro banner in README.md for giving visibility to the Enterprise version of Prowler.

License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

* Update README.md
2022-05-03 16:24:23 +02:00
Milton Torasso
13c96a80db feat(deployment): Serverless multi account Prowler with SecurityHub Integration (#1113) 2022-05-03 13:41:56 +02:00
Sergio Garcia
de77a33341 fix(allowlist_db): Improve DynamoDB regex for allowlisting. (#1126)
Co-authored-by: sergargar <sergio@verica.io>
2022-05-03 11:57:23 +02:00
ChrisGoKim
295bb74acf fix(additions-policy): Updated multi-org ProwlerRole.yaml (#1123) 2022-05-03 11:34:12 +02:00
Jens Brey
59abd2bd5b check_extra7113: Fix wrong listing of RDS instances in regions without databases (#1124)
Co-authored-by: Jens Brey <jens.brey@allcloud.io>
2022-05-03 11:31:23 +02:00
Sergio Garcia
ecbfbfb960 fix(allowlist_db): Improve DynamoDB regex for allowlisting. (#1125)
Co-authored-by: sergargar <sergio@verica.io>
2022-05-03 11:31:12 +02:00
Justin Plock
04e5804665 Update CloudFormation template for CodeBuild (#1114) 2022-05-03 09:14:38 +02:00
Pepe Fagoaga
681d0d9538 feat(group7): Include extra7178 (#1121) 2022-04-29 14:26:19 +02:00
Pepe Fagoaga
8bfd9c0e62 feat(emr): Check BlockPublicAccessConfiguration for EMR (#1120) 2022-04-29 14:23:54 +02:00
Divyanshu
95df9bc316 feat(checks): New group and checks for Codebuild and EMR (#1112) 2022-04-29 14:19:04 +02:00
Sergio Garcia
d08576f672 feat(add_prowler_pro_banner): include Prowler Pro banner in README.md (#1117) 2022-04-28 17:28:52 +02:00
Sergio Garcia
aa16bf4084 feat(dynamodb_allowlist): Support DynamoDB tables ARN for allowlist input (#1118)
* feat(dynamodb_allowlist): Support dynamodb tables arn for allowlist input.

* feat(allowlist): Include logging messages for input file

* fix(allowlist): Modify DynamoDB key name

Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-04-28 17:04:44 +02:00
Pepe Fagoaga
432632d981 chore(release): 2.9.0 (#1109) 2022-04-13 13:54:53 +02:00
Pepe Fagoaga
d6ade7694e chore(allowlist): Rename references (#1108)
* chore(allowlist): rename file

* chore(allowlist): remove old references
2022-04-13 11:31:50 +02:00
n4ch04
c9e282f236 IAM check116 and check122 modified to log also PASS results (#1107)
* fix(check116): Fixed logic to include resource_id of passed users

* fix(check122): Changed logic check to include explicit pass records
2022-04-12 19:54:51 +02:00
carterjones
5b902a1329 fix typo: publiccly -> publicly (#1106) 2022-04-12 18:12:26 +02:00
Pepe Fagoaga
fc7c932169 fix(extra7147): Handle unsupported AWS regions for Glacier (#1101) 2022-04-11 16:10:23 +02:00
n4ch04
819b52687c Replace comma from csv input info (#1102)
* fix(output): replace comma from csv input info

* fix(outputs): parameter expansion done in echo to csv
2022-04-11 16:04:47 +02:00
Sergio Garcia
28fff104a1 feat(S3_in_w_x_flags): Support S3 URIs for custom checks paths and whitelist files. (#1090)
* feat(S3_in_w_x_flags): Support S3 URIs for custom checks paths and whitelist files.

* feat(S3_in_w_x_flags): README document was updated.

* Update README.md

* Update README.md

* Update README.md

* Update README.md

Co-authored-by: Toni de la Fuente <toni@blyx.com>
Co-authored-by: Sergio Garcia Garcia
2022-04-07 14:37:02 -04:00
n4ch04
07b2b0de5a fix(extra764): Deleted temp file refs (#1089) 2022-04-07 17:03:32 +02:00
nealalan
4287b7ac61 check empty array in SECURITYGROUPS object (#1099)
* check empty array in SECURITYGROUPS object

Logic is only checking an object to see if it is null. This should be checking for the array in the object to see if it is empty.

* Replace new conditional with the old one

* Update check_extra75

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2022-04-07 10:57:29 -04:00
Sergio Garcia
734331d5bc fix(extra764): NoSuchBucket error properly handled. (#1094) 2022-03-31 15:35:17 +02:00
Sergio Garcia
5de2bf7a83 fix(extra7172): IllegalLocationConstraintException properly handled. (#1093) 2022-03-31 14:40:32 +02:00
Sergio Garcia
1744921a0a fix(extra792): TLS1.3 policies added as secure (#1091) 2022-03-30 17:50:00 +02:00
Andrew Grangaard
d4da64582c docs(tf-quickstart): Update example code for terraform-quickstart (#1086)
+ use primary repository rather than fork.
+ use default branch.
+ fixed a missing character typos.
+ remove blank end-of-line spaces.

@singergs: thanks for adding this code and the video.
2022-03-30 09:15:38 +02:00
Andrea Di Fabio
d94acfeb17 New Extra Check - Detect SGs created by the EC2 Launch Wizard (#1081)
* new check

* added check to group

* fixed name

* added testpass logic

* Fixed a few issues

* Fixed more issues

* Updated to add extended information

* Added new line at end of file

* Fixed Spelling

* fix(title): Update title name

* refactor(style): Minor changes

Co-authored-by: Andrea Di Fabio <adifabio@amazon.com>
2022-03-29 10:06:44 +02:00
soffensive
fcc14012da Update check_extra736, is missing $PROFILE_OPT (#1084)
$PROFILE_OPT was missing in one aws command
2022-03-29 09:11:41 +02:00
Lucas Moura
cc8cbc89fd Fix typo extra729 and extra740 (#1083)
* Fix typo on remediation

* Fix typo on remediation description
2022-03-29 08:58:06 +02:00
Sergio Garcia
8582e40edf fix(secrets_library): Verify if detect-secrets library is missing (#1080) 2022-03-25 13:19:05 +01:00
Toni de la Fuente
1e87ef12ee feat(new_version): Prowler 2.8.1 (#1082) 2022-03-25 12:58:06 +01:00
Pepe Fagoaga
565200529f fix(detect-secrets): Include missing colon to link values (#1078) 2022-03-22 13:53:36 +01:00
Sergio Garcia
198c7f48ca fix(bucket_region): check extra764 doesn't handle bucket region properly (#1077)
* fix(bucket_region): check extra764 doesn't handle bucket region properly
2022-03-18 11:51:42 +01:00
Toni de la Fuente
8105e63b79 fix(extras-group): Add extra7172 to group extras (#1074) 2022-03-16 18:39:16 +01:00
Sergio Garcia
3932296fcf feat(new_version): Prowler 2.8.0 (#1073) 2022-03-16 18:15:57 +01:00
David Childs
cb0d9d3392 fix(filter-region): Support comma separated regions (#1071)
* regions separated by a comma deliminator

* Update README.md

Co-authored-by: Toni de la Fuente <toni@blyx.com>

* Update README.md

Co-authored-by: David Childs <d.childs@elsevier.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Toni de la Fuente <toni@blyx.com>
2022-03-16 17:49:04 +01:00
Pepe Fagoaga
4b90eca21e docs(readme): Fix typo (#1072) 2022-03-16 16:54:27 +01:00
Toni de la Fuente
365b396f9a feat(metadata): Include account metadata in Prowler assessments (#1049)
* Add support for organizations accounts metadata part 1

* Add support for organizations accounts metadata part 2

* Add gathering account metadata from org

* chore(prowler): get accounts metadata

Use assume_role backing up normal assumed credentials to assume management account and then restore it to old ones

* fix(orgs metadata): deleted assume_role_orgs

* refactor(organization_metadata)

Reformulate to extract AWS Organizations metadata

* doc(org_metadata): include required -R in usage

* docs(org-metadata): Update README

Co-authored-by: n4ch04 <nachor1992@gmail.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-03-16 16:27:19 +01:00
plarso
c526c61d5e Fix(check122): Error when policy name contains commas (#1067)
* check122 - Support policy names with commas

* Requested changes
2022-03-16 15:06:12 +01:00
Leonardo Azize Martins
c4aff56f23 fix(extra760): Improve error handling (#1055)
* Fix AccessDenied issue

* fix(extra760): Error handling

* Fix merge conflict

* Improve code style

* Fix grep filter

* Fix bash variable expansion

* Fix grep logic to handle zip file
2022-03-16 14:57:37 +01:00
n4ch04
d9e0ed1cc9 fix(check_extra7161): fixed check title (#1068) 2022-03-15 12:30:57 +01:00
Leonardo Azize Martins
e77cd6b2b2 fix: Change lower case from bash variable expansion to tr (#1064)
* fix(extra715): Change lower case from bash variable expansion to tr command

* fix: Change from bash variable expansion to tr command

* Change the way to handle lower case
2022-03-15 08:22:22 +01:00
n4ch04
f04b174e67 fix(whitelist): Whitelist logic reformulated (#1061)
* fix(whitelist): Whitelist logic reformulated again

* chore(whitelist): reformulate style
2022-03-11 10:15:58 +01:00
Pepe Fagoaga
0c1c641765 fix(extra776): Handle image tag commas and json output (#1063) 2022-03-08 19:08:40 +01:00
xxxMinoo
d44f6bf20f fix: extra7167 Advanced Shield and CloudFront bug parsing None output without distributions (#1062)
* fix: not to flag as finding for account without cloudfront distributions

* fix: output empty for None from cloudfront list-distributions

* fix: extra7167 Advanced Shield and CloudFront bug parsing None output without distributions

Co-authored-by: moo.xin.foo <moo.xin.foo@accenture.com>
2022-03-08 14:09:20 +01:00
Leonardo Azize Martins
1fa62cf417 fix(extra758): Reduce API calls. Print correct instance state. (#1057)
* fix(extra758): Reduce API calls. Print correct instance state.

* feat(oldage-format): Include comment

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-03-08 10:45:02 +01:00
Toni de la Fuente
d8d2ddd9e7 Revert "fix: extra7167 Advanced Shield and CloudFront bug parsing None output without distributions (#1053)" (#1054)
This reverts commit f3ff8369c3.
2022-03-04 13:12:03 +01:00
xxxMinoo
f3ff8369c3 fix: extra7167 Advanced Shield and CloudFront bug parsing None output without distributions (#1053)
* fix: not to flag as finding for account without cloudfront distributions

* fix: output empty for None from cloudfront list-distributions

Co-authored-by: moo.xin.foo <moo.xin.foo@accenture.com>
2022-03-04 10:25:47 +01:00
Roman Mueller
99d1868827 Add right region to CSV if access is denied (#1045) 2022-03-02 16:32:35 +01:00
Andrea Di Fabio
31cefa5b3c Make python3 default in Dockerfile (#1043) 2022-03-02 16:21:28 +01:00
Andrea Di Fabio
2d5ac8238b Added Timestamp to secrets related 5 checks (#1041) 2022-03-02 15:56:02 +01:00
Leonardo Azize Martins
248cc9d68b Fix(extra771): jq fail when policy action is an array (#1031)
* Fix error handling and policy output

* Fix jq filter when Action is an array

Fix jq select condition to handle Action as string or as array.
Add error handling.
When fail, print policies as just one line.

* Double quote variables to prevent globbing and word splitting

* Replace comma character from json by word comma
2022-03-02 15:04:18 +01:00
Leonardo Azize Martins
5f0a5b57f9 Fix(ES): Improve AWS CLI query and add error handling for ElasticSearch/OpenSearch checks (#1032)
* Fix CLI query and add error handling

Check extra781, extra782, extra783, extra784 and extra785

* Fix CLI query, add error handling, combine AWS CLI calls when possible

Checks related to Opensearch/ElasticSearch.

* Fix CLI query, add error handling, combine AWS CLI calls when possible

Checks related to Opensearch/ElasticSearch.
2022-03-02 12:44:24 +01:00
Pepe Fagoaga
86367fca3f fix: remove PR automatic labels (#1044) 2022-02-15 08:19:40 +01:00
Pepe Fagoaga
07be3c21bf docs(templates): Include triage label (#1042) 2022-02-14 17:47:53 +01:00
n4ch04
3097ba6c66 fix(include/outputs):Rolling back whitelist checking to RE check (#1037)
* fix(include/outputs):Rolling back whitelist checking to RE check

* fix(include/ouputs): Clarified variable assignation coming from argument
2022-02-14 13:04:47 +01:00
n4ch04
b4669a2a72 fix(check41/42): Added tcp protocol filter to query (#1035)
* fix(check41/42): Added tcp protocol filter to query

* Include {} in vars

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

* Include {} in vars

Co-authored-by: Pepe Fagoaga <pepe@verica.io>

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
2022-02-11 10:54:32 +01:00
Leonardo Azize Martins
e8848ca261 docs: Improve check_sample examples, add general comments (#1039) 2022-02-10 17:58:50 +01:00
Pepe Fagoaga
5c6902b459 fix(extra730): Handle invalid date formats checking ACM certificates (#1033) 2022-02-09 17:56:55 +01:00
Leonardo Azize Martins
9b772a70a1 Fix(extra7141): Error handling and include missing policy (#1024)
* Fix AccessDenied issue when get document

Add check to validate access denied when get document from SSM.
Add missing action permission to allow ssm:GetDocument.

* Double quote variables to prevent globbing and word splitting
2022-02-09 16:01:01 +01:00
Pepe Fagoaga
6c12a3e1e0 fix(extra736): Recover Customer Managed KMS keys (#1036) 2022-02-09 10:05:57 +01:00
jeffmaley
c6f0351e9c feat(check): New check7172 for S3 Bucket ACLs (#1023)
* added check7172 for s3 bucket acls

* Added more errors to error handling and an access check for s3

* Removed extra api call

Co-authored-by: Jeff Maley <jeff.maley@symmetry-systems.com>
2022-02-07 16:58:18 -05:00
Martin Muller
7e90389dab fix: CFN codebuild example (#1030)
Since 2.7.0 this template failed:

```
An error occurred (AccessDeniedException) when calling the GetSubscriptionState operation: User: arn:aws:sts::863046042023:assumed-role/prowler-codebuild-role/AWSCodeBuild-2c3151c9-7c5d-4618-94e5-0234bddce775 is not authorized to perform: shield:GetSubscriptionState on resource: arn:aws:shield::863046042023:subscription/* because no identity-based policy allows the shield:GetSubscriptionState action
       INFO! No AWS Shield Advanced subscription found. Skipping check. 
7.167 [extra7167] Check if Cloudfront distributions are protected by AWS Shield Advanced - shield [Medium]
```

I aligned it with https://github.com/prowler-cloud/prowler/blob/master/iam/prowler-additions-policy.json#L19 .
2022-02-04 12:09:53 -05:00
n4ch04
30ce25300f fix(include/outputs): Whitelist logic reformulated to exactly match input (#1029)
* fix(inlcude/outputs) Whitelist logic reformulated to exactly match input

* fix(include/outputs): Changed name of iterative variable that browses whitelisted values

* fix(include/outputs): Deleted missing echo and include and put variables in brackets
2022-02-04 12:07:48 -05:00
Pepe Fagoaga
26caf51619 fix(CODEOWNERS): Rename team (#1027) 2022-02-04 12:05:43 -05:00
Leonardo Azize Martins
3ecb5dbce6 Fix AccessDenied issue (#1025) 2022-02-04 12:05:10 -05:00
Toni de la Fuente
1d409d04f2 Fix (extra7148 and add action #1017 (#1021) 2022-02-04 11:58:22 -05:00
Daniel Lorch
679414418e Fix: when prowler exits with a non-zero status, the remainder of the block is not executed (#1015)
* Fix: when prowler exits with a non-zero status, the remainder of the block is not executed

* Fix: do not trigger exit code 3 on failed checks, so that the remainder of the block is executed
2022-02-02 17:45:56 +01:00
Daniel Lorch
b26370d508 Typo (breaking change) (#1010)
Co-authored-by: Daniel Lorch <lorchda@amazon.ch>
2022-02-02 11:13:31 -05:00
Daniel Lorch
72b30aa45f Skip packages with broken dependencies when upgrading system (#1009)
Co-authored-by: Daniel Lorch <lorchda@amazon.ch>
2022-02-02 11:12:58 -05:00
n4ch04
d9561d5d22 fix(check32): filterName base64encoded to avoid space problems in filter names (#1020)
* fix(check32): filterName base64encoded to avoid space problems in filter names

* fix(check32): base64 decoding atomic expression

* fix(check32): Variable enclosing

Co-authored-by: Nacho Rivera <nachor1992@gmail>
2022-02-02 11:09:38 -05:00
Mike Stewart
3d0ab4684f docs(docker): Docker hub references (#1018) 2022-02-02 16:45:07 +01:00
Daniel Lorch
29a071c98e docs(whitelist): Add examples for Control Tower resources (#1013) 2022-02-02 13:36:02 +01:00
Daniel Lorch
0ac7064d80 fix(ftr-group): Visual formatting (#1012) 2022-02-02 13:17:46 +01:00
Toni de la Fuente
dcd55dbb8f Add badges 2022-01-28 12:12:59 +01:00
Jan Sepke
441dc11963 Fix issue #1002 (#1007)
regression in extra793

Co-authored-by: Jan Sepke <jan.sepke@jungheinrich.de>
2022-01-28 11:01:32 +01:00
Jan Sepke
21a8193510 Fix issue #1001 (#1006)
regression in extra75

Co-authored-by: Jan Sepke <jan.sepke@jungheinrich.de>
2022-01-27 15:13:07 +01:00
Pepe Fagoaga
3b9a3ff6be Include codeowners template (#1005)
* docs(templates): include Codeowners

* docs(templates): update PR template
2022-01-27 12:58:14 +01:00
Toni de la Fuente
c5f12f0a6c Fix issue #1002 (#1004) 2022-01-27 12:27:41 +01:00
Pepe Fagoaga
90565099bd Change references from toniblyx to prowler-cloud (#1003)
Co-authored-by: Toni de la Fuente <toni@blyx.com>
2022-01-27 12:17:38 +01:00
Toni de la Fuente
2b2814723f Prowler 2.7.0 - Brave (#998)
* Extra7161 EFS encryption at rest check

* Added check_extra7162 which checks if Log groups have 365 days retention

* fixed code to handle all regions and formatted output

* changed check title, resource type and service name as well as making the code more dynamic

* Extra7161 EFS encryption at rest check

* New check_extra7163 Secrets Manager key rotation enabled

* New check7160 Enabled AutomaticVersionUpgrade on RedShift Cluster

* Update ProwlerRole.yaml to have same permissions as util/org-multi-account/ProwlerRole.yaml

* Fix link to quicksight dashboard

* Install detect-secrets (e.g. for check_extra742)

* Updating check_extra7163 with requested changes

* fix(assumed-role): Check if -T and -A options are set

* docs(Readme): `-T` option is not mandatory

* fix(assume-role): Handle AWS STS CLI errors

* fix(assume-role): Handle AWS STS CLI errors

* Update group25_FTR

When trying to run the group 25 (Amazon FTR related security checks) nothing happens, after looking at the code there is a misconfiguration in 2 params: GROUP_RUN_BY_DEFAULT[9] and GROUP_CHECKS[9]. Updating values to 25 fixed the issue.

* Update README.md

broken link for capital letters in group file (group25_FTR)

* #938 issue assume_role multiple times should be fixed

* Label 2.7.0-1December2021 for tests

* Fixed error that appeared if the number of findings was very high.

* Adjusted the batch to only do 50 at a time. 100 caused capacity issues. Also added a check for an edge case where if the updated findings was a multiple of the batch size, it would throw an error for attempting to import 0 findings.

* Added line to delete the temp folder after everything is done.

* New check 7164 Check if Cloudwatch log groups are protected by AWS KMS@maisenhe

* updated CHECK_RISK

* Added checks extra7160,extra7161,extra7162,extra7163 to group Extras

* Added checks extra7160,extra7161,extra7162,extra7163 to group Extras

* Added issue templates

* New check 7165 DynamoDB: DAX encrypted at rest @Daniel-Peladeau

* New check 7165 DynamoDB: DAX encrypted at rest @Daniel-Peladeau

* Fix #963 check 792 to force json in ELB queries

* Fix #957 check 763 had us-east-1 region hardcoded

* Fix #962 check 7147 ALTERNATE NAME

* Fix #940 handling error when can not list functions

* Added new checks 7164 and 7165 to group extras

* Added invalid check or group id to the error message #962

* Fix Broken Link

* Add docker volume example to README.md

* Updated Dockerfile to use amazonlinux container

* Updated Dockerfile with AWS cli v2

* Added upgrade to the RUN

* Added cache purge to Dockerfile

* Backup AWS Credentials before AssumeRole and Restore them before CopyToS3

* exporting the ENV variables

* fixed bracket

* Improved documentation for install process

* fix checks with comma issues

* Added -D option to copy to S3 with the initial AWS credentials

* Cosmetic variable name change

* Added $PROFILE_OPT to CopyToS3 commands

* remove commas

* removed file as it is not needed

* Improved help usage options -h

* Fixed CIS LEVEL on 7163 through 7165

* When performing a restoreInitialAWSCredentials, unset the credentials ENV variables if they were never set

* New check 7166 Elastic IP addresses with associations are protected by AWS Shield Advanced

* New check 7167 Cloudfront distributions are protected by AWS Shield Advanced

* New check 7168 Route53 hosted zones are protected by AWS Shield Advanced

* New check 7169 Global accelerators are protected by AWS Shield Advanced

* New check 7170 Application load balancers are protected by AWS Shield Advanced

* New check 7171 Classic load balancers are protected by AWS Shield Advanced

* Include example for global resources

* Add AWS Advance Shield protection checks corrections

* Added Shield actions GetSubscriptionState and DescribeProtection

* Added Shield actions GetSubscriptionState and DescribeProtection

* docs(templates): Improve bug template with more info (#982)

* Removed echoes after role chaining fix

* Changed Route53 checks7152 and 7153 to INFO when no domains found

* Changed Route53 checks 7152 and 7153 title to clarify

* Added passed security groups in output to check 778

* Added passed security groups and updated title to check 777

* Added FAIL as error handling when SCP prevents queries to regions

* Label version 2.7.0-6January2022

* Updated .dockerignore with .github/

* Fix: issue #758 and #984

* Fix: issue #741 CloudFront and real-time logs

* Fix issues #971 set all as INFO instead of FAIL when no access to resource

* Fix: issue #986

* Add additional action permissions for Glue and Shield Advanced checks @lazize

* Add extra shield action permission

Allows the shield:GetSubscriptionState action

* Add permission actions

Make sure all files where permission actions are necessary will have the same actions

* Fix: Credential chaining from environment variables @lazize #996f

If profile is not defined, restore original credentials from environment variables,
if they exists, before assume-role

* Lable version 2.7.0-24January2022

Co-authored-by: Lee Myers <ichilegend@gmail.com>
Co-authored-by: Chinedu Obiakara <obiakac@amazon.com>
Co-authored-by: Daniel Peladeau <dcpeladeau@gmail.com>
Co-authored-by: Jonathan Lozano <jonloza@amazon.com>
Co-authored-by: Daniel Lorch <dlorch@gmail.com>
Co-authored-by: Pepe Fagoaga <jose.fagoaga@smartprotection.com>
Co-authored-by: Israel <6672089+lopmoris@users.noreply.github.com>
Co-authored-by: root <halfluke@gmail.com>
Co-authored-by: nikirby <nikirby@amazon.com>
Co-authored-by: Joel Maisenhelder <maisenhe@gmail.com>
Co-authored-by: RT <35173068+rtcms@users.noreply.github.com>
Co-authored-by: Andrea Di Fabio <39841198+sectoramen@users.noreply.github.com>
Co-authored-by: Joseph de CLERCK <clerckj@amazon.fr>
Co-authored-by: Michael Dickinson <45626543+michael-dickinson-sainsburys@users.noreply.github.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
Co-authored-by: Leonardo Azize Martins <lazize@users.noreply.github.com>
2022-01-24 13:49:47 +01:00
233 changed files with 6730 additions and 2818 deletions

View File

@@ -1,6 +1,17 @@
# Ignore git files
.git/
.github/
# Ignore Dodckerfile
Dockerfile
# Ignore hidden files
.pre-commit-config.yaml
.dockerignore
.gitignore
.pytest*
.DS_Store
# Ignore output directories
output/
junit-reports/

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @prowler-cloud/prowler-team

View File

@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: "[Bug]: "
labels: ["bug", "triage"]
labels: bug, status/needs-triage
assignees: ''
---

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions & Help
url: https://github.com/prowler-cloud/prowler/discussions/categories/q-a
about: Please ask and answer questions here.

View File

@@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
labels: enhancement, status/needs-triage
assignees: ''
---

View File

@@ -1 +1,13 @@
### Context
Please include relevant motivation and context for this PR.
### Description
Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change.
### License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

View File

@@ -0,0 +1,209 @@
name: build-lint-push-containers
on:
push:
branches:
- 'master'
paths-ignore:
- '.github/**'
- 'README.md'
release:
types: [published, edited]
env:
AWS_REGION_STG: eu-west-1
AWS_REGION_PLATFORM: eu-west-1
AWS_REGION_PRO: us-east-1
IMAGE_NAME: prowler
LATEST_TAG: latest
STABLE_TAG: stable
TEMPORARY_TAG: temporary
DOCKERFILE_PATH: ./Dockerfile
jobs:
# Lint Dockerfile using Hadolint
# dockerfile-linter:
# runs-on: ubuntu-latest
# steps:
# -
# name: Checkout
# uses: actions/checkout@v3
# -
# name: Install Hadolint
# run: |
# VERSION=$(curl --silent "https://api.github.com/repos/hadolint/hadolint/releases/latest" | \
# grep '"tag_name":' | \
# sed -E 's/.*"v([^"]+)".*/\1/' \
# ) && curl -L -o /tmp/hadolint https://github.com/hadolint/hadolint/releases/download/v${VERSION}/hadolint-Linux-x86_64 \
# && chmod +x /tmp/hadolint
# -
# name: Run Hadolint
# run: |
# /tmp/hadolint util/Dockerfile
# Build Prowler OSS container
container-build:
# needs: dockerfile-linter
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/build-push-action@v2
with:
# Without pushing to registries
push: false
tags: ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }}
file: ${{ env.DOCKERFILE_PATH }}
outputs: type=docker,dest=/tmp/${{ env.IMAGE_NAME }}.tar
-
name: Share image between jobs
uses: actions/upload-artifact@v2
with:
name: ${{ env.IMAGE_NAME }}.tar
path: /tmp/${{ env.IMAGE_NAME }}.tar
# Lint Prowler OSS container using Dockle
# container-linter:
# needs: container-build
# runs-on: ubuntu-latest
# steps:
# -
# name: Get container image from shared
# uses: actions/download-artifact@v2
# with:
# name: ${{ env.IMAGE_NAME }}.tar
# path: /tmp
# -
# name: Load Docker image
# run: |
# docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
# docker image ls -a
# -
# name: Install Dockle
# run: |
# VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
# grep '"tag_name":' | \
# sed -E 's/.*"v([^"]+)".*/\1/' \
# ) && curl -L -o dockle.deb https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.deb \
# && sudo dpkg -i dockle.deb && rm dockle.deb
# -
# name: Run Dockle
# run: dockle ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }}
# Push Prowler OSS container to registries
container-push:
# needs: container-linter
needs: container-build
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read # This is required for actions/checkout
steps:
-
name: Get container image from shared
uses: actions/download-artifact@v2
with:
name: ${{ env.IMAGE_NAME }}.tar
path: /tmp
-
name: Load Docker image
run: |
docker load --input /tmp/${{ env.IMAGE_NAME }}.tar
docker image ls -a
-
name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to Public ECR
uses: docker/login-action@v2
with:
registry: public.ecr.aws
username: ${{ secrets.PUBLIC_ECR_AWS_ACCESS_KEY_ID }}
password: ${{ secrets.PUBLIC_ECR_AWS_SECRET_ACCESS_KEY }}
env:
AWS_REGION: ${{ env.AWS_REGION_PRO }}
-
name: Configure AWS Credentials -- STG
if: github.event_name == 'push'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION_STG }}
role-to-assume: ${{ secrets.STG_IAM_ROLE_ARN }}
role-session-name: build-lint-containers-stg
-
name: Login to ECR -- STG
if: github.event_name == 'push'
uses: docker/login-action@v2
with:
registry: ${{ secrets.STG_ECR }}
-
name: Configure AWS Credentials -- PLATFORM
if: github.event_name == 'release'
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION_PLATFORM }}
role-to-assume: ${{ secrets.STG_IAM_ROLE_ARN }}
role-session-name: build-lint-containers-pro
-
name: Login to ECR -- PLATFORM
if: github.event_name == 'release'
uses: docker/login-action@v2
with:
registry: ${{ secrets.PLATFORM_ECR }}
-
# Push to master branch - push "latest" tag
name: Tag (latest)
if: github.event_name == 'push'
run: |
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ env.LATEST_TAG }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
-
# Push to master branch - push "latest" tag
name: Push (latest)
if: github.event_name == 'push'
run: |
docker push ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ env.LATEST_TAG }}
docker push ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
docker push ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}
-
# Tag the new release (stable and release tag)
name: Tag (release)
if: github.event_name == 'release'
run: |
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ github.event.release.tag_name }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ env.STABLE_TAG }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
docker tag ${{ env.IMAGE_NAME }}:${{ env.TEMPORARY_TAG }} ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
-
# Push the new release (stable and release tag)
name: Push (release)
if: github.event_name == 'release'
run: |
docker push ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ github.event.release.tag_name }}
docker push ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }}
docker push ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }}
docker push ${{ secrets.PLATFORM_ECR }}/${{ secrets.PLATFORM_ECR_REPOSITORY }}:${{ env.STABLE_TAG }}
docker push ${{ secrets.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
docker push ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${{ env.STABLE_TAG }}
-
name: Delete artifacts
if: always()
uses: geekyeggo/delete-artifact@v1
with:
name: ${{ env.IMAGE_NAME }}.tar

18
.github/workflows/find-secrets.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: find-secrets
on: pull_request
jobs:
trufflehog:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@v3.13.0
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD

41
.github/workflows/pull-request.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Lint & Test
on:
push:
branches:
- 'prowler-3.0-dev'
pull_request:
branches:
- 'prowler-3.0-dev'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pipenv
pipenv install
- name: Bandit
run: |
pipenv run bandit -q -lll -x '*_test.py,./contrib/' -r .
- name: Safety
run: |
pipenv run safety check
- name: Vulture
run: |
pipenv run vulture --exclude "contrib" --min-confidence 100 .
- name: Test with pytest
run: |
pipenv run pytest -n auto

View File

@@ -0,0 +1,50 @@
# This is a basic workflow to help you get started with Actions
name: Refresh regions of AWS services
on:
schedule:
- cron: "0 9 * * *" #runs at 09:00 UTC everyday
env:
GITHUB_BRANCH: "prowler-3.0-dev"
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
with:
ref: ${{ env.GITHUB_BRANCH }}
- name: setup python
uses: actions/setup-python@v2
with:
python-version: 3.9 #install the python needed
# Runs a single command using the runners shell
- name: Run a one-line script
run: python3 util/update_aws_services_regions.py
# Create pull request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "feat(regions_update): Update regions for AWS services."
branch: "aws-services-regions-updated"
labels: "status/waiting-for-revision, severity/low"
title: "feat(regions_update): Changes in regions for AWS services."
body: |
### Description
This PR updates the regions for AWS services.
### License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

29
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,29 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.3.0
hooks:
- id: check-merge-conflict
- id: check-yaml
args: ['--unsafe']
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: 'README.md'
- id: no-commit-to-branch
- id: pretty-format-json
args: ['--autofix']
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.8.0
hooks:
- id: shellcheck
- repo: https://github.com/hadolint/hadolint
rev: v2.10.0
hooks:
- id: hadolint
name: Lint Dockerfiles
description: Runs hadolint to lint Dockerfiles
language: system
types: ["dockerfile"]
entry: hadolint

64
Dockerfile Normal file
View File

@@ -0,0 +1,64 @@
# Build command
# docker build --platform=linux/amd64 --no-cache -t prowler:latest -f ./Dockerfile .
# hadolint ignore=DL3007
FROM public.ecr.aws/amazonlinux/amazonlinux:latest
LABEL maintainer="https://github.com/prowler-cloud/prowler"
ARG USERNAME=prowler
ARG USERID=34000
# Prepare image as root
USER 0
# System dependencies
# hadolint ignore=DL3006,DL3013,DL3033
RUN yum upgrade -y && \
yum install -y python3 bash curl jq coreutils py3-pip which unzip shadow-utils && \
yum clean all && \
rm -rf /var/cache/yum
RUN amazon-linux-extras install -y epel postgresql14 && \
yum clean all && \
rm -rf /var/cache/yum
# Create non-root user
RUN useradd -l -s /bin/bash -U -u ${USERID} ${USERNAME}
USER ${USERNAME}
# Python dependencies
# hadolint ignore=DL3006,DL3013,DL3042
RUN pip3 install --upgrade pip && \
pip3 install --no-cache-dir boto3 detect-secrets==1.0.3 && \
pip3 cache purge
# Set Python PATH
ENV PATH="/home/${USERNAME}/.local/bin:${PATH}"
USER 0
# Install AWS CLI
RUN curl https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip && \
unzip -q awscliv2.zip && \
aws/install && \
rm -rf aws awscliv2.zip
# Keep Python2 for yum
RUN sed -i '1 s/python/python2.7/' /usr/bin/yum
# Set Python3
RUN rm /usr/bin/python && \
ln -s /usr/bin/python3 /usr/bin/python
# Set working directory
WORKDIR /prowler
# Copy all files
COPY . ./
# Set files ownership
RUN chown -R prowler .
USER ${USERNAME}
ENTRYPOINT ["./prowler"]

648
README.md
View File

@@ -1,17 +1,38 @@
<p align="center">
<img align="center" src="docs/images/prowler-pro-dark.png#gh-dark-mode-only" width="150" height="36">
<img align="center" src="docs/images/prowler-pro-light.png#gh-light-mode-only" width="15%" height="15%">
</p>
<p align="center">
<b><i>&nbsp&nbsp&nbsp See all the things you and your team can do with ProwlerPro at <a href="https://prowler.pro">prowler.pro</a></i></b>
</p>
<hr>
<p align="center">
<img src="https://user-images.githubusercontent.com/3985464/113734260-7ba06900-96fb-11eb-82bc-d4f68a1e2710.png" />
</p>
<p align="center">
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog"><img alt="Slack Shield" src="https://img.shields.io/badge/slack-prowler-brightgreen.svg?logo=slack"></a>
<a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/toniblyx/prowler"></a>
<a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker" src="https://img.shields.io/docker/cloud/build/toniblyx/prowler"></a>
<a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker" src="https://img.shields.io/docker/image-size/toniblyx/prowler"></a>
<a href="https://gallery.ecr.aws/o4g1s5r6/prowler"><img width="120" height=19" alt="AWS ECR Gallery" src="https://user-images.githubusercontent.com/3985464/151531396-b6535a68-c907-44eb-95a1-a09508178616.png"></a>
<a href="https://github.com/prowler-cloud/prowler"><img alt="Repo size" src="https://img.shields.io/github/repo-size/prowler-cloud/prowler"></a>
<a href="https://github.com/prowler-cloud/prowler"><img alt="Lines" src="https://img.shields.io/tokei/lines/github/prowler-cloud/prowler"></a>
<a href="https://github.com/prowler-cloud/prowler/issues"><img alt="Issues" src="https://img.shields.io/github/issues/prowler-cloud/prowler"></a>
<a href="https://github.com/prowler-cloud/prowler/releases"><img alt="Version" src="https://img.shields.io/github/v/release/prowler-cloud/prowler?include_prereleases"></a>
<a href="https://github.com/prowler-cloud/prowler/releases"><img alt="Version" src="https://img.shields.io/github/release-date/prowler-cloud/prowler"></a>
<a href="https://github.com/prowler-cloud/prowler"><img alt="Contributors" src="https://img.shields.io/github/contributors-anon/prowler-cloud/prowler"></a>
<a href="https://github.com/prowler-cloud/prowler"><img alt="License" src="https://img.shields.io/github/license/prowler-cloud/prowler"></a>
<a href="https://twitter.com/ToniBlyx"><img alt="Twitter" src="https://img.shields.io/twitter/follow/toniblyx?style=social"></a>
</p>
# Prowler - AWS Security Tool
<p align="center">
<i>Prowler</i> is an Open Source security tool to perform AWS security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness. It contains more than 240 controls covering CIS, PCI-DSS, ISO27001, GDPR, HIPAA, FFIEC, SOC2, AWS FTR, ENS and custom security frameworks.
</p>
[![Discord Shield](https://discordapp.com/api/guilds/807208614288818196/widget.png?style=shield)](https://discord.gg/UjSMCVnxSB)
[![Docker Pulls](https://img.shields.io/docker/pulls/toniblyx/prowler)](https://hub.docker.com/r/toniblyx/prowler)
[![aws-ecr](https://user-images.githubusercontent.com/3985464/141164269-8cfeef0f-6b62-4c99-8fe9-4537986a1613.png)](https://gallery.ecr.aws/o4g1s5r6/prowler)
## Table of Contents
- [Description](#description)
- [Prowler Container Versions](#prowler-container-versions)
- [Features](#features)
- [High level architecture](#high-level-architecture)
- [Requirements and Installation](#requirements-and-installation)
@@ -20,7 +41,8 @@
- [Advanced Usage](#advanced-usage)
- [Security Hub integration](#security-hub-integration)
- [CodeBuild deployment](#codebuild-deployment)
- [Whitelist/allowlist or remove FAIL from resources](#whitelist-or-allowlist-or-remove-a-fail-from-resources)
- [Allowlist](#allowlist-or-remove-a-fail-from-resources)
- [Inventory](#inventory)
- [Fix](#how-to-fix-every-fail)
- [Troubleshooting](#troubleshooting)
- [Extras](#extras)
@@ -29,7 +51,7 @@
- [HIPAA Checks](#hipaa-checks)
- [Trust Boundaries Checks](#trust-boundaries-checks)
- [Multi Account and Continuous Monitoring](util/org-multi-account/README.md)
- [Add Custom Checks](#add-custom-checks)
- [Custom Checks](#custom-checks)
- [Third Party Integrations](#third-party-integrations)
- [Full list of checks and groups](/LIST_OF_CHECKS_AND_GROUPS.md)
- [License](#license)
@@ -38,21 +60,32 @@
Prowler is a command line tool that helps you with AWS security assessment, auditing, hardening and incident response.
It follows guidelines of the CIS Amazon Web Services Foundations Benchmark (49 checks) and has more than 100 additional checks including related to GDPR, HIPAA, PCI-DSS, ISO-27001, FFIEC, SOC2 and others.
It follows guidelines of the CIS Amazon Web Services Foundations Benchmark (49 checks) and has more than 190 additional checks including related to GDPR, HIPAA, PCI-DSS, ISO-27001, FFIEC, SOC2 and others.
Read more about [CIS Amazon Web Services Foundations Benchmark v1.2.0 - 05-23-2018](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf)
## Prowler container versions
The available versions of Prowler are the following:
- latest: in sync with master branch (bear in mind that it is not a stable version)
- <x.y.z> (release): you can find the releases [here](https://github.com/prowler-cloud/prowler/releases), those are stable releases.
- stable: this tag always point to the latest release.
The container images are available here:
- [DockerHub](https://hub.docker.com/r/toniblyx/prowler/tags)
- [AWS Public ECR](https://gallery.ecr.aws/o4g1s5r6/prowler)
## Features
+200 checks covering security best practices across all AWS regions and most of AWS services and related to the next groups:
+240 checks covering security best practices across all AWS regions and most of AWS services and related to the next groups:
- Identity and Access Management [group1]
- Logging [group2]
- Logging [group2]
- Monitoring [group3]
- Networking [group4]
- CIS Level 1 [cislevel1]
- CIS Level 2 [cislevel2]
- Extras *see Extras section* [extras]
- Extras _see Extras section_ [extras]
- Forensics related group of checks [forensics-ready]
- GDPR [gdpr] Read more [here](#gdpr-checks)
- HIPAA [hipaa] Read more [here](#hipaa-checks)
@@ -67,9 +100,10 @@ With Prowler you can:
- Get a direct colorful or monochrome report
- A HTML, CSV, JUNIT, JSON or JSON ASFF (Security Hub) format report
- Send findings directly to Security Hub
- Send findings directly to the Security Hub
- Run specific checks and groups or create your own
- Check multiple AWS accounts in parallel or sequentially
- Get an inventory of your AWS resources
- And more! Read examples below
## High level architecture
@@ -77,6 +111,7 @@ With Prowler you can:
You can run Prowler from your workstation, an EC2 instance, Fargate or any other container, Codebuild, CloudShell and Cloud9.
![Prowler high level architecture](https://user-images.githubusercontent.com/3985464/109143232-1488af80-7760-11eb-8d83-726790fda592.jpg)
## Requirements and Installation
Prowler has been written in bash using AWS-CLI underneath and it works in Linux, Mac OS or Windows with cygwin or virtualization. Also requires `jq` and `detect-secrets` to work properly.
@@ -84,140 +119,143 @@ Prowler has been written in bash using AWS-CLI underneath and it works in Linux,
- Make sure the latest version of AWS-CLI is installed. It works with either v1 or v2, however _latest v2 is recommended if using new regions since they require STS v2 token_, and other components needed, with Python pip already installed.
- For Amazon Linux (`yum` based Linux distributions and AWS CLI v2):
```
sudo yum update -y
sudo yum remove -y awscli
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
sudo yum install -y python3 jq git
sudo pip3 install detect-secrets==1.0.3
git clone https://github.com/toniblyx/prowler
```
```
sudo yum update -y
sudo yum remove -y awscli
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
sudo yum install -y python3 jq git
sudo pip3 install detect-secrets==1.0.3
git clone https://github.com/prowler-cloud/prowler
```
- For Ubuntu Linux (`apt` based Linux distributions and AWS CLI v2):
```
sudo apt update
sudo apt install python3 python3-pip jq git zip
pip install detect-secrets==1.0.3
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
git clone https://github.com/toniblyx/prowler
```
> NOTE: detect-secrets Yelp version is no longer supported, the one from IBM is mantained now. Use the one mentioned below or the specific Yelp version 1.0.3 to make sure it works as expected (`pip install detect-secrets==1.0.3`):
```sh
pip install "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets"
```
```
sudo apt update
sudo apt install python3 python3-pip jq git zip
pip install detect-secrets==1.0.3
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
git clone https://github.com/prowler-cloud/prowler
```
AWS-CLI can be also installed it using other methods, refer to official documentation for more details: <https://aws.amazon.com/cli/>, but `detect-secrets` has to be installed using `pip` or `pip3`.
> NOTE: detect-secrets Yelp version is no longer supported, the one from IBM is mantained now. Use the one mentioned below or the specific Yelp version 1.0.3 to make sure it works as expected (`pip install detect-secrets==1.0.3`):
```sh
pip install "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets"
```
AWS-CLI can be also installed it using other methods, refer to official documentation for more details: <https://aws.amazon.com/cli/>, but `detect-secrets` has to be installed using `pip` or `pip3`.
- Once Prowler repository is cloned, get into the folder and you can run it:
```sh
cd prowler
./prowler
```
```sh
cd prowler
./prowler
```
- Since Prowler users AWS CLI under the hood, you can follow any authentication method as described [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-precedence). Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly (or instance profile/role):
```sh
aws configure
```
```sh
aws configure
```
or
or
```sh
export AWS_ACCESS_KEY_ID="ASXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXX"
export AWS_SESSION_TOKEN="XXXXXXXXX"
```
```sh
export AWS_ACCESS_KEY_ID="ASXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXX"
export AWS_SESSION_TOKEN="XXXXXXXXX"
```
- Those credentials must be associated to a user or role with proper permissions to do all checks. To make sure, add the AWS managed policies, SecurityAudit and ViewOnlyAccess, to the user or role being used. Policy ARNs are:
- Those credentials must be associated to a user or role with proper permissions to do all checks. To make sure, add the AWS managed policies, SecurityAudit and ViewOnlyAccess, to the user or role being used. Policy ARNs are:
```sh
arn:aws:iam::aws:policy/SecurityAudit
arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
```
```sh
arn:aws:iam::aws:policy/SecurityAudit
arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
```
> Additional permissions needed: to make sure Prowler can scan all services included in the group *Extras*, make sure you attach also the custom policy [prowler-additions-policy.json](https://github.com/toniblyx/prowler/blob/master/iam/prowler-additions-policy.json) to the role you are using. If you want Prowler to send findings to [AWS Security Hub](https://aws.amazon.com/security-hub), make sure you also attach the custom policy [prowler-security-hub.json](https://github.com/toniblyx/prowler/blob/master/iam/prowler-security-hub.json).
> Additional permissions needed: to make sure Prowler can scan all services included in the group _Extras_, make sure you attach also the custom policy [prowler-additions-policy.json](https://github.com/prowler-cloud/prowler/blob/master/iam/prowler-additions-policy.json) to the role you are using. If you want Prowler to send findings to [AWS Security Hub](https://aws.amazon.com/security-hub), make sure you also attach the custom policy [prowler-security-hub.json](https://github.com/prowler-cloud/prowler/blob/master/iam/prowler-security-hub.json).
## Usage
1. Run the `prowler` command without options (it will use your environment variable credentials if they exist or will default to using the `~/.aws/credentials` file and run checks over all regions when needed. The default region is us-east-1):
```sh
./prowler
```
```sh
./prowler
```
Use `-l` to list all available checks and the groups (sections) that reference them. To list all groups use `-L` and to list content of a group use `-l -g <groupname>`.
Use `-l` to list all available checks and the groups (sections) that reference them. To list all groups use `-L` and to list content of a group use `-l -g <groupname>`.
If you want to avoid installing dependencies run it using Docker:
If you want to avoid installing dependencies run it using Docker:
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest
```
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest
```
In case you want to get reports created by Prowler use docker volume option like in the example below:
```sh
docker run -ti --rm -v /your/local/output:/prowler/output --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -g hipaa -M csv,json,html
```
In case you want to get reports created by Prowler use docker volume option like in the example below:
```sh
docker run -ti --rm -v /your/local/output:/prowler/output --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -g hipaa -M csv,json,html
```
1. For custom AWS-CLI profile and region, use the following: (it will use your custom profile and run checks over all regions when needed):
```sh
./prowler -p custom-profile -r us-east-1
```
```sh
./prowler -p custom-profile -r us-east-1
```
1. For a single check use option `-c`:
```sh
./prowler -c check310
```
```sh
./prowler -c check310
```
With Docker:
With Docker:
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest "-c check310"
```
```sh
docker run -ti --rm --name prowler --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest "-c check310"
```
or multiple checks separated by comma:
or multiple checks separated by comma:
```sh
./prowler -c check310,check722
```
```sh
./prowler -c check310,check722
```
or all checks but some of them:
or all checks but some of them:
```sh
./prowler -E check42,check43
```
```sh
./prowler -E check42,check43
```
or for custom profile and region:
or for custom profile and region:
```sh
./prowler -p custom-profile -r us-east-1 -c check11
```
```sh
./prowler -p custom-profile -r us-east-1 -c check11
```
or for a group of checks use group name:
or for a group of checks use group name:
```sh
./prowler -g group1 # for iam related checks
```
```sh
./prowler -g group1 # for iam related checks
```
or exclude some checks in the group:
or exclude some checks in the group:
```sh
./prowler -g group4 -E check42,check43
```
```sh
./prowler -g group4 -E check42,check43
```
Valid check numbers are based on the AWS CIS Benchmark guide, so 1.1 is check11 and 3.10 is check310
Valid check numbers are based on the AWS CIS Benchmark guide, so 1.1 is check11 and 3.10 is check310
### Regions
By default, Prowler scans all opt-in regions available, that might take a long execution time depending on the number of resources and regions used. Same applies for GovCloud or China regions. See below Advance usage for examples.
Prowler has two parameters related to regions: `-r` that is used query AWS services API endpoints (it uses `us-east-1` by default and required for GovCloud or China) and the option `-f` that is to filter those regions you only want to scan. For example if you want to scan Dublin only use `-f eu-west-1` and if you want to scan Dublin and Ohio `-f 'eu-west-1 us-east-s'`, note the single quotes and space between regions.
Prowler has two parameters related to regions: `-r` that is used query AWS services API endpoints (it uses `us-east-1` by default and required for GovCloud or China) and the option `-f` that is to filter those regions you only want to scan. For example if you want to scan Dublin only use `-f eu-west-1` and if you want to scan Dublin and Ohio `-f eu-west-1,us-east-1`, note the regions are separated by a comma delimiter (it can be used as before with `-f 'eu-west-1,us-east-1'`).
## Screenshots
@@ -241,80 +279,260 @@ Prowler has two parameters related to regions: `-r` that is used query AWS servi
1. If you want to save your report for later analysis thare are different ways, natively (supported text, mono, csv, json, json-asff, junit-xml and html, see note below for more info):
```sh
./prowler -M csv
```
```sh
./prowler -M csv
```
or with multiple formats at the same time:
or with multiple formats at the same time:
```sh
./prowler -M csv,json,json-asff,html
```
```sh
./prowler -M csv,json,json-asff,html
```
or just a group of checks in multiple formats:
or just a group of checks in multiple formats:
```sh
./prowler -g gdpr -M csv,json,json-asff
```
```sh
./prowler -g gdpr -M csv,json,json-asff
```
or if you want a sorted and dynamic HTML report do:
or if you want a sorted and dynamic HTML report do:
```sh
./prowler -M html
```
```sh
./prowler -M html
```
Now `-M` creates a file inside the prowler `output` directory named `prowler-output-AWSACCOUNTID-YYYYMMDDHHMMSS.format`. You don't have to specify anything else, no pipes, no redirects.
Now `-M` creates a file inside the prowler `output` directory named `prowler-output-AWSACCOUNTID-YYYYMMDDHHMMSS.format`. You don't have to specify anything else, no pipes, no redirects.
or just saving the output to a file like below:
or just saving the output to a file like below:
```sh
./prowler -M mono > prowler-report.txt
```
```sh
./prowler -M mono > prowler-report.txt
```
To generate JUnit report files, include the junit-xml format. This can be combined with any other format. Files are written inside a prowler root directory named `junit-reports`:
To generate JUnit report files, include the junit-xml format. This can be combined with any other format. Files are written inside a prowler root directory named `junit-reports`:
```sh
./prowler -M text,junit-xml
```
```sh
./prowler -M text,junit-xml
```
>Note about output formats to use with `-M`: "text" is the default one with colors, "mono" is like default one but monochrome, "csv" is comma separated values, "json" plain basic json (without comma between lines) and "json-asff" is also json with Amazon Security Finding Format that you can ship to Security Hub using `-S`.
> Note about output formats to use with `-M`: "text" is the default one with colors, "mono" is like default one but monochrome, "csv" is comma separated values, "json" plain basic json (without comma between lines) and "json-asff" is also json with Amazon Security Finding Format that you can ship to Security Hub using `-S`.
or save your report in an S3 bucket (this only works for text or mono. For csv, json or json-asff it has to be copied afterwards):
To save your report in an S3 bucket, use `-B` to define a custom output bucket along with `-M` to define the output format that is going to be uploaded to S3:
```sh
./prowler -M mono | aws s3 cp - s3://bucket-name/prowler-report.txt
```
```sh
./prowler -M csv -B my-bucket/folder/
```
When generating multiple formats and running using Docker, to retrieve the reports, bind a local directory to the container, e.g.:
> In the case you do not want to use the assumed role credentials but the initial credentials to put the reports into the S3 bucket, use `-D` instead of `-B`. Make sure that the used credentials have s3:PutObject permissions in the S3 path where the reports are going to be uploaded.
```sh
docker run -ti --rm --name prowler --volume "$(pwd)":/prowler/output --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -M csv,json
```
When generating multiple formats and running using Docker, to retrieve the reports, bind a local directory to the container, e.g.:
```sh
docker run -ti --rm --name prowler --volume "$(pwd)":/prowler/output --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY --env AWS_SESSION_TOKEN toniblyx/prowler:latest -M csv,json
```
1. To perform an assessment based on CIS Profile Definitions you can use cislevel1 or cislevel2 with `-g` flag, more information about this [here, page 8](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf):
```sh
./prowler -g cislevel1
```
```sh
./prowler -g cislevel1
```
1. If you want to run Prowler to check multiple AWS accounts in parallel (runs up to 4 simultaneously `-P 4`) but you may want to read below in Advanced Usage section to do so assuming a role:
```sh
grep -E '^\[([0-9A-Aa-z_-]+)\]' ~/.aws/credentials | tr -d '][' | shuf | \
xargs -n 1 -L 1 -I @ -r -P 4 ./prowler -p @ -M csv 2> /dev/null >> all-accounts.csv
```
```sh
grep -E '^\[([0-9A-Aa-z_-]+)\]' ~/.aws/credentials | tr -d '][' | shuf | \
xargs -n 1 -L 1 -I @ -r -P 4 ./prowler -p @ -M csv 2> /dev/null >> all-accounts.csv
```
1. For help about usage run:
```
./prowler -h
```
```
./prowler -h
```
## Database providers connector
You can send the Prowler's output to different databases (right now only PostgreSQL is supported).
Jump into the section for the database provider you want to use and follow the required steps to configure it.
### PostgreSQL
Install psql
- Mac -> `brew install libpq`
- Ubuntu -> `sudo apt-get install postgresql-client `
- RHEL/Centos -> `sudo yum install postgresql10`
#### Audit ID Field
To use Prowler postgres connector it is needed to set the -u flag to include `audit_id` field into the query. This field helps to identify each audit that has been made in the database. This field needs to be an UUID V4 to match the table schema.
For example:
```
./prowler -M csv -d postgresql -u e5a0f214-8bf9-4600-a0c3-ff659b30e6c0
```
#### Credentials
There are two options to pass the PostgreSQL credentials to Prowler:
##### Using a .pgpass file
Configure a `~/.pgpass` file into the root folder of the user that is going to launch Prowler ([pgpass file doc](https://www.postgresql.org/docs/current/libpq-pgpass.html)), including an extra field at the end of the line, separated by `:`, to name the table, using the following format:
`hostname:port:database:username:password:table`
##### Using environment variables
- Configure the following environment variables:
- `POSTGRES_HOST`
- `POSTGRES_PORT`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
- `POSTGRES_DB`
- `POSTGRES_TABLE`
> _Note_: If you are using a schema different than postgres please include it at the beginning of the `POSTGRES_TABLE` variable, like: `export POSTGRES_TABLE=prowler.findings`
Also you need to have enabled the `uuid` postgresql extension, to enable it:
`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`
Create a table in your PostgreSQL database to store the Prowler's data. You can use the following SQL statement to create the table:
```
CREATE TABLE IF NOT EXISTS prowler_findings (
id uuid,
audit_id uuid ,
profile text,
account_number text,
region text,
check_id text,
result text,
item_scored text,
item_level text,
check_title text,
result_extended text,
check_asff_compliance_type text,
severity text,
service_name text,
check_asff_resource_type text,
check_asff_type text,
risk text,
remediation text,
documentation text,
check_caf_epic text,
resource_id text,
account_details_email text,
account_details_name text,
account_details_arn text,
account_details_org text,
account_details_tags text,
prowler_start_time text
);
```
- Execute Prowler with `-d` flag, for example:
`./prowler -M csv -d postgresql -u e5a0f214-8bf9-4600-a0c3-ff659b30e6c0`
> _Note_: This command creates a `csv` output file and stores the Prowler output in the configured PostgreSQL DB. It's an example, `-d` flag **does not** require `-M` to run.
## Output Formats
Prowler supports natively the following output formats:
- CSV
- JSON
- JSON-ASFF
- HTML
- JUNIT-XML
Hereunder is the structure for each of them
### CSV
| PROFILE | ACCOUNT_NUM | REGION | TITLE_ID | CHECK_RESULT | ITEM_SCORED | ITEM_LEVEL | TITLE_TEXT | CHECK_RESULT_EXTENDED | CHECK_ASFF_COMPLIANCE_TYPE | CHECK_SEVERITY | CHECK_SERVICENAME | CHECK_ASFF_RESOURCE_TYPE | CHECK_ASFF_TYPE | CHECK_RISK | CHECK_REMEDIATION | CHECK_DOC | CHECK_CAF_EPIC | CHECK_RESOURCE_ID | PROWLER_START_TIME | ACCOUNT_DETAILS_EMAIL | ACCOUNT_DETAILS_NAME | ACCOUNT_DETAILS_ARN | ACCOUNT_DETAILS_ORG | ACCOUNT_DETAILS_TAGS |
| ------- | ----------- | ------ | -------- | ------------ | ----------- | ---------- | ---------- | --------------------- | -------------------------- | -------------- | ----------------- | ------------------------ | --------------- | ---------- | ----------------- | --------- | -------------- | ----------------- | ------------------ | --------------------- | -------------------- | ------------------- | ------------------- | -------------------- |
### JSON
```
{
"Profile": "ENV",
"Account Number": "1111111111111",
"Control": "[check14] Ensure access keys are rotated every 90 days or less",
"Message": "us-west-2: user has not rotated access key 2 in over 90 days",
"Severity": "Medium",
"Status": "FAIL",
"Scored": "",
"Level": "CIS Level 1",
"Control ID": "1.4",
"Region": "us-west-2",
"Timestamp": "2022-05-18T10:33:48Z",
"Compliance": "ens-op.acc.1.aws.iam.4 ens-op.acc.5.aws.iam.3",
"Service": "iam",
"CAF Epic": "IAM",
"Risk": "Access keys consist of an access key ID and secret access key which are used to sign programmatic requests that you make to AWS. AWS users need their own access keys to make programmatic calls to AWS from the AWS Command Line Interface (AWS CLI)- Tools for Windows PowerShell- the AWS SDKs- or direct HTTP calls using the APIs for individual AWS services. It is recommended that all access keys be regularly rotated.",
"Remediation": "Use the credential report to ensure access_key_X_last_rotated is less than 90 days ago.",
"Doc link": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html",
"Resource ID": "terraform-user",
"Account Email": "",
"Account Name": "",
"Account ARN": "",
"Account Organization": "",
"Account tags": ""
}
```
> NOTE: Each finding is a `json` object.
### JSON-ASFF
```
{
"SchemaVersion": "2018-10-08",
"Id": "prowler-1.4-1111111111111-us-west-2-us-west-2_user_has_not_rotated_access_key_2_in_over_90_days",
"ProductArn": "arn:aws:securityhub:us-west-2::product/prowler/prowler",
"RecordState": "ACTIVE",
"ProductFields": {
"ProviderName": "Prowler",
"ProviderVersion": "2.9.0-13April2022",
"ProwlerResourceName": "user"
},
"GeneratorId": "prowler-check14",
"AwsAccountId": "1111111111111",
"Types": [
"ens-op.acc.1.aws.iam.4 ens-op.acc.5.aws.iam.3"
],
"FirstObservedAt": "2022-05-18T10:33:48Z",
"UpdatedAt": "2022-05-18T10:33:48Z",
"CreatedAt": "2022-05-18T10:33:48Z",
"Severity": {
"Label": "MEDIUM"
},
"Title": "iam.[check14] Ensure access keys are rotated every 90 days or less",
"Description": "us-west-2: user has not rotated access key 2 in over 90 days",
"Resources": [
{
"Type": "AwsIamUser",
"Id": "user",
"Partition": "aws",
"Region": "us-west-2"
}
],
"Compliance": {
"Status": "FAILED",
"RelatedRequirements": [
"ens-op.acc.1.aws.iam.4 ens-op.acc.5.aws.iam.3"
]
}
}
```
> NOTE: Each finding is a `json` object.
## Advanced Usage
### Assume Role:
Prowler uses the AWS CLI underneath so it uses the same authentication methods. However, there are few ways to run Prowler against multiple accounts using IAM Assume Role feature depending on eachg use case. You can just set up your custom profile inside `~/.aws/config` with all needed information about the role to assume then call it with `./prowler -p your-custom-profile`. Additionally you can use `-A 123456789012` and `-R RemoteRoleToAssume` and Prowler will get those temporary credentials using `aws sts assume-role`, set them up as environment variables and run against that given account. To create a role to assume in multiple accounts easier eather as CFN Stack or StackSet, look at [this CloudFormation template](iam/create_role_to_assume_cfn.yaml) and adapt it.
Prowler uses the AWS CLI underneath so it uses the same authentication methods. However, there are few ways to run Prowler against multiple accounts using IAM Assume Role feature depending on eachg use case. You can just set up your custom profile inside `~/.aws/config` with all needed information about the role to assume then call it with `./prowler -p your-custom-profile`. Additionally you can use `-A 123456789012` and `-R RemoteRoleToAssume` and Prowler will get those temporary credentials using `aws sts assume-role`, set them up as environment variables and run against that given account. To create a role to assume in multiple accounts easier either as CFN Stack or StackSet, look at [this CloudFormation template](iam/create_role_to_assume_cfn.yaml) and adapt it.
```sh
./prowler -A 123456789012 -R ProwlerRole
@@ -324,16 +542,18 @@ Prowler uses the AWS CLI underneath so it uses the same authentication methods.
./prowler -A 123456789012 -R ProwlerRole -I 123456
```
> *NOTE 1 about Session Duration*: By default it gets credentials valid for 1 hour (3600 seconds). Depending on the mount of checks you run and the size of your infrastructure, Prowler may require more than 1 hour to finish. Use option `-T <seconds>` to allow up to 12h (43200 seconds). To allow more than 1h you need to modify *"Maximum CLI/API session duration"* for that particular role, read more [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session).
> _NOTE 1 about Session Duration_: By default it gets credentials valid for 1 hour (3600 seconds). Depending on the mount of checks you run and the size of your infrastructure, Prowler may require more than 1 hour to finish. Use option `-T <seconds>` to allow up to 12h (43200 seconds). To allow more than 1h you need to modify _"Maximum CLI/API session duration"_ for that particular role, read more [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session).
> *NOTE 2 about Session Duration*: Bear in mind that if you are using roles assumed by role chaining there is a hard limit of 1 hour so consider not using role chaining if possible, read more about that, in foot note 1 below the table [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html).
> _NOTE 2 about Session Duration_: Bear in mind that if you are using roles assumed by role chaining there is a hard limit of 1 hour so consider not using role chaining if possible, read more about that, in foot note 1 below the table [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html).
For example, if you want to get only the fails in CSV format from all checks regarding RDS without banner from the AWS Account 123456789012 assuming the role RemoteRoleToAssume and set a fixed session duration of 1h:
```sh
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -b -M cvs -q -g rds
```
or with a given External ID:
```sh
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -I 123456 -b -M cvs -q -g rds
```
@@ -343,30 +563,67 @@ or with a given External ID:
If you want to run Prowler or just a check or a group across all accounts of AWS Organizations you can do this:
First get a list of accounts that are not suspended:
```
ACCOUNTS_IN_ORGS=$(aws organizations list-accounts --query Accounts[?Status==`ACTIVE`].Id --output text)
```
Then run Prowler to assume a role (same in all members) per each account, in this example it is just running one particular check:
```
for accountId in $ACCOUNTS_IN_ORGS; do ./prowler -A $accountId -R RemoteRoleToAssume -c extra79; done
```
Usig the same for loop it can be scanned a list of accounts with a variable like `ACCOUNTS_LIST='11111111111 2222222222 333333333'`
Using the same for loop it can be scanned a list of accounts with a variable like `ACCOUNTS_LIST='11111111111 2222222222 333333333'`
### Get AWS Account details from your AWS Organization:
From Prowler v2.8, you can get additional information of the scanned account in CSV and JSON outputs. When scanning a single account you get the Account ID as part of the output. Now, if you have AWS Organizations and are scanning multiple accounts using the assume role functionality, Prowler can get your account details like Account Name, Email, ARN, Organization ID and Tags and you will have them next to every finding in the CSV and JSON outputs.
In order to do that you can use the new option `-O <management account id>`, requires `-R <role to assume>` and also needs permissions `organizations:ListAccounts*` and `organizations:ListTagsForResource`. See the following sample command:
```
./prowler -R ProwlerScanRole -A 111111111111 -O 222222222222 -M json,csv
```
In that command Prowler will scan the account `111111111111` assuming the role `ProwlerScanRole` and getting the account details from the AWS Organizatiosn management account `222222222222` assuming the same role `ProwlerScanRole` for that and creating two reports with those details in JSON and CSV.
In the JSON output below (redacted) you can see tags coded in base64 to prevent breaking CSV or JSON due to its format:
```json
"Account Email": "my-prod-account@domain.com",
"Account Name": "my-prod-account",
"Account ARN": "arn:aws:organizations::222222222222:account/o-abcde1234/111111111111",
"Account Organization": "o-abcde1234",
"Account tags": "\"eyJUYWdzIjpasf0=\""
```
The additional fields in CSV header output are as follow:
```csv
ACCOUNT_DETAILS_EMAIL,ACCOUNT_DETAILS_NAME,ACCOUNT_DETAILS_ARN,ACCOUNT_DETAILS_ORG,ACCOUNT_DETAILS_TAGS
```
### GovCloud
Prowler runs in GovCloud regions as well. To make sure it points to the right API endpoint use `-r` to either `us-gov-west-1` or `us-gov-east-1`. If not filter region is used it will look for resources in both GovCloud regions by default:
```sh
./prowler -r us-gov-west-1
```
> For Security Hub integration see below in Security Hub section.
### Custom folder for custom checks
Flag `-x /my/own/checks` will include any check in that particular directory. To see how to write checks see [Add Custom Checks](#add-custom-checks) section.
Flag `-x /my/own/checks` will include any check in that particular directory (files must start by check). To see how to write checks see [Add Custom Checks](#add-custom-checks) section.
S3 URIs are also supported as custom folders for custom checks, e.g. `s3://bucket/prefix/checks`. Prowler will download the folder locally and run the checks as they are called with default execution,`-c` or `-g`.
> Make sure that the used credentials have s3:GetObject permissions in the S3 path where the custom checks are located.
### Show or log only FAILs
In order to remove noise and get only FAIL findings there is a `-q` flag that makes Prowler to show and log only FAILs.
In order to remove noise and get only FAIL findings there is a `-q` flag that makes Prowler to show and log only FAILs.
It can be combined with any other option.
Will show WARNINGS when a resource is excluded, just to take into consideration.
@@ -384,34 +641,39 @@ Sets the entropy limit for high entropy hex strings from environment variable `H
export BASE64_LIMIT=4.5
export HEX_LIMIT=3.0
```
### Run Prowler using AWS CloudShell
An easy way to run Prowler to scan your account is using AWS CloudShell. Read more and learn how to do it [here](util/cloudshell/README.md).
## Security Hub integration
Since October 30th 2020 (version v2.3RC5), Prowler supports natively and as **official integration** sending findings to [AWS Security Hub](https://aws.amazon.com/security-hub). This integration allows Prowler to import its findings to AWS Security Hub. With Security Hub, you now have a single place that aggregates, organizes, and prioritizes your security alerts, or findings, from multiple AWS services, such as Amazon GuardDuty, Amazon Inspector, Amazon Macie, AWS Identity and Access Management (IAM) Access Analyzer, and AWS Firewall Manager, as well as from AWS Partner solutions and from Prowler for free.
Since October 30th 2020 (version v2.3RC5), Prowler supports natively and as **official integration** sending findings to [AWS Security Hub](https://aws.amazon.com/security-hub). This integration allows Prowler to import its findings to AWS Security Hub. With Security Hub, you now have a single place that aggregates, organizes, and prioritizes your security alerts, or findings, from multiple AWS services, such as Amazon GuardDuty, Amazon Inspector, Amazon Macie, AWS Identity and Access Management (IAM) Access Analyzer, and AWS Firewall Manager, as well as from AWS Partner solutions and from Prowler for free.
Before sending findings to Prowler, you need to perform next steps:
1. Since Security Hub is a region based service, enable it in the region or regions you require. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
- `aws securityhub enable-security-hub --region <region>`.
2. Enable Prowler as partner integration integration. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
- `aws securityhub enable-import-findings-for-product --region <region> --product-arn arn:aws:securityhub:<region>::product/prowler/prowler` (change region also inside the ARN).
- Using the AWS Management Console:
![Screenshot 2020-10-29 at 10 26 02 PM](https://user-images.githubusercontent.com/3985464/97634660-5ade3400-1a36-11eb-9a92-4a45cc98c158.png)
1. Since Security Hub is a region based service, enable it in the region or regions you require. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
- `aws securityhub enable-security-hub --region <region>`.
2. Enable Prowler as partner integration integration. Use the AWS Management Console or using the AWS CLI with this command if you have enough permissions:
- `aws securityhub enable-import-findings-for-product --region <region> --product-arn arn:aws:securityhub:<region>::product/prowler/prowler` (change region also inside the ARN).
- Using the AWS Management Console:
![Screenshot 2020-10-29 at 10 26 02 PM](https://user-images.githubusercontent.com/3985464/97634660-5ade3400-1a36-11eb-9a92-4a45cc98c158.png)
3. As mentioned in section "Custom IAM Policy", to allow Prowler to import its findings to AWS Security Hub you need to add the policy below to the role or user running Prowler:
- [iam/prowler-security-hub.json](iam/prowler-security-hub.json)
- [iam/prowler-security-hub.json](iam/prowler-security-hub.json)
Once it is enabled, it is as simple as running the command below (for all regions):
```sh
./prowler -M json-asff -S
```
or for only one filtered region like eu-west-1:
```sh
./prowler -M json-asff -q -S -f eu-west-1
```
> Note 1: It is recommended to send only fails to Security Hub and that is possible adding `-q` to the command.
> Note 1: It is recommended to send only fails to Security Hub and that is possible adding `-q` to the command.
> Note 2: Since Prowler perform checks to all regions by defaults you may need to filter by region when runing Security Hub integration, as shown in the example above. Remember to enable Security Hub in the region or regions you need by calling `aws securityhub enable-security-hub --region <region>` and run Prowler with the option `-f <region>` (if no region is used it will try to push findings in all regions hubs).
@@ -424,6 +686,7 @@ Once you run findings for first time you will be able to see Prowler findings in
### Security Hub in GovCloud regions
To use Prowler and Security Hub integration in GovCloud there is an additional requirement, usage of `-r` is needed to point the API queries to the right API endpoint. Here is a sample command that sends only failed findings to Security Hub in region `us-gov-west-1`:
```
./prowler -r us-gov-west-1 -f us-gov-west-1 -S -M csv,json-asff -q
```
@@ -431,6 +694,7 @@ To use Prowler and Security Hub integration in GovCloud there is an additional r
### Security Hub in China regions
To use Prowler and Security Hub integration in China regions there is an additional requirement, usage of `-r` is needed to point the API queries to the right API endpoint. Here is a sample command that sends only failed findings to Security Hub in region `cn-north-1`:
```
./prowler -r cn-north-1 -f cn-north-1 -q -S -M csv,json-asff
```
@@ -439,19 +703,39 @@ To use Prowler and Security Hub integration in China regions there is an additio
Either to run Prowler once or based on a schedule this template makes it pretty straight forward. This template will create a CodeBuild environment and run Prowler directly leaving all reports in a bucket and creating a report also inside CodeBuild basedon the JUnit output from Prowler. Scheduling can be cron based like `cron(0 22 * * ? *)` or rate based like `rate(5 hours)` since CloudWatch Event rules (or Eventbridge) is used here.
The Cloud Formation template that helps you doing that is [here](https://github.com/toniblyx/prowler/blob/master/util/codebuild/codebuild-prowler-audit-account-cfn.yaml).
The Cloud Formation template that helps you to do that is [here](https://github.com/prowler-cloud/prowler/blob/master/util/codebuild/codebuild-prowler-audit-account-cfn.yaml).
> This is a simple solution to monitor one account. For multiples accounts see [Multi Account and Continuous Monitoring](util/org-multi-account/README.md).
## Whitelist or allowlist or remove a fail from resources
## Allowlist or remove a fail from resources
Sometimes you may find resources that are intentionally configured in a certain way that may be a bad practice but it is all right with it, for example an S3 bucket open to the internet hosting a web site, or a security group with an open port needed in your use case. Now you can use `-w whitelist_sample.txt` and add your resources as `checkID:resourcename` as in this command:
Sometimes you may find resources that are intentionally configured in a certain way that may be a bad practice but it is all right with it, for example an S3 bucket open to the internet hosting a web site, or a security group with an open port needed in your use case. Now you can use `-w allowlist_sample.txt` and add your resources as `checkID:resourcename` as in this command:
```
./prowler -w whitelist_sample.txt
./prowler -w allowlist_sample.txt
```
Whitelist option works along with other options and adds a `WARNING` instead of `INFO`, `PASS` or `FAIL` to any output format except for `json-asff`.
S3 URIs are also supported as allowlist file, e.g. `s3://bucket/prefix/allowlist_sample.txt`
> Make sure that the used credentials have s3:GetObject permissions in the S3 path where the allowlist file is located.
DynamoDB table ARNs are also supported as allowlist file, e.g. `arn:aws:dynamodb:us-east-1:111111222222:table/allowlist`
> Make sure that the table has `account_id` as partition key and `rule` as sort key, and that the used credentials have `dynamodb:PartiQLSelect` permissions in the table.
>
> <p align="left"><img src="https://user-images.githubusercontent.com/38561120/165769502-296f9075-7cc8-445e-8158-4b21804bfe7e.png" alt="image" width="397" height="252" /></p>
> The field `account_id` can contain either an account ID or an `*` (which applies to all the accounts that use this table as a whitelist). As in the traditional allowlist file, the `rule` field must contain `checkID:resourcename` pattern.
>
> <p><img src="https://user-images.githubusercontent.com/38561120/165770610-ed5c2764-7538-44c2-9195-bcfdecc4ef9b.png" alt="image" width="394" /></p>
Allowlist option works along with other options and adds a `WARNING` instead of `INFO`, `PASS` or `FAIL` to any output format except for `json-asff`.
## Inventory
With Prowler you can get an inventory of your AWS resources. To do so, run `./prowler -i` to see what AWS resources you have deployed in your AWS account. This feature lists almost all resources in all regions based on [this](https://docs.aws.amazon.com/resourcegroupstagging/latest/APIReference/API_GetResources.html) API call. Note that it does not cover 100% of resource types.
The inventory will be stored in an output `csv` file by default, under common Prowler `output` folder, with the following format: `prowler-inventory-${ACCOUNT_NUM}-${OUTPUT_DATE}.csv`
## How to fix every FAIL
@@ -493,10 +777,12 @@ There are some helpfull tools to save time in this process like [aws-mfa-script]
### AWS Managed IAM Policies
[ViewOnlyAccess](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_view-only-user)
- Use case: This user can view a list of AWS resources and basic metadata in the account across all services. The user cannot read resource content or metadata that goes beyond the quota and list information for resources.
- Policy description: This policy grants List*, Describe*, Get*, View*, and Lookup* access to resources for most AWS services. To see what actions this policy includes for each service, see [ViewOnlyAccess Permissions](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/job-function/ViewOnlyAccess)
- Policy description: This policy grants List*, Describe*, Get*, View*, and Lookup\* access to resources for most AWS services. To see what actions this policy includes for each service, see [ViewOnlyAccess Permissions](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/job-function/ViewOnlyAccess)
[SecurityAudit](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_security-auditor)
- Use case: This user monitors accounts for compliance with security requirements. This user can access logs and events to investigate potential security breaches or potential malicious activity.
- Policy description: This policy grants permissions to view configuration data for many AWS services and to review their logs. To see what actions this policy includes for each service, see [SecurityAudit Permissions](https://console.aws.amazon.com/iam/home#policies/arn:aws:iam::aws:policy/SecurityAudit)
@@ -516,7 +802,7 @@ Allows Prowler to import its findings to [AWS Security Hub](https://aws.amazon.c
### Bootstrap Script
Quick bash script to set up a "prowler" IAM user with "SecurityAudit" and "ViewOnlyAccess" group with the required permissions (including "Prowler-Additions-Policy"). To run the script below, you need user with administrative permissions; set the `AWS_DEFAULT_PROFILE` to use that account:
Quick bash script to set up a "prowler" IAM user with "SecurityAudit" and "ViewOnlyAccess" group with the required permissions (including "Prowler-Additions-Policy"). To run the script below, you need a user with administrative permissions; set the `AWS_DEFAULT_PROFILE` to use that account:
```sh
export AWS_DEFAULT_PROFILE=default
@@ -532,7 +818,7 @@ aws iam create-access-key --user-name prowler
unset ACCOUNT_ID AWS_DEFAULT_PROFILE
```
The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to `~/.aws/credentials` with an appropriate profile name to use them with Prowler. This is the only time they secret key will be shown. If you lose it, you will need to generate a replacement.
The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to `~/.aws/credentials` with an appropriate profile name to use them with Prowler. This is the only time the secret key will be shown. If you lose it, you will need to generate a replacement.
> [This CloudFormation template](iam/create_role_to_assume_cfn.yaml) may also help you on that task.
@@ -548,7 +834,7 @@ To list all existing checks in the extras group run the command below:
./prowler -l -g extras
```
>There are some checks not included in that list, they are experimental or checks that takes long to run like `extra759` and `extra760` (search for secrets in Lambda function variables and code).
> There are some checks not included in that list, they are experimental or checks that take long to run like `extra759` and `extra760` (search for secrets in Lambda function variables and code).
To check all extras in one command:
@@ -568,7 +854,6 @@ or to run multiple extras in one go:
./prowler -c extraNumber,extraNumber
```
## Forensics Ready Checks
With this group of checks, Prowler looks if each service with logging or audit capabilities has them enabled to ensure all needed evidences are recorded and collected for an eventual digital forensic investigation in case of incident. List of checks part of this group (you can also see all groups with `./prowler -L`). The list of checks can be seen in the group file at:
@@ -583,7 +868,7 @@ The `forensics-ready` group of checks uses existing and extra checks. To get a f
## GDPR Checks
With this group of checks, Prowler shows result of checks related to GDPR, more information [here](https://github.com/toniblyx/prowler/issues/189). The list of checks can be seen in the group file at:
With this group of checks, Prowler shows result of checks related to GDPR, more information [here](https://github.com/prowler-cloud/prowler/issues/189). The list of checks can be seen in the group file at:
[groups/group9_gdpr](groups/group9_gdpr)
@@ -609,7 +894,7 @@ The `ftr` group of checks uses existing and extra checks. To get a AWS FTR repor
With this group of checks, Prowler shows results of controls related to the "Security Rule" of the Health Insurance Portability and Accountability Act aka [HIPAA](https://www.hhs.gov/hipaa/for-professionals/security/index.html) as defined in [45 CFR Subpart C - Security Standards for the Protection of Electronic Protected Health Information](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-C) within [PART 160 - GENERAL ADMINISTRATIVE REQUIREMENTS](https://www.law.cornell.edu/cfr/text/45/part-160) and [Subpart A](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-A) and [Subpart C](https://www.law.cornell.edu/cfr/text/45/part-164/subpart-C) of PART 164 - SECURITY AND PRIVACY
More information on the original PR is [here](https://github.com/toniblyx/prowler/issues/227).
More information on the original PR is [here](https://github.com/prowler-cloud/prowler/issues/227).
### Note on Business Associate Addendum's (BAA)
@@ -640,6 +925,7 @@ AWS is made to be flexible for service links within and between different AWS ac
This group of checks helps to analyse a particular AWS account (subject) on existing links to other AWS accounts across various AWS services, in order to identify untrusted links.
### Run
To give it a quick shot just call:
```sh
@@ -656,12 +942,12 @@ Currently, this check group supports two different scenarios:
### Coverage
Current coverage of Amazon Web Service (AWS) taken from [here](https://docs.aws.amazon.com/whitepapers/latest/aws-overview/introduction.html):
| Topic | Service | Trust Boundary |
| Topic | Service | Trust Boundary |
|---------------------------------|------------|---------------------------------------------------------------------------|
| Networking and Content Delivery | Amazon VPC | VPC endpoints connections ([extra786](checks/check_extra786)) |
| | | VPC endpoints whitelisted principals ([extra787](checks/check_extra787)) |
| Networking and Content Delivery | Amazon VPC | VPC endpoints connections ([extra786](checks/check_extra786)) |
| | | VPC endpoints allowlisted principals ([extra787](checks/check_extra787)) |
All ideas or recommendations to extend this group are very welcome [here](https://github.com/toniblyx/prowler/issues/new/choose).
All ideas or recommendations to extend this group are very welcome [here](https://github.com/prowler-cloud/prowler/issues/new/choose).
### Detailed Explanation of the Concept
@@ -676,7 +962,17 @@ Single Account environment assumes that only the AWS account subject to this ana
Multi Account environments assumes a minimum of two trusted or known accounts. For this particular example all trusted and known accounts will be tested. Therefore `GROUP_TRUSTBOUNDARIES_TRUSTED_ACCOUNT_IDS` variable in [groups/group16_trustboundaries](groups/group16_trustboundaries) should include all trusted accounts Account #A, Account #B, Account #C, and Account #D in order to finally raise Account #E and Account #F for being untrusted or unknown.
![multi-account-environment](/docs/images/prowler-multi-account-environment.png)
## Add Custom Checks
## Custom Checks
Using `./prowler -c extra9999 -a` you can build your own on-the-fly custom check by specifying the AWS CLI command to execute.
> Omit the "aws" command and only use its parameters within quotes and do not nest quotes in the aws parameter, --output text is already included in the check.
>
> Here is an example of a check to find SGs with inbound port 80:
```sh
./prowler -c extra9999 -a 'ec2 describe-security-groups --filters Name=ip-permission.to-port,Values=80 --query SecurityGroups[*].GroupId[]]'
```
In order to add any new check feel free to create a new extra check in the extras group or other group. To do so, you will need to follow these steps:
@@ -711,4 +1007,4 @@ Prowler is licensed as Apache License 2.0 as specified in each file. You may obt
**I'm not related anyhow with CIS organization, I just write and maintain Prowler to help companies over the world to make their cloud infrastructure more secure.**
If you want to contact me visit <https://blyx.com/contact> or follow me on Twitter <https://twitter.com/toniblyx> my DMs are open.
If you want to contact me visit <https://blyx.com/contact> or follow me on Twitter <https://twitter.com/prowler-cloud> my DMs are open.

View File

@@ -16,6 +16,14 @@ check26:myignoredbucket
#<checkid2>:<resource to ignore 1>
# REGEXES
# This whitelist works with regexes (ERE, the same style of regex as grep -E and bash's =~ use)
# This allowlist works with regexes (ERE, the same style of regex as grep -E and bash's =~ use)
# therefore:
# extra718:[[:alnum:]]+-logs # will ignore all buckets containing the terms ci-logs, qa-logs, etc.
# extra718:[[:alnum:]]+-logs # will ignore all buckets containing the terms ci-logs, qa-logs, etc.
# EXAMPLE: CONTROL TOWER
# When using Control Tower, guardrails prevent access to certain protected resources. The allowlist
# below ensures that warnings instead of errors are reported for the affected resources.
#extra734:aws-controltower-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra734:aws-controltower-s3-access-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra764:aws-controltower-logs-[[:digit:]]+-[[:alpha:]\-]+
#extra764:aws-controltower-s3-access-logs-[[:digit:]]+-[[:alpha:]\-]+

View File

@@ -25,23 +25,27 @@ CHECK_DOC_check11='http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practice
CHECK_CAF_EPIC_check11='IAM'
check11(){
# "Avoid the use of the root account (Scored)."
MAX_DAYS=-1
last_login_dates=$(cat $TEMP_REPORT_FILE | awk -F, '{ print $1,$5,$11,$16 }' | grep '<root_account>' | cut -d' ' -f2,3,4)
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
else
# "Avoid the use of the root account (Scored)."
MAX_DAYS=-1
last_login_dates=$(cat $TEMP_REPORT_FILE | awk -F, '{ print $1,$5,$11,$16 }' | grep '<root_account>' | cut -d' ' -f2,3,4)
failures=0
for date in $last_login_dates; do
if [[ ${date%T*} =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]];then
days_not_in_use=$(how_many_days_from_today ${date%T*})
if [ "$days_not_in_use" -gt "$MAX_DAYS" ];then
failures=1
textFail "$REGION: Root user in the account was last accessed ${MAX_DAYS#-} day ago" "$REGION" "root"
break
failures=0
for date in $last_login_dates; do
if [[ ${date%T*} =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]];then
days_not_in_use=$(how_many_days_from_today ${date%T*})
if [ "$days_not_in_use" -gt "$MAX_DAYS" ];then
failures=1
textFail "$REGION: Root user in the account was last accessed ${MAX_DAYS#-} day ago" "$REGION" "root"
break
fi
fi
fi
done
done
if [[ $failures == 0 ]]; then
textPass "$REGION: Root user in the account wasn't accessed in the last ${MAX_DAYS#-} days" "$REGION" "root"
if [[ $failures == 0 ]]; then
textPass "$REGION: Root user in the account wasn't accessed in the last ${MAX_DAYS#-} days" "$REGION" "root"
fi
fi
}

View File

@@ -25,11 +25,15 @@ CHECK_DOC_check113='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-use
CHECK_CAF_EPIC_check113='IAM'
check113(){
# "Ensure MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
if [ "$COMMAND113" == "1" ]; then
textPass "$REGION: Virtual MFA is enabled for root" "$REGION" "MFA"
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
else
textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
# "Ensure MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
if [ "$COMMAND113" == "1" ]; then
textPass "$REGION: Virtual MFA is enabled for root" "$REGION" "MFA"
else
textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
fi
fi
}

View File

@@ -25,16 +25,20 @@ CHECK_DOC_check114='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-use
CHECK_CAF_EPIC_check114='IAM'
check114(){
# "Ensure hardware MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
if [ "$COMMAND113" == "1" ]; then
COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --output text --assignment-status Assigned --query 'VirtualMFADevices[*].[SerialNumber]' | grep "^arn:${AWS_PARTITION}:iam::[0-9]\{12\}:mfa/root-account-mfa-device$")
if [[ "$COMMAND114" ]]; then
textFail "$REGION: Only Virtual MFA is enabled for root" "$REGION" "MFA"
else
textPass "$REGION: Hardware MFA is enabled for root" "$REGION" "MFA"
fi
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
else
textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
# "Ensure hardware MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled')
if [ "$COMMAND113" == "1" ]; then
COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --output text --assignment-status Assigned --query 'VirtualMFADevices[*].[SerialNumber]' | grep "^arn:${AWS_PARTITION}:iam::[0-9]\{12\}:mfa/root-account-mfa-device$")
if [[ "$COMMAND114" ]]; then
textFail "$REGION: Only Virtual MFA is enabled for root" "$REGION" "MFA"
else
textPass "$REGION: Hardware MFA is enabled for root" "$REGION" "MFA"
fi
else
textFail "$REGION: MFA is not ENABLED for root account" "$REGION" "MFA"
fi
fi
}

View File

@@ -25,6 +25,10 @@ CHECK_DOC_check115='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credenti
CHECK_CAF_EPIC_check115='IAM'
check115(){
# "Ensure security questions are registered in the AWS account (Not Scored)"
textInfo "No command available for check 1.15. Login to the AWS Console as root & click on the Account. Name -> My Account -> Configure Security Challenge Questions."
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks." "$REGION" "root"
else
# "Ensure security questions are registered in the AWS account (Not Scored)"
textInfo "${REGION}: No command available for check 1.15. Login to the AWS Console as root & click on the Account. Name -> My Account -> Configure Security Challenge Questions." "$REGION" "root"
fi
}

View File

@@ -29,20 +29,26 @@ CHECK_CAF_EPIC_check116='IAM'
check116(){
# "Ensure IAM policies are attached only to groups or roles (Scored)"
LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION)
C116_NUM_USERS=0
for user in $LIST_USERS;do
USER_POLICY=$($AWSCLI iam list-attached-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
if [[ $USER_POLICY ]]; then
textFail "$REGION: $user has managed policy directly attached" "$REGION" "$user"
C116_NUM_USERS=$(expr $C116_NUM_USERS + 1)
fi
USER_POLICY=$($AWSCLI iam list-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
if [[ $USER_POLICY ]]; then
textFail "$REGION: $user has inline policy directly attached" "$REGION" "$user"
C116_NUM_USERS=$(expr $C116_NUM_USERS + 1)
fi
done
if [[ $C116_NUM_USERS -eq 0 ]]; then
textPass "$REGION: No policies attached to users" "$REGION" "$user"
if [[ "${LIST_USERS}" ]]
then
for user in $LIST_USERS;do
USER_ATTACHED_POLICY=$($AWSCLI iam list-attached-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
USER_INLINE_POLICY=$($AWSCLI iam list-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
if [[ $USER_ATTACHED_POLICY ]] || [[ $USER_INLINE_POLICY ]]
then
if [[ $USER_ATTACHED_POLICY ]]
then
textFail "$REGION: $user has managed policy directly attached" "$REGION" "$user"
fi
if [[ $USER_INLINE_POLICY ]]
then
textFail "$REGION: $user has inline policy directly attached" "$REGION" "$user"
fi
else
textPass "$REGION: No policies attached to user $user" "$REGION" "$user"
fi
done
else
textPass "$REGION: No users found" "$REGION" "No users found"
fi
}

View File

@@ -13,7 +13,7 @@
CHECK_ID_check117="1.17"
CHECK_TITLE_check117="[check117] Maintain current contact details"
CHECK_SCORED_check117="NOT_SCORED"
CHECK_SCORED_check117="SCORED"
CHECK_CIS_LEVEL_check117="LEVEL1"
CHECK_SEVERITY_check117="Medium"
CHECK_ASFF_TYPE_check117="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
@@ -25,7 +25,19 @@ CHECK_DOC_check117='https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2
CHECK_CAF_EPIC_check117='IAM'
check117(){
# "Maintain current contact details (Scored)"
# No command available
textInfo "No command available for check 1.17. See section 1.17 on the CIS Benchmark guide for details."
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks." "${REGION}" "root"
else
# "Maintain current contact details (Scored)"
GET_CONTACT_DETAILS=$($AWSCLI account get-contact-information --output text $PROFILE_OPT --region "${REGION}" 2>&1)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${GET_CONTACT_DETAILS}"; then
textInfo "${REGION}: Access Denied trying to get account contact information" "${REGION}"
else
if [[ ${GET_CONTACT_DETAILS} ]];then
textPass "${REGION}: Account has contact information. Perhaps check for freshness of these details." "${REGION}" "root"
else
textFail "${REGION}: Unable to get account contact details. See section 1.17 on the CIS Benchmark guide for details." "${REGION}" "root"
fi
fi
fi
}

View File

@@ -13,7 +13,7 @@
CHECK_ID_check118="1.18"
CHECK_TITLE_check118="[check118] Ensure security contact information is registered"
CHECK_SCORED_check118="NOT_SCORED"
CHECK_SCORED_check118="SCORED"
CHECK_CIS_LEVEL_check118="LEVEL1"
CHECK_SEVERITY_check118="Medium"
CHECK_ASFF_TYPE_check118="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
@@ -25,7 +25,19 @@ CHECK_DOC_check118='https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2
CHECK_CAF_EPIC_check118='IAM'
check118(){
# "Ensure security contact information is registered (Scored)"
# No command available
textInfo "No command available for check 1.18. See section 1.18 on the CIS Benchmark guide for details."
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks." "${REGION}" "root"
else
# "Ensure security contact information is registered (Scored)"
GET_SECURITY_CONTACT_DETAILS=$("${AWSCLI}" account get-alternate-contact --alternate-contact-type SECURITY --output text ${PROFILE_OPT} --region "${REGION}" 2>&1)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${GET_SECURITY_CONTACT_DETAILS}"; then
textInfo "${REGION}: Access Denied trying to get account contact information" "${REGION}"
else
if grep "SECURITY" <<< "${GET_SECURITY_CONTACT_DETAILS}"; then
textPass "${REGION}: Account has security contact information. Perhaps check for freshness of these details." "${REGION}" "root"
else
textFail "${REGION}: Account has not security contact information, or it was unable to capture. See section 1.18 on the CIS Benchmark guide for details." "${REGION}" "root"
fi
fi
fi
}

View File

@@ -21,7 +21,7 @@ CHECK_ASFF_RESOURCE_TYPE_check119="AwsEc2Instance"
CHECK_ALTERNATE_check119="check119"
CHECK_SERVICENAME_check119="ec2"
CHECK_RISK_check119='AWS access from within AWS instances can be done by either encoding AWS keys into AWS API calls or by assigning the instance to a role which has an appropriate permissions policy for the required access. AWS IAM roles reduce the risks associated with sharing and rotating credentials that can be used outside of AWS itself. If credentials are compromised; they can be used from outside of the AWS account.'
CHECK_REMEDIATION_check119='IAM roles can only be associated at the launch of an instance. To remediate an instance to add it to a role you must create or re-launch a new instance. (Check for external dependencies on its current private ip or public addresses).'
CHECK_REMEDIATION_check119='Create an IAM instance role if necessary and attach it to the corresponding EC2 instance.'
CHECK_DOC_check119='http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html'
CHECK_CAF_EPIC_check119='IAM'

View File

@@ -22,7 +22,7 @@ CHECK_ALTERNATE_check102="check12"
CHECK_ASFF_COMPLIANCE_TYPE_check12="ens-op.acc.5.aws.iam.1"
CHECK_SERVICENAME_check12="iam"
CHECK_RISK_check12='Unauthorized access to this critical account if password is not secure or it is disclosed in any way.'
CHECK_REMEDIATION_check12='Enable MFA for root account. MFA is a simple best practice that adds an extra layer of protection on top of your user name and password. Recommended to use hardware keys over virtual MFA.'
CHECK_REMEDIATION_check12='Enable MFA for all IAM users that have a console password. MFA is a simple best practice that adds an extra layer of protection on top of your user name and password. Recommended to use hardware keys over virtual MFA.'
CHECK_DOC_check12='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html'
CHECK_CAF_EPIC_check12='IAM'

View File

@@ -23,7 +23,7 @@ CHECK_ASFF_COMPLIANCE_TYPE_check120="ens-op.acc.1.aws.iam.4"
CHECK_SERVICENAME_check120="iam"
CHECK_RISK_check120='AWS provides a support center that can be used for incident notification and response; as well as technical support and customer services. Create an IAM Role to allow authorized users to manage incidents with AWS Support.'
CHECK_REMEDIATION_check120='Create an IAM role for managing incidents with AWS.'
CHECK_DOC_check120='https://docs.aws.amazon.com/awssupport/latest/user/using-service-linked-roles-sup.html'
CHECK_DOC_check120='https://docs.aws.amazon.com/awssupport/latest/user/accessing-support.html'
CHECK_CAF_EPIC_check120='IAM'
check120(){

View File

@@ -30,19 +30,19 @@ check122(){
LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text $PROFILE_OPT --region $REGION --scope Local --query 'Policies[*].[Arn,DefaultVersionId]' | grep -v -e '^None$' | awk -F '\t' '{print $1","$2"\n"}')
if [[ $LIST_CUSTOM_POLICIES ]]; then
for policy in $LIST_CUSTOM_POLICIES; do
POLICY_ARN=$(echo $policy | awk -F ',' '{print $1}')
POLICY_VERSION=$(echo $policy | awk -F ',' '{print $2}')
POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "[PolicyVersion.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && Action == '*']" $PROFILE_OPT --region $REGION)
POLICY_ARN=$(awk 'BEGIN{FS=OFS=","}{NF--; print}' <<< "${policy}")
POLICY_VERSION=$(awk -F ',' '{print $(NF)}' <<< "${policy}")
POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "[PolicyVersion.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && contains(Action, '*')]" $PROFILE_OPT --region $REGION)
if [[ $POLICY_WITH_FULL ]]; then
POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $POLICY_ARN"
else
textPass "$REGION: Policy ${policy//,/[comma]} that does not allow full \"*:*\" administrative privileges" "${REGION}" "${policy}"
fi
done
if [[ $POLICIES_ALLOW_LIST ]]; then
for policy in $POLICIES_ALLOW_LIST; do
textFail "$REGION: Policy $policy allows \"*:*\"" "$REGION" "$policy"
textFail "$REGION: Policy ${policy//,/[comma]} allows \"*:*\"" "${REGION}" "${policy}"
done
else
textPass "$REGION: No custom policy found that allow full \"*:*\" administrative privileges" "$REGION"
fi
else
textPass "$REGION: No custom policies found" "$REGION"

View File

@@ -27,42 +27,40 @@ CHECK_DOC_check21='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cl
CHECK_CAF_EPIC_check21='Logging and Monitoring'
check21(){
trail_count=0
# "Ensure CloudTrail is enabled in all regions (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion, Multiregion:IsMultiRegionTrail, ARN:TrailARN}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail)
if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail is not enabled for all regions" "$regx" "$trail"
else
TRAIL_ON_OFF_STATUS=$($AWSCLI cloudtrail get-trail-status $PROFILE_OPT --region $TRAIL_REGION --name $trail --query IsLogging --output text)
if [[ "$TRAIL_ON_OFF_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail is configured for all regions but it is OFF" "$regx" "$trail"
else
textPass "$regx: Trail $trail is enabled for all regions" "$regx" "$trail"
fi
fi
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_ARN TRAIL_HOME_REGION IS_MULTIREGION TRAIL_NAME
do
TRAIL_ON_OFF_STATUS=$(${AWSCLI} cloudtrail get-trail-status ${PROFILE_OPT} --region ${regx} --name ${TRAIL_ARN} --query IsLogging --output text)
if [[ "$TRAIL_ON_OFF_STATUS" == "False" ]]
then
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textFail "$regx: Trail ${TRAIL_NAME} is multiregion configured from region "${TRAIL_HOME_REGION}" but it is not logging" "${regx}" "${TRAIL_NAME}"
else
textFail "$regx: Trail ${TRAIL_NAME} is not a multiregion trail and it is not logging" "${regx}" "${TRAIL_NAME}"
fi
elif [[ "$TRAIL_ON_OFF_STATUS" == "True" ]]
then
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textPass "$regx: Trail ${TRAIL_NAME} is multiregion configured from region "${TRAIL_HOME_REGION}" and it is logging" "${regx}" "${TRAIL_NAME}"
else
textFail "$regx: Trail ${TRAIL_NAME} is not a multiregion trail and it is logging" "${regx}" "${TRAIL_NAME}"
fi
fi
done <<< "${REGION_TRAIL}"
done
else
textFail "$regx: No CloudTrail trails were found for the region" "${regx}" "No trails found"
fi
done
if [[ $trail_count == 0 ]]; then
if [[ $FILTERREGION ]]; then
textFail "$regx: No CloudTrail trails were found in the filtered region" "$regx" "$trail"
else
textFail "$regx: No CloudTrail trails were found in the account" "$regx" "$trail"
fi
fi
}
}

View File

@@ -27,34 +27,40 @@ CHECK_DOC_check22='http://docs.aws.amazon.com/awscloudtrail/latest/userguide/clo
CHECK_CAF_EPIC_check22='Logging and Monitoring'
check22(){
trail_count=0
# "Ensure CloudTrail log file validation is enabled (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
continue
for regx in $REGIONS
do
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion, Multiregion:IsMultiRegionTrail, LogFileValidation:LogFileValidationEnabled}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail)
if [[ "$LOGFILEVALIDATION_TRAIL_STATUS" == 'False' ]];then
textFail "$regx: Trail $trail log file validation disabled" "$regx" "$trail"
else
textPass "$regx: Trail $trail log file validation enabled" "$regx" "$trail"
fi
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_HOME_REGION LOG_FILE_VALIDATION IS_MULTIREGION TRAIL_NAME
do
if [[ "${LOG_FILE_VALIDATION}" == "True" ]]
then
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textPass "$regx: Multiregion trail ${TRAIL_NAME} configured from region ${TRAIL_HOME_REGION} log file validation enabled" "$regx" "$TRAIL_NAME"
else
textPass "$regx: Single region trail ${TRAIL_NAME} log file validation enabled" "$regx" "$TRAIL_NAME"
fi
else
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textFail "$regx: Multiregion trail ${TRAIL_NAME} configured from region ${TRAIL_HOME_REGION} log file validation disabled" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Single region trail ${TRAIL_NAME} log file validation disabled" "$regx" "$TRAIL_NAME"
fi
fi
done <<< "${REGION_TRAIL}"
done
else
textPass "$regx: No trails found in the region" "$regx"
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -27,68 +27,66 @@ CHECK_DOC_check23='https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_po
CHECK_CAF_EPIC_check23='Logging and Monitoring'
check23(){
trail_count=0
# "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
for regx in $REGIONS
do
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion, Multiregion:IsMultiRegionTrail, BucketName:S3BucketName}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_BUCKET TRAIL_HOME_REGION IS_MULTIREGION TRAIL_NAME
do
if [[ ! "${TRAIL_BUCKET}" ]]
then
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textFail "$regx: Multiregion trail ${TRAIL_NAME} configured from region ${TRAIL_HOME_REGION} does not publish to S3" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Single region trail ${TRAIL_NAME} does not publish to S3" "$regx" "$TRAIL_NAME"
fi
continue
fi
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail)
if [[ -z $CLOUDTRAILBUCKET ]]; then
textFail "Trail $trail in $TRAIL_REGION does not publish to S3"
continue
fi
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $TRAIL_BUCKET --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]
then
textInfo "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} Access Denied getting bucket location for bucket $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
continue
fi
if [[ $(echo "$BUCKET_LOCATION" | grep NoSuchBucket) ]]
then
textInfo "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} S3 logging bucket $TRAIL_BUCKET does not exist" "$regx" "$TRAIL_NAME"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ $BUCKET_LOCATION == "EU" ]]; then
BUCKET_LOCATION="eu-west-1"
fi
CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }')
if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then
textInfo "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not in current account"
continue
fi
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $TRAIL_BUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} Access Denied getting bucket acl for bucket $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
continue
fi
#
# LOCATION - requests referencing buckets created after March 20, 2019
# must be made to S3 endpoints in the same region as the bucket was
# created.
#
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket location for $CLOUDTRAILBUCKET"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ $BUCKET_LOCATION == "EU" ]]; then
BUCKET_LOCATION="eu-west-1"
fi
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket acl for $CLOUDTRAILBUCKET"
continue
fi
if [[ -z $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
textPass "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not publicly accessible"
else
textFail "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is publicly accessible"
fi
if [[ ! $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
textPass "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} S3 logging bucket $TRAIL_BUCKET is not publicly accessible" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} S3 logging bucket $TRAIL_BUCKET is publicly accessible" "$regx" "$TRAIL_NAME"
fi
done <<< "${REGION_TRAIL}"
done
else
textPass "$regx: No trails found in the region" "$regx"
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -27,40 +27,34 @@ CHECK_DOC_check24='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/se
CHECK_CAF_EPIC_check24='Logging and Monitoring'
check24(){
trail_count=0
# "Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion, ARN:TrailARN}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail $PROFILE_OPT --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None)
if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then
textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" "$TRAIL_REGION" "$trail"
else
LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
if [ $HOWOLDER -gt "1" ];then
textFail "$TRAIL_REGION: $trail trail is not logging in the last 24h or not configured" "$TRAIL_REGION" "$trail"
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_ARN TRAIL_HOME_REGION TRAIL_NAME
do
LATESTDELIVERY_TIMESTAMP=$(${AWSCLI} cloudtrail get-trail-status ${PROFILE_OPT} --region ${regx} --name ${TRAIL_ARN} --query LatestCloudWatchLogsDeliveryTime --output text|grep -v None)
if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then
textFail "$regx: $TRAIL_NAME trail is not logging in the last 24h or not configured (its home region is $TRAIL_HOME_REGION)" "$regx" "$trail"
else
textPass "$TRAIL_REGION: $trail trail has been logging during the last 24h" "$TRAIL_REGION" "$trail"
LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
if [ $HOWOLDER -gt "1" ];then
textFail "$regx: $TRAIL_NAME trail is not logging in the last 24h or not configured" "$regx" "$TRAIL_NAME"
else
textPass "$regx: $TRAIL_NAME trail has been logging during the last 24h" "$regx" "$TRAIL_NAME"
fi
fi
fi
done <<< "${REGION_TRAIL}"
done
else
textFail "$regx: No CloudTrail trails were found for the region" "${regx}" "No trails found"
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -26,68 +26,62 @@ CHECK_DOC_check26='https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best
CHECK_CAF_EPIC_check26='Logging and Monitoring'
check26(){
trail_count=0
# "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
for regx in $REGIONS
do
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion, Multiregion:IsMultiRegionTrail, BucketName:S3BucketName}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_BUCKET TRAIL_HOME_REGION IS_MULTIREGION TRAIL_NAME
do
if [[ ! "${TRAIL_BUCKET}" ]]
then
if [[ "${IS_MULTIREGION}" == "True" ]]
then
textFail "$regx: Multiregion trail ${TRAIL_NAME} configured from region ${TRAIL_HOME_REGION} does not publish to S3" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Single region trail ${TRAIL_NAME} does not publish to S3" "$regx" "$TRAIL_NAME"
fi
continue
fi
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail)
if [[ -z $CLOUDTRAILBUCKET ]]; then
textFail "$regx: Trail $trail does not publish to S3" "$TRAIL_REGION" "$trail"
continue
fi
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $TRAIL_BUCKET --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]
then
textInfo "$regx: Trail ${TRAIL_NAME} with home region ${TRAIL_HOME_REGION} Access Denied getting bucket location for bucket $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ $BUCKET_LOCATION == "EU" ]]; then
BUCKET_LOCATION="eu-west-1"
fi
CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }')
if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then
textInfo "$regx: Trail $trail S3 logging bucket $CLOUDTRAILBUCKET is not in current account" "$TRAIL_REGION" "$trail"
continue
fi
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $TRAIL_BUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'LoggingEnabled.TargetBucket' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then
textInfo "$regx: Trail $TRAIL_NAME Access Denied getting bucket logging for $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
continue
fi
#
# LOCATION - requests referencing buckets created after March 20, 2019
# must be made to S3 endpoints in the same region as the bucket was
# created.
#
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
textInfo "$regx: Trail $trail Access Denied getting bucket location for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
BUCKET_LOCATION="us-east-1"
fi
if [[ $BUCKET_LOCATION == "EU" ]]; then
BUCKET_LOCATION="eu-west-1"
fi
if [[ $CLOUDTRAILBUCKET_LOGENABLED != "None" ]]; then
textPass "$regx: Trail $TRAIL_NAME S3 bucket access logging is enabled for $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Trail $TRAIL_NAME S3 bucket access logging is not enabled for $TRAIL_BUCKET" "$regx" "$TRAIL_NAME"
fi
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'LoggingEnabled.TargetBucket' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then
textInfo "$regx: Trail $trail Access Denied getting bucket logging for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
continue
fi
if [[ $CLOUDTRAILBUCKET_LOGENABLED != "None" ]]; then
textPass "$regx: Trail $trail S3 bucket access logging is enabled for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
else
textFail "$regx: Trail $trail S3 bucket access logging is not enabled for $CLOUDTRAILBUCKET" "$TRAIL_REGION" "$trail"
fi
done <<< "${REGION_TRAIL}"
done
else
textPass "$regx: No trails found in the region" "$regx"
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -27,33 +27,28 @@ CHECK_DOC_check27='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/en
CHECK_CAF_EPIC_check27='Logging and Monitoring'
check27(){
trail_count=0
# "Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)"
for regx in $REGIONS; do
TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',')
if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx" "$trail"
TRAILS_DETAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:Name, KeyID:KmsKeyId}' --output text 2>&1)
if [[ $(echo "$TRAILS_DETAILS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe trails" "$regx"
continue
fi
if [[ $TRAILS_AND_REGIONS ]]; then
for reg_trail in $TRAILS_AND_REGIONS; do
TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1)
if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region
continue
fi
trail=$(echo $reg_trail | cut -d',' -f2)
trail_count=$((trail_count + 1))
KMSKEYID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].KmsKeyId' --output text --trail-name-list $trail)
if [[ "$KMSKEYID" ]];then
textPass "$regx: Trail $trail has encryption enabled" "$regx" "$trail"
else
textFail "$regx: Trail $trail has encryption disabled" "$regx" "$trail"
fi
if [[ $TRAILS_DETAILS ]]
then
for REGION_TRAIL in "${TRAILS_DETAILS}"
do
while read -r TRAIL_KEY_ID TRAIL_NAME
do
if [[ "${TRAIL_KEY_ID}" != "None" ]]
then
textPass "$regx: Trail $TRAIL_NAME has encryption enabled" "$regx" "$TRAIL_NAME"
else
textFail "$regx: Trail $TRAIL_NAME has encryption disabled" "$regx" "$TRAIL_NAME"
fi
done <<< "${REGION_TRAIL}"
done
else
textPass "$regx: No CloudTrail trails were found for the region" "${regx}" "No trails found"
fi
done
if [[ $trail_count == 0 ]]; then
textFail "$REGION: No CloudTrail trails were found in the account" "$REGION" "$trail"
fi
}

View File

@@ -52,5 +52,9 @@ CHECK_DOC_check33='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cl
CHECK_CAF_EPIC_check33='Logging and Monitoring'
check33(){
check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"'
if [[ "${REGION}" == "us-gov-west-1" || "${REGION}" == "us-gov-east-1" ]]; then
textInfo "${REGION}: This is an AWS GovCloud account and there is no root account to perform checks."
else
check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"'
fi
}

View File

@@ -29,7 +29,7 @@ CHECK_CAF_EPIC_check41='Infrastructure Security'
check41(){
# "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 22 (Scored)"
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`22` && ToPort>=`22`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
SG_LIST=$("${AWSCLI}" ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`22` && ToPort>=`22`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region "${regx}" --output text 2>&1)
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue

View File

@@ -29,7 +29,7 @@ CHECK_CAF_EPIC_check42='Infrastructure Security'
check42(){
# "Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to port 3389 (Scored)"
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`3389` && ToPort>=`3389`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
SG_LIST=$("${AWSCLI}" ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`3389` && ToPort>=`3389`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`) ]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region "${regx}" --output text 2>&1)
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue

View File

@@ -21,8 +21,8 @@ CHECK_ALTERNATE_check71="extra71"
CHECK_ALTERNATE_check701="extra71"
CHECK_ASFF_COMPLIANCE_TYPE_extra71="ens-op.exp.10.aws.trail.2"
CHECK_SERVICENAME_extra71="iam"
CHECK_RISK_extra71='Policy "may" allow Anonymous users to perform actions.'
CHECK_REMEDIATION_extra71='Ensure this repository and its contents should be publicly accessible.'
CHECK_RISK_extra71='Any user with AdministratorAccess is allowed to perform any action on an AWS account, so it needs to have a multi factor authentication enabled to avoid impersonation through a potential credentials leak'
CHECK_REMEDIATION_extra71='Enable MFA for users belonging to groups with AdministratorAccess policies'
CHECK_DOC_extra71='https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html'
CHECK_CAF_EPIC_extra71='Infrastructure Security'
@@ -30,27 +30,32 @@ extra71(){
# "Ensure users of groups with AdministratorAccess policy have MFA tokens enabled "
ADMIN_GROUPS=''
AWS_GROUPS=$($AWSCLI $PROFILE_OPT iam list-groups --output text --region $REGION --query 'Groups[].GroupName')
for grp in $AWS_GROUPS; do
# aws --profile onlinetraining iam list-attached-group-policies --group-name Administrators --query 'AttachedPolicies[].PolicyArn' | grep 'arn:aws:iam::aws:policy/AdministratorAccess'
# list-attached-group-policies
CHECK_ADMIN_GROUP=$($AWSCLI $PROFILE_OPT --region $REGION iam list-attached-group-policies --group-name $grp --output json --query 'AttachedPolicies[].PolicyArn' | grep "arn:${AWS_PARTITION}:iam::aws:policy/AdministratorAccess")
if [[ $CHECK_ADMIN_GROUP ]]; then
ADMIN_GROUPS="$ADMIN_GROUPS $grp"
textInfo "$REGION: $grp group provides administrative access" "$REGION" "$grp"
ADMIN_USERS=$($AWSCLI $PROFILE_OPT iam get-group --region $REGION --group-name $grp --output json --query 'Users[].UserName' | grep '"' | cut -d'"' -f2 )
for auser in $ADMIN_USERS; do
# users in group are Administrators
# users
# check for user MFA device in credential report
USER_MFA_ENABLED=$( cat $TEMP_REPORT_FILE | grep "^$auser," | cut -d',' -f8)
if [[ "true" == $USER_MFA_ENABLED ]]; then
textPass "$REGION: $auser / MFA Enabled / admin via group $grp" "$REGION" "$grp"
else
textFail "$REGION: $auser / MFA DISABLED / admin via group $grp" "$REGION" "$grp"
fi
done
else
textInfo "$REGION: $grp group provides non-administrative access" "$REGION" "$grp"
fi
done
if [[ ${AWS_GROUPS} ]]
then
for grp in $AWS_GROUPS; do
# aws --profile onlinetraining iam list-attached-group-policies --group-name Administrators --query 'AttachedPolicies[].PolicyArn' | grep 'arn:aws:iam::aws:policy/AdministratorAccess'
# list-attached-group-policies
CHECK_ADMIN_GROUP=$($AWSCLI $PROFILE_OPT --region $REGION iam list-attached-group-policies --group-name $grp --output json --query 'AttachedPolicies[].PolicyArn' | grep "arn:${AWS_PARTITION}:iam::aws:policy/AdministratorAccess")
if [[ $CHECK_ADMIN_GROUP ]]; then
ADMIN_GROUPS="$ADMIN_GROUPS $grp"
textInfo "$REGION: $grp group provides administrative access" "$REGION" "$grp"
ADMIN_USERS=$($AWSCLI $PROFILE_OPT iam get-group --region $REGION --group-name $grp --output json --query 'Users[].UserName' | grep '"' | cut -d'"' -f2 )
for auser in $ADMIN_USERS; do
# users in group are Administrators
# users
# check for user MFA device in credential report
USER_MFA_ENABLED=$( cat $TEMP_REPORT_FILE | grep "^$auser," | cut -d',' -f8)
if [[ "true" == $USER_MFA_ENABLED ]]; then
textPass "$REGION: $auser / MFA Enabled / admin via group $grp" "$REGION" "$grp"
else
textFail "$REGION: $auser / MFA DISABLED / admin via group $grp" "$REGION" "$grp"
fi
done
else
textInfo "$REGION: $grp group provides non-administrative access" "$REGION" "$grp"
fi
done
else
textPass "$REGION: There is no IAM groups" "$REGION"
fi
}

View File

@@ -68,8 +68,7 @@ extra7100(){
done
if [[ $PERMISSIVE_POLICIES_LIST ]]; then
textInfo "STS AssumeRole Policies should only include the complete ARNs for the Roles that the user needs"
textInfo "Learn more: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html#roles-usingrole-createpolicy"
textInfo "STS AssumeRole Policies should only include the complete ARNs for the Roles that the user needs. Learn more: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html#roles-usingrole-createpolicy" "$REGION"
for policy in $PERMISSIVE_POLICIES_LIST; do
textFail "$REGION: Policy $policy allows permissive STS Role assumption" "$REGION" "$policy"
done

View File

@@ -25,23 +25,27 @@ CHECK_DOC_extra7101='https://docs.aws.amazon.com/elasticsearch-service/latest/de
CHECK_CAF_EPIC_extra7101='Logging and Monitoring'
extra7101(){
for regx in $REGIONS; do
LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text 2>&1)
if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to list domain names" "$regx"
for regx in ${REGIONS}; do
LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
continue
fi
if [[ $LIST_OF_DOMAINS ]]; then
for domain in $LIST_OF_DOMAINS;do
AUDIT_LOGS_ENABLED=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.LogPublishingOptions.Options.AUDIT_LOGS.Enabled --output text |grep -v ^None|grep -v ^False)
if [[ $AUDIT_LOGS_ENABLED ]];then
textPass "$regx: Amazon ES domain $domain AUDIT_LOGS enabled" "$regx" "$domain"
if [[ "${LIST_OF_DOMAINS}" ]]; then
for domain in ${LIST_OF_DOMAINS}; do
AUDIT_LOGS_ENABLED=$("${AWSCLI}" es describe-elasticsearch-domain-config --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainConfig.LogPublishingOptions.Options.AUDIT_LOGS.Enabled' --output text 2>&1)
if [[ $(echo "${AUDIT_LOGS_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "${regx}: Access Denied trying to get ES domain config for ${domain}" "${regx}"
continue
fi
if [[ $(tr '[:upper:]' '[:lower:]' <<< "${AUDIT_LOGS_ENABLED}") == "true" ]]; then
textPass "${regx}: Amazon ES domain ${domain} AUDIT_LOGS enabled" "${regx}" "${domain}"
else
textFail "$regx: Amazon ES domain $domain AUDIT_LOGS disabled!" "$regx" "$domain"
textFail "${regx}: Amazon ES domain ${domain} AUDIT_LOGS disabled!" "${regx}" "${domain}"
fi
done
else
textInfo "$regx: No Amazon ES domain found" "$regx"
textInfo "${regx}: No Amazon ES domain found" "${regx}"
fi
done
}

View File

@@ -23,7 +23,7 @@ CHECK_REMEDIATION_extra7102='Check Identified IPs; consider changing them to pri
CHECK_DOC_extra7102='https://www.shodan.io/'
CHECK_CAF_EPIC_extra7102='Infrastructure Security'
# Watch out, always use Shodan API key, if you use `curl https://www.shodan.io/host/{ip}` massively
# Watch out, always use Shodan API key, if you use `curl https://www.shodan.io/host/{ip}` massively
# your IP will be banned by Shodan
# This is the right way to do so
@@ -32,31 +32,31 @@ CHECK_CAF_EPIC_extra7102='Infrastructure Security'
# Each finding will be saved in prowler/output folder for further review.
extra7102(){
if [[ ! $SHODAN_API_KEY ]]; then
textInfo "[extra7102] Requires a Shodan API key to work. Use -N <shodan_api_key>"
else
for regx in $REGIONS; do
LIST_OF_EIP=$($AWSCLI $PROFILE_OPT --region $regx ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text 2>&1)
if [[ $(echo "$LIST_OF_EIP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe network interfaces" "$regx"
continue
fi
if [[ $LIST_OF_EIP ]]; then
for ip in $LIST_OF_EIP;do
SHODAN_QUERY=$(curl -ks https://api.shodan.io/shodan/host/$ip?key=$SHODAN_API_KEY)
# Shodan has a request rate limit of 1 request/second.
sleep 1
if [[ $SHODAN_QUERY == *"No information available for that IP"* ]]; then
textPass "$regx: IP $ip is not listed in Shodan" "$regx"
else
echo $SHODAN_QUERY > $OUTPUT_DIR/shodan-output-$ip.json
IP_SHODAN_INFO=$(cat $OUTPUT_DIR/shodan-output-$ip.json | jq -r '. | { ports: .ports, org: .org, country: .country_name }| @text' | tr -d \"\{\}\}\]\[ | tr , '\ ' )
textFail "$regx: IP $ip is listed in Shodan with data $IP_SHODAN_INFO. More info https://www.shodan.io/host/$ip and $OUTPUT_DIR/shodan-output-$ip.json" "$regx" "$ip"
fi
done
if [[ ! $SHODAN_API_KEY ]]; then
textInfo "$regx: Requires a Shodan API key to work. Use -N <shodan_api_key>" "$regx"
else
textInfo "$regx: No Public or Elastic IPs found" "$regx"
LIST_OF_EIP=$($AWSCLI $PROFILE_OPT --region $regx ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text 2>&1)
if [[ $(echo "$LIST_OF_EIP" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe network interfaces" "$regx"
continue
fi
if [[ $LIST_OF_EIP ]]; then
for ip in $LIST_OF_EIP;do
SHODAN_QUERY=$(curl -ks https://api.shodan.io/shodan/host/$ip?key=$SHODAN_API_KEY)
# Shodan has a request rate limit of 1 request/second.
sleep 1
if [[ $SHODAN_QUERY == *"No information available for that IP"* ]]; then
textPass "$regx: IP $ip is not listed in Shodan" "$regx"
else
echo $SHODAN_QUERY > $OUTPUT_DIR/shodan-output-$ip.json
IP_SHODAN_INFO=$(cat $OUTPUT_DIR/shodan-output-$ip.json | jq -r '. | { ports: .ports, org: .org, country: .country_name }| @text' | tr -d \"\{\}\}\]\[ | tr , '\ ' )
textFail "$regx: IP $ip is listed in Shodan with data $IP_SHODAN_INFO. More info https://www.shodan.io/host/$ip and $OUTPUT_DIR/shodan-output-$ip.json" "$regx" "$ip"
fi
done
else
textInfo "$regx: No Public or Elastic IPs found" "$regx"
fi
fi
done
fi
}

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2020) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -31,17 +31,21 @@ extra7111(){
textInfo "$regx: Access Denied trying to list notebook instances" "$regx"
continue
fi
if [[ $LIST_SM_NB_INSTANCES ]];then
if [[ $LIST_SM_NB_INSTANCES ]];then
for nb_instance in $LIST_SM_NB_INSTANCES; do
SM_NB_DIRECTINET=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'DirectInternetAccess' --output text)
SM_NB_DIRECTINET=$($AWSCLI $PROFILE_OPT --region $regx sagemaker describe-notebook-instance --notebook-instance-name $nb_instance --query 'DirectInternetAccess' --output text 2>&1)
if [[ $(echo "$SM_NB_DIRECTINET" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe notebook instances" "$regx"
continue
fi
if [[ "${SM_NB_DIRECTINET}" == "Enabled" ]]; then
textFail "${regx}: Sagemaker Notebook instance $nb_instance has direct internet access enabled" "${regx}" "$nb_instance"
else
textPass "${regx}: Sagemaker Notebook instance $nb_instance has direct internet access disabled" "${regx}" "$nb_instance"
fi
fi
done
else
else
textInfo "${regx}: No Sagemaker Notebook instances found" "${regx}"
fi
fi
done
}
}

View File

@@ -52,7 +52,7 @@ extra7113(){
fi
done
else
textInfo "$regx: No RDS instances found" "$regx" "$rdsinstance"
textInfo "$regx: No RDS instances found" "$regx"
fi
done
}

View File

@@ -23,13 +23,22 @@ CHECK_REMEDIATION_extra712='Enable Amazon Macie and create appropriate jobs to d
CHECK_DOC_extra712='https://docs.aws.amazon.com/macie/latest/user/getting-started.html'
CHECK_CAF_EPIC_extra712='Data Protection'
extra712(){
# "No API commands available to check if Macie is enabled,"
# "just looking if IAM Macie related permissions exist. "
MACIE_IAM_ROLES_CREATED=$($AWSCLI iam list-roles $PROFILE_OPT --query 'Roles[*].Arn'|grep AWSMacieServiceCustomer|wc -l)
if [[ $MACIE_IAM_ROLES_CREATED -eq 2 ]];then
textPass "$REGION: Macie related IAM roles exist so it might be enabled. Check it out manually" "$REGION"
else
textFail "$REGION: No Macie related IAM roles found. It is most likely not to be enabled" "$REGION"
fi
}
extra712(){
# Macie supports get-macie-session which tells the current status, if not Disabled.
# Capturing the STDOUT can help determine when Disabled.
for region in $REGIONS; do
MACIE_STATUS=$($AWSCLI macie2 get-macie-session ${PROFILE_OPT} --region "$region" --query status --output text 2>&1)
if [[ "$MACIE_STATUS" == "ENABLED" ]]; then
textPass "$region: Macie is enabled." "$region"
elif [[ "$MACIE_STATUS" == "PAUSED" ]]; then
textFail "$region: Macie is currently in a SUSPENDED state." "$region"
elif grep -q -E 'Macie is not enabled' <<< "${MACIE_STATUS}"; then
textFail "$region: Macie is not enabled." "$region"
elif grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${MACIE_STATUS}"; then
textInfo "$region: Access Denied trying to get AWS Macie information." "$region"
fi
done
}

View File

@@ -33,6 +33,6 @@ extra7123(){
textFail "User $user has 2 active access keys" "$REGION" "$user"
done
else
textPass "No users with 2 active access keys"
textPass "No users with 2 active access keys" "$REGION"
fi
}
}

View File

@@ -31,15 +31,15 @@ extra7125(){
for user in $LIST_USERS; do
# Would be virtual if sms-mfa or mfa, hardware is u2f or different.
MFA_TYPE=$($AWSCLI iam list-mfa-devices --user-name $user $PROFILE_OPT --region $REGION --query MFADevices[].SerialNumber --output text | awk -F':' '{ print $6 }'| awk -F'/' '{ print $1 }')
if [[ $MFA_TYPE == "mfa" || $MFA_TYPE == "sms-mfa" ]]; then
textInfo "User $user has virtual MFA enabled"
elif [[ $MFA_TYPE == "" ]]; then
if [[ $MFA_TYPE == "mfa" || $MFA_TYPE == "sms-mfa" ]]; then
textInfo "User $user has virtual MFA enabled" "$REGION" "$user"
elif [[ $MFA_TYPE == "" ]]; then
textFail "User $user has not hardware MFA enabled" "$REGION" "$user"
else
else
textPass "User $user has hardware MFA enabled" "$REGION" "$user"
fi
done
else
textPass "No users found"
textPass "No users found" "$REGION"
fi
}
}

View File

@@ -18,15 +18,15 @@ CHECK_SEVERITY_extra7131="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7131="AwsRdsDbInstance"
CHECK_ALTERNATE_check7131="extra7131"
CHECK_SERVICENAME_extra7131="rds"
CHECK_RISK_extra7131='Auto Minor Version Upgrade is a feature that you can enable to have your database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs; and therefor should be applied.'
CHECK_REMEDIATION_extra7131='Enable auto minor version upgrade for all databases and environments.'
CHECK_RISK_extra7131='Auto Minor Version Upgrade is a feature that you can enable to have your relational database automatically upgraded when a new minor database engine version is available. Minor version upgrades often patch security vulnerabilities and fix bugs; and therefor should be applied.'
CHECK_REMEDIATION_extra7131='Enable auto minor version upgrade for all relational databases and environments.'
CHECK_DOC_extra7131='https://aws.amazon.com/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/'
CHECK_CAF_EPIC_extra7131='Infrastructure Security'
extra7131(){
for regx in $REGIONS; do
# LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true` && DBInstanceStatus==`"available"`].[DBInstanceIdentifier,Endpoint.Address]' --output text)
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].[DBInstanceIdentifier,AutoMinorVersionUpgrade]' --output text 2>&1)
LIST_OF_RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query "DBInstances[?Engine != 'docdb'].[DBInstanceIdentifier,AutoMinorVersionUpgrade]" --output text 2>&1)
if [[ $(echo "$LIST_OF_RDS_INSTANCES" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe DB instances" "$regx"
continue

View File

@@ -34,14 +34,14 @@ extra7132(){
for rdsinstance in ${RDS_INSTANCES}; do
RDS_NAME="$rdsinstance"
MONITORING_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].[EnhancedMonitoringResourceArn]' --output text)
if [[ $MONITORING_FLAG == "None" ]];then
textFail "$regx: RDS instance: $RDS_NAME has enhanced monitoring disabled!" "$rex" "$RDS_NAME"
if [[ $MONITORING_FLAG == "None" ]];then
textFail "$regx: RDS instance: $RDS_NAME has enhanced monitoring disabled!" "$regx" "$RDS_NAME"
else
textPass "$regx: RDS instance: $RDS_NAME has enhanced monitoring enabled." "$regx" "$RDS_NAME"
fi
done
else
textInfo "$regx: no RDS instances found" "$regx" "$RDS_NAME"
textInfo "$regx: no RDS instances found" "$regx"
fi
done
}

View File

@@ -25,7 +25,7 @@ CHECK_CAF_EPIC_extra7134='Infrastructure Security'
extra7134(){
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`20` && ToPort==`21`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`20` && ToPort==`21`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
@@ -38,4 +38,4 @@ extra7134(){
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for FTP ports" "$regx" "$SG"
fi
done
}
}

View File

@@ -25,7 +25,7 @@ CHECK_CAF_EPIC_extra7135='Infrastructure Security'
extra7135(){
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`9092` && ToPort==`9092`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`9092` && ToPort==`9092`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
@@ -38,4 +38,4 @@ extra7135(){
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Kafka ports" "$regx"
fi
done
}
}

View File

@@ -11,7 +11,7 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7136="7.136"
CHECK_TITLE_extra7136="[extra7136] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Telnet port 23 "
CHECK_TITLE_extra7136="[extra7136] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Telnet port 23"
CHECK_SCORED_extra7136="NOT_SCORED"
CHECK_CIS_LEVEL_extra7136="EXTRA"
CHECK_SEVERITY_extra7136="High"
@@ -25,7 +25,7 @@ CHECK_CAF_EPIC_extra7136='Infrastructure Security'
extra7136(){
for regx in $REGIONS; do
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`23` && ToPort==`23`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort==`23` && ToPort==`23`)) && (contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`)) && (IpProtocol==`tcp`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text 2>&1)
if [[ $(echo "$SG_LIST" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
@@ -38,4 +38,4 @@ extra7136(){
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Telnet ports" "$regx" "$SG"
fi
done
}
}

View File

@@ -11,7 +11,7 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7137="7.137"
CHECK_TITLE_extra7137="[extra7137] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Windows SQL Server ports 1433 or 1434 "
CHECK_TITLE_extra7137="[extra7137] Ensure no security groups allow ingress from 0.0.0.0/0 or ::/0 to Windows SQL Server ports 1433 or 1434"
CHECK_SCORED_extra7137="NOT_SCORED"
CHECK_CIS_LEVEL_extra7137="EXTRA"
CHECK_SEVERITY_extra7137="High"
@@ -38,4 +38,4 @@ extra7137(){
textPass "$regx: No Security Groups found with any port open to 0.0.0.0/0 for Microsoft SQL Server ports" "$regx"
fi
done
}
}

View File

@@ -24,36 +24,45 @@ CHECK_DOC_extra7141='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGu
CHECK_CAF_EPIC_extra7141='IAM'
extra7141(){
SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM"
if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then
SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME"
if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then
# this folder is deleted once this check is finished
mkdir $SECRETS_TEMP_FOLDER
mkdir "${SECRETS_TEMP_FOLDER}"
fi
for regx in $REGIONS; do
SSM_DOCS=$($AWSCLI $PROFILE_OPT --region $regx ssm list-documents --filters Key=Owner,Values=Self --query DocumentIdentifiers[].Name --output text 2>&1)
if [[ $(echo "$SSM_DOCS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to list documents" "$regx"
continue
fi
if [[ $SSM_DOCS ]];then
for ssmdoc in $SSM_DOCS; do
SSM_DOC_FILE="$SECRETS_TEMP_FOLDER/extra7141-$ssmdoc-$regx-content.txt"
$AWSCLI $PROFILE_OPT --region $regx ssm get-document --name $ssmdoc --output text --document-format JSON > $SSM_DOC_FILE
FINDINGS=$(secretsDetector file $SSM_DOC_FILE)
if [[ $FINDINGS -eq 0 ]]; then
textPass "$regx: No secrets found in SSM Document $ssmdoc" "$regx" "$ssmdoc"
# delete file if nothing interesting is there
rm -f $SSM_DOC_FILE
else
textFail "$regx: Potential secret found SSM Document $ssmdoc" "$regx" "$ssmdoc"
# delete file to not leave trace, user must look at the CFN Stack
rm -f $SSM_DOC_FILE
fi
done
for regx in ${REGIONS}; do
CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector)
if [[ $? -eq 241 ]]; then
textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx"
else
textInfo "$regx: No SSM Document found." "$regx"
SSM_DOCS=$("${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm list-documents --filters 'Key=Owner,Values=Self' --query 'DocumentIdentifiers[].Name' --output text 2>&1)
if [[ $(echo "${SSM_DOCS}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "${regx}: Access Denied trying to list documents" "${regx}"
continue
fi
if [[ ${SSM_DOCS} ]];then
for ssmdoc in ${SSM_DOCS}; do
SSM_DOC_FILE="${SECRETS_TEMP_FOLDER}/extra7141-${ssmdoc}-${regx}-content.txt"
"${AWSCLI}" ${PROFILE_OPT} --region "${regx}" ssm get-document --name "${ssmdoc}" --output text --document-format JSON > "${SSM_DOC_FILE}" 2>&1
if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' "${SSM_DOC_FILE}") ]]; then
textInfo "${regx}: Access Denied trying to get document" "${regx}"
continue
fi
FINDINGS=$(secretsDetector file "${SSM_DOC_FILE}")
if [[ "${FINDINGS}" -eq 0 ]]; then
textPass "${regx}: No secrets found in SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
# delete file if nothing interesting is there
rm -f "${SSM_DOC_FILE}"
else
textFail "${regx}: Potential secret found SSM Document ${ssmdoc}" "${regx}" "${ssmdoc}"
# delete file to not leave trace, user must look at the CFN Stack
rm -f "${SSM_DOC_FILE}"
fi
done
else
textInfo "${regx}: No SSM Document found." "${regx}"
fi
fi
done
rm -rf $SECRETS_TEMP_FOLDER
rm -rf "${SECRETS_TEMP_FOLDER}"
}

View File

@@ -41,8 +41,8 @@ extra7142(){
textFail "$regx: Application Load Balancer $alb is not dropping invalid header fields" "$regx" "$alb"
fi
done
else
textInfo "$regx: no ALBs found"
else
textInfo "$regx: no ALBs found" "$regx"
fi
done
}

View File

@@ -18,8 +18,8 @@ CHECK_SEVERITY_extra7144="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7144="AwsCloudWatch"
CHECK_ALTERNATE_check7144="extra7144"
CHECK_SERVICENAME_extra7144="cloudwatch"
CHECK_RISK_extra7144=''
CHECK_REMEDIATION_extra7144=''
CHECK_RISK_extra7144='Cross-Account access to CloudWatch could increase the risk of compromising information between accounts'
CHECK_REMEDIATION_extra7144='Grant usage permission on a per-resource basis to enforce least privilege and Zero Trust principles'
CHECK_DOC_extra7144='https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Cross-Account-Cross-Region.html'
CHECK_CAF_EPIC_extra7144='Logging and Monitoring'

View File

@@ -25,27 +25,32 @@ CHECK_CAF_EPIC_extra7147='Data Protection'
extra7147(){
for regx in $REGIONS; do
LIST_OF_VAULTS=$($AWSCLI glacier list-vaults $PROFILE_OPT --region $regx --account-id $ACCOUNT_NUM --query VaultList[*].VaultName --output text 2>&1|xargs -n1)
if [[ $(echo "$LIST_OF_VAULTS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to list vaults" "$regx"
LIST_OF_VAULTS=$($AWSCLI glacier list-vaults ${PROFILE_OPT} --region "${regx}" --account-id "${ACCOUNT_NUM}" --query VaultList[*].VaultName --output text 2>&1|xargs -n1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_OF_VAULTS"; then
textInfo "$regx: Access Denied trying to list vaults" "${regx}"
continue
fi
# Check for unsupported regions
if grep -q -E 'error' <<< "${LIST_OF_VAULTS}"; then
textInfo "$regx: An error occurred when calling the ListVaults operation - check if this region is supported" "${regx}"
continue
fi
if [[ $LIST_OF_VAULTS ]]; then
for vault in $LIST_OF_VAULTS;do
VAULT_POLICY_STATEMENTS=$($AWSCLI glacier $PROFILE_OPT get-vault-access-policy --region $regx --account-id $ACCOUNT_NUM --vault-name $vault --output json --query policy.Policy 2>&1)
VAULT_POLICY_STATEMENTS=$($AWSCLI glacier ${PROFILE_OPT} get-vault-access-policy --region "${regx}" --account-id "${ACCOUNT_NUM}" --vault-name "${vault}" --output json --query policy.Policy 2>&1)
if [[ $VAULT_POLICY_STATEMENTS == *GetVaultAccessPolicy* ]]; then
textInfo "$regx: Vault $vault doesn't have any policy" "$regx" "$vault"
textInfo "${regx}: Vault $vault doesn't have any policy" "${regx}" "$vault"
else
VAULT_POLICY_BAD_STATEMENTS=$(echo $VAULT_POLICY_STATEMENTS | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
VAULT_POLICY_BAD_STATEMENTS=$(jq '. | fromjson' <<< "${VAULT_POLICY_STATEMENTS}" | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")')
if [[ $VAULT_POLICY_BAD_STATEMENTS != "" ]]; then
textFail "$regx: Vault $vault has policy which allows access to everyone" "$regx" "$vault"
textFail "${regx}: Vault $vault has policy which allows access to everyone" "${regx}" "$vault"
else
textPass "$regx: Vault $vault has policy which does not allow access to everyone" "$regx" "$vault"
textPass "${regx}: Vault $vault has policy which does not allow access to everyone" "${regx}" "$vault"
fi
fi
done
else
textInfo "$regx: No Glacier vaults found" "$regx"
textInfo "${regx}: No Glacier vaults found" "${regx}"
fi
done
}
}

View File

@@ -33,7 +33,11 @@ extra7148() {
if [[ $LIST_OF_EFS_SYSTEMS ]]; then
for filesystem in $LIST_OF_EFS_SYSTEMS; do
# if retention is 0 then is disabled
BACKUP_POLICY=$($AWSCLI efs describe-backup-policy $PROFILE_OPT --region $regx --file-system-id $filesystem --query BackupPolicy --output text)
BACKUP_POLICY=$($AWSCLI efs describe-backup-policy $PROFILE_OPT --region $regx --file-system-id $filesystem --query BackupPolicy --output text 2>&1)
if [[ $(echo "$BACKUP_POLICY" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe backup policy" "$regx"
continue
fi
if [[ $BACKUP_POLICY == "DISABLED" ]]; then
textFail "$regx: File system $filesystem does not have backup enabled!" "$regx" "$filesystem"
else

View File

@@ -24,29 +24,35 @@ CHECK_DOC_extra715='https://docs.aws.amazon.com/elasticsearch-service/latest/dev
CHECK_CAF_EPIC_extra715='Logging and Monitoring'
extra715(){
for regx in $REGIONS; do
LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text 2>&1)
if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to list domain names" "$regx"
for regx in ${REGIONS}; do
LIST_OF_DOMAINS=$("${AWSCLI}" es list-domain-names ${PROFILE_OPT} --region "${regx}" --query 'DomainNames[].DomainName' --output text 2>&1)
if [[ $(echo "${LIST_OF_DOMAINS}" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "${regx}: Access Denied trying to list domain names" "${regx}"
continue
fi
if [[ $LIST_OF_DOMAINS ]]; then
for domain in $LIST_OF_DOMAINS;do
SEARCH_SLOWLOG_ENABLED=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.LogPublishingOptions.Options.SEARCH_SLOW_LOGS.Enabled --output text |grep -v ^None|grep -v ^False)
if [[ $SEARCH_SLOWLOG_ENABLED ]];then
textPass "$regx: Amazon ES domain $domain SEARCH_SLOW_LOGS enabled" "$regx" "$domain"
else
textFail "$regx: Amazon ES domain $domain SEARCH_SLOW_LOGS disabled!" "$regx" "$domain"
fi
INDEX_SLOWLOG_ENABLED=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.LogPublishingOptions.Options.INDEX_SLOW_LOGS.Enabled --output text |grep -v ^None|grep -v ^False)
if [[ $INDEX_SLOWLOG_ENABLED ]];then
textPass "$regx: Amazon ES domain $domain INDEX_SLOW_LOGS enabled" "$regx" "$domain"
else
textFail "$regx: Amazon ES domain $domain INDEX_SLOW_LOGS disabled!" "$regx" "$domain"
if [[ "${LIST_OF_DOMAINS}" ]]; then
for domain in ${LIST_OF_DOMAINS}; do
SLOWLOG_ENABLED=$("${AWSCLI}" es describe-elasticsearch-domain-config --domain-name "${domain}" ${PROFILE_OPT} --region "${regx}" --query 'DomainConfig.LogPublishingOptions.Options.[SEARCH_SLOW_LOGS.Enabled, INDEX_SLOW_LOGS.Enabled]' --output text 2>&1)
if [[ $(echo "${SLOWLOG_ENABLED}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "${regx}: Access Denied trying to get ES domain config for ${domain}" "${regx}"
continue
fi
read -r SEARCH_SLOWLOG_ENABLED INDEX_SLOWLOG_ENABLED <<< "${SLOWLOG_ENABLED}" && {
if [[ $(tr '[:upper:]' '[:lower:]' <<< "${SEARCH_SLOWLOG_ENABLED}") == "true" ]]; then
textPass "${regx}: Amazon ES domain ${domain} SEARCH_SLOW_LOGS enabled" "${regx}" "${domain}"
else
textFail "${regx}: Amazon ES domain ${domain} SEARCH_SLOW_LOGS disabled!" "${regx}" "${domain}"
fi
if [[ $(tr '[:upper:]' '[:lower:]' <<< "${INDEX_SLOWLOG_ENABLED}") == "true" ]]; then
textPass "${regx}: Amazon ES domain ${domain} INDEX_SLOW_LOGS enabled" "${regx}" "${domain}"
else
textFail "${regx}: Amazon ES domain ${domain} INDEX_SLOW_LOGS disabled!" "${regx}" "${domain}"
fi
}
done
else
textInfo "$regx: No Amazon ES domain found" "$regx"
textInfo "${regx}: No Amazon ES domain found" "${regx}"
fi
done
}

View File

@@ -29,7 +29,7 @@ extra7156(){
# "Check if API Gateway V2 has Access Logging enabled "
for regx in $REGIONS; do
LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query Items[*].ApiId --output text 2>&1)
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|BadRequestException') ]]; then
textInfo "$regx: Access Denied trying to get APIs" "$regx"
continue
fi
@@ -54,4 +54,4 @@ extra7156(){
textInfo "$regx: No API Gateway found" "$regx"
fi
done
}
}

View File

@@ -26,7 +26,7 @@ CHECK_CAF_EPIC_extra7157='IAM'
extra7157(){
for regx in $REGIONS; do
LIST_OF_API_GW=$($AWSCLI apigatewayv2 get-apis $PROFILE_OPT --region $regx --query "Items[*].ApiId" --output text 2>&1)
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
if [[ $(echo "$LIST_OF_API_GW" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|BadRequestException') ]]; then
textInfo "$regx: Access Denied trying to get APIs" "$regx"
continue
fi

View File

@@ -25,22 +25,30 @@ CHECK_CAF_EPIC_extra716='Infrastructure Security'
extra716(){
for regx in $REGIONS; do
LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text 2>&1)
LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query 'DomainNames[].DomainName' --output text 2>&1)
if [[ $(echo "$LIST_OF_DOMAINS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to list domain names" "$regx"
continue
fi
if [[ $LIST_OF_DOMAINS ]]; then
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX)
for domain in $LIST_OF_DOMAINS;do
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX)
# get endpoint or vpc endpoints
ES_DOMAIN_ENDPOINT=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.[Endpoint || Endpoints]' --output text)
ES_DOMAIN_INFO=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.[Endpoints.vpc, VPCOptions.VPCId]' --output text 2>&1)
if [[ $(echo "$ES_DOMAIN_INFO" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to get domain $domain" "$regx"
continue
fi
read ES_DOMAIN_ENDPOINT_VPC ES_DOMAIN_VPC <<< "$ES_DOMAIN_INFO" &&
# If the endpoint starts with "vpc-" it is in a VPC then it is fine.
if [[ "$ES_DOMAIN_ENDPOINT" =~ ^vpc-* ]];then
ES_DOMAIN_VPC=$($AWSCLI es describe-elasticsearch-domain --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainStatus.VPCOptions.VPCId' --output text)
if [[ "${ES_DOMAIN_ENDPOINT_VPC:0:3}" == "vpc" ]]; then
textInfo "$regx: Amazon ES domain $domain is in VPC $ES_DOMAIN_VPC run extra779 to make sure it is not exposed using custom proxy" "$regx" "$domain"
else
$AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.AccessPolicies.Options --output text > $TEMP_POLICY_FILE 2> /dev/null
$AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query 'DomainConfig.AccessPolicies.Options' --output text > $TEMP_POLICY_FILE 2>&1
if [[ $(grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' $TEMP_POLICY_FILE) ]]; then
textInfo "$regx: Access Denied trying to get domain config for $domain" "$regx"
continue
fi
# check if the policy has a principal set up
CHECK_ES_POLICY_PRINCIPAL=$(cat $TEMP_POLICY_FILE | jq -r '. | .Statement[] | select(.Effect == "Allow" and (((.Principal|type == "object") and .Principal.AWS != "*") or ((.Principal|type == "string") and .Principal != "*")) and select(has("Condition") | not))')
if [[ $CHECK_ES_POLICY_PRINCIPAL ]]; then
@@ -89,9 +97,9 @@ extra716(){
textPass "$regx: Amazon ES domain $domain does not allow anonymous access" "$regx" "$domain"
fi
fi
rm -f $TEMP_POLICY_FILE
fi
done
[[ -f "${TEMP_POLICY_FILE}" ]] && rm -f $TEMP_POLICY_FILE
else
textInfo "$regx: No Amazon ES domain found" "$regx"
fi

View File

@@ -11,7 +11,7 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7161="7.161"
CHECK_TITLE_extra7161="[extra7161] Check if EFS have protects sensative data with encryption at rest"
CHECK_TITLE_extra7161="[extra7161] Check if EFS protects sensitive data with encryption at rest"
CHECK_SCORED_extra7161="NOT_SCORED"
CHECK_CIS_LEVEL_extra7161="EXTRA"
CHECK_SEVERITY_extra7161="Medium"

View File

@@ -11,7 +11,7 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra7162="7.162"
CHECK_TITLE_extra7162="[extra7162] Check if CloudWatch Log Groups have a retention policy of 365 days"
CHECK_TITLE_extra7162="[extra7162] Check if CloudWatch Log Groups have a retention policy of at least 365 days"
CHECK_SCORED_extra7162="NOT_SCORED"
CHECK_CIS_LEVEL_extra7162="EXTRA"
CHECK_SEVERITY_extra7162="Medium"
@@ -19,36 +19,57 @@ CHECK_ASFF_RESOURCE_TYPE_extra7162="AwsLogsLogGroup"
CHECK_ALTERNATE_check7162="extra7162"
CHECK_SERVICENAME_extra7162="cloudwatch"
CHECK_RISK_extra7162='If log groups have a low retention policy of less than 365 days; crucial logs and data can be lost'
CHECK_REMEDIATION_extra7162='Add Log Retention policy of 365 days to log groups. This will persist logs and traces for a long time.'
CHECK_REMEDIATION_extra7162='Add Log Retention policy of at least 365 days to log groups. This will persist logs and traces for a long time.'
CHECK_DOC_extra7162='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Logs.html'
CHECK_CAF_EPIC_extra7162='Data Retention'
extra7162() {
# "Check if CloudWatch Log Groups have a retention policy of 365 days"
declare -i LOG_GROUP_RETENTION_PERIOD_DAYS=365
for regx in $REGIONS; do
LIST_OF_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays=="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text 2>&1)
if [[ $(echo "$LIST_OF_365_RETENTION_LOG_GROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
textInfo "$regx: Access Denied trying to describe log groups" "$regx"
local LOG_GROUP_RETENTION_PERIOD_DAYS="365"
for regx in ${REGIONS}; do
LIST_OF_365_OR_MORE_RETENTION_LOG_GROUPS=$("${AWSCLI}" logs describe-log-groups ${PROFILE_OPT} --region "${regx}" --query "logGroups[?retentionInDays>=\`${LOG_GROUP_RETENTION_PERIOD_DAYS}\`].[logGroupName]" --output text 2>&1)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_365_OR_MORE_RETENTION_LOG_GROUPS}"; then
textInfo "${regx}: Access Denied trying to describe log groups" "${regx}"
continue
fi
if [[ $LIST_OF_365_RETENTION_LOG_GROUPS ]]; then
for log in $LIST_OF_365_RETENTION_LOG_GROUPS; do
textPass "$regx: $log Log Group has 365 days retention period!" "$regx" "$log"
if [[ ${LIST_OF_365_OR_MORE_RETENTION_LOG_GROUPS} ]]; then
for log in ${LIST_OF_365_OR_MORE_RETENTION_LOG_GROUPS}; do
textPass "${regx}: ${log} Log Group has at least 365 days retention period!" "${regx}" "${log}"
done
fi
LIST_OF_NON_365_RETENTION_LOG_GROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[?retentionInDays!="${LOG_GROUP_RETENTION_PERIOD_DAYS}"].[logGroupName]' --output text)
if [[ $LIST_OF_NON_365_RETENTION_LOG_GROUPS ]]; then
for log in $LIST_OF_NON_365_RETENTION_LOG_GROUPS; do
textFail "$regx: $log Log Group does not have 365 days retention period!" "$regx" "$log"
LIST_OF_NEVER_EXPIRE_RETENTION_LOG_GROUPS=$("${AWSCLI}" logs describe-log-groups ${PROFILE_OPT} --region "${regx}" --query "logGroups[?retentionInDays==null].[logGroupName]" --output text 2>&1)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_NEVER_EXPIRE_RETENTION_LOG_GROUPS}"; then
textInfo "${regx}: Access Denied trying to describe log groups" "${regx}"
continue
fi
if [[ ${LIST_OF_NEVER_EXPIRE_RETENTION_LOG_GROUPS} ]]; then
for log in ${LIST_OF_NEVER_EXPIRE_RETENTION_LOG_GROUPS}; do
textPass "${regx}: ${log} Log Group retention period never expires!" "${regx}" "${log}"
done
fi
REGION_NO_LOG_GROUP=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output text)
if [[ $REGION_NO_LOG_GROUP ]]; then
LIST_OF_NON_365_RETENTION_LOG_GROUPS=$("${AWSCLI}" logs describe-log-groups ${PROFILE_OPT} --region "${regx}" --query "logGroups[?retentionInDays<\`${LOG_GROUP_RETENTION_PERIOD_DAYS}\`].[logGroupName]" --output text 2>&1)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_NON_365_RETENTION_LOG_GROUPS}"; then
textInfo "${regx}: Access Denied trying to describe log groups" "${regx}"
continue
fi
if [[ ${LIST_OF_NON_365_RETENTION_LOG_GROUPS} ]]; then
for log in ${LIST_OF_NON_365_RETENTION_LOG_GROUPS}; do
textFail "${regx}: ${log} Log Group does not have at least 365 days retention period!" "${regx}" "${log}"
done
fi
REGION_NO_LOG_GROUP=$("${AWSCLI}" logs describe-log-groups ${PROFILE_OPT} --region "${regx}" --output text)
if grep -E -q 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${REGION_NO_LOG_GROUP}"; then
textInfo "${regx}: Access Denied trying to describe log groups" "${regx}"
continue
fi
if [[ ${REGION_NO_LOG_GROUP} ]]; then
:
else
textInfo "$regx does not have a Log Group!" "$regx"
textInfo "${regx} does not have a Log Group!" "${regx}"
fi
done
}

View File

@@ -33,7 +33,7 @@ CHECK_ASFF_RESOURCE_TYPE_extra7163="AwsSecretsManagerSecret"
CHECK_ALTERNATE_extra7163="extra7163"
CHECK_SERVICENAME_extra7163="secretsmanager"
CHECK_RISK_extra7163="Rotating secrets minimizes exposure to attacks using stolen keys."
CHECK_REMEDITATION_extra7163="Enable key rotation on Secrets Manager key."
CHECK_REMEDIATION_extra7163="Enable key rotation on Secrets Manager key."
CHECK_DOC_extra7163="https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets_strategies.html"
CHECK_CAF_EPIC_extra7163="Data Protection"

View File

@@ -18,7 +18,7 @@
# --log-group-name <value>
# --kms-key-id <value>
# [--cli-input-json <value>]
# [--generate-cli-skeleton <value>]
# [--generate-cli-skeleton <value>]
CHECK_ID_extra7164="7.164"
CHECK_TITLE_extra7164="[extra7164] Check if CloudWatch log groups are protected by AWS KMS "
@@ -29,33 +29,34 @@ CHECK_ASFF_RESOURCE_TYPE_extra7164="Logs"
CHECK_ALTERNATE_extra7164="extra7164"
CHECK_SERVICENAME_extra7164="logs"
CHECK_RISK_extra7164="Using customer managed KMS to encrypt CloudWatch log group provide additional confidentiality and control over the log data"
CHECK_REMEDITATION_extra7164="Associate KMS Key with Cloudwatch log group."
CHECK_REMEDIATION_extra7164="Associate KMS Key with Cloudwatch log group."
CHECK_DOC_extra7164="https://docs.aws.amazon.com/cli/latest/reference/logs/associate-kms-key.html"
CHECK_CAF_EPIC_extra7164="Data Protection"
extra7164(){
# "Check if Cloudwatch log groups are associated with AWS KMS"
# "Check if Cloudwatch log groups are associated with AWS KMS"
for regx in $REGIONS; do
LIST_OF_LOGGROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --output json 2>&1 )
if [[ $(echo "$LIST_OF_LOGGROUPS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then
LIST_OF_LOGGROUPS=$($AWSCLI logs describe-log-groups $PROFILE_OPT --region $regx --query 'logGroups[]' 2>&1 )
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_LOGGROUPS}"
then
textInfo "$regx: Access Denied trying to describe log groups" "$regx"
continue
fi
if [[ $LIST_OF_LOGGROUPS ]]; then
LIST_OF_LOGGROUPS_WITHOUT_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' | jq '. | select( has("kmsKeyId") == false )' | jq -r '.logGroupName')
LIST_OF_LOGGROUPS_WITH_KMS=$(echo "${LIST_OF_LOGGROUPS}" | jq '.logGroups[]' | jq '. | select( has("kmsKeyId") == true )' | jq -r '.logGroupName')
if [[ $LIST_OF_LOGGROUPS_WITHOUT_KMS ]]; then
for loggroup in $LIST_OF_LOGGROUPS_WITHOUT_KMS; do
textFail "$regx: ${loggroup} does not have AWS KMS keys associated." "$regx" "${loggroup}"
done
fi
if [[ $LIST_OF_LOGGROUPS_WITH_KMS ]]; then
for loggroup in $LIST_OF_LOGGROUPS_WITH_KMS; do
textPass "$regx: ${loggroup} does have AWS KMS keys associated." "$regx" "${loggroup}"
done
fi
else
textPass "$regx: No Cloudwatch log groups found." "$regx"
if [[ "${LIST_OF_LOGGROUPS}" != '[]' ]]
then
for LOGGROUP in $(jq -c '.[]' <<< "${LIST_OF_LOGGROUPS}"); do
LOGGROUP_NAME=$(jq -r '.logGroupName' <<< "${LOGGROUP}")
if [[ $(jq '. | select( has("kmsKeyId") == false )' <<< "${LOGGROUP}") ]]
then
textFail "$regx: ${LOGGROUP_NAME} does not have AWS KMS keys associated." "$regx" "${LOGGROUP_NAME}"
else
textPass "$regx: ${LOGGROUP_NAME} does have AWS KMS keys associated." "$regx" "${LOGGROUP_NAME}"
fi
done
else
textPass "$regx: No Cloudwatch log groups found." "$regx" "No log groups"
fi
done
}

View File

@@ -25,11 +25,11 @@ CHECK_DOC_extra7166='https://docs.aws.amazon.com/waf/latest/developerguide/confi
CHECK_CAF_EPIC_extra7166='Infrastructure security'
extra7166() {
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
for regx in $REGIONS; do
for regx in $REGIONS; do
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query 'Addresses[?AssociationId].AllocationId' --output text)
if [[ $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS ]]; then
for elastic_ip in $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS; do
@@ -41,10 +41,10 @@ extra7166() {
fi
done
else
textInfo "$regx: no elastic IP addresses with assocations found" "$regx"
textInfo "$regx: No elastic IP addresses with assocations found" "$regx"
fi
done
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
fi
else
textInfo "$regx: No AWS Shield Advanced subscription found. Skipping check" "$regx"
fi
done
}

View File

@@ -26,7 +26,7 @@ CHECK_CAF_EPIC_extra7167='Infrastructure security'
extra7167() {
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
LIST_OF_CLOUDFRONT_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].[Id,ARN]' --output text)
LIST_OF_CLOUDFRONT_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].[Id,ARN]' --output text | grep -v None)
if [[ $LIST_OF_CLOUDFRONT_DISTRIBUTIONS ]]; then
while read -r distribution; do
DISTRIBUTION_ID=$(echo $distribution | awk '{ print $1; }')
@@ -41,6 +41,6 @@ extra7167() {
textInfo "$REGION: no Cloudfront distributions found" "$REGION"
fi
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
textInfo "$REGION: no AWS Shield Advanced subscription found. Skipping check." "$REGION"
fi
}

View File

@@ -44,6 +44,6 @@ extra7168() {
textInfo "$REGION: no Route53 hosted zones found" "$REGION"
fi
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
textInfo "$REGION: no AWS Shield Advanced subscription found. Skipping check." "$REGION"
fi
}

View File

@@ -41,6 +41,6 @@ extra7169() {
textInfo "$REGION: no global accelerators found" "$REGION"
fi
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
textInfo "$REGION: no AWS Shield Advanced subscription found. Skipping check." "$REGION"
fi
}

View File

@@ -25,8 +25,8 @@ CHECK_DOC_extra7170='https://docs.aws.amazon.com/waf/latest/developerguide/confi
CHECK_CAF_EPIC_extra7170='Infrastructure security'
extra7170() {
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
for regx in $REGIONS; do
for regx in $REGIONS; do
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
LIST_OF_APPLICATION_LOAD_BALANCERS=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application` && Scheme == `internet-facing`].[LoadBalancerName,LoadBalancerArn]' --output text)
if [[ $LIST_OF_APPLICATION_LOAD_BALANCERS ]]; then
while read -r alb; do
@@ -39,10 +39,10 @@ extra7170() {
fi
done <<<"$LIST_OF_APPLICATION_LOAD_BALANCERS"
else
textInfo "$regx: no application load balancers found" "$regx"
textInfo "$regx: No application load balancers found" "$regx"
fi
done
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
fi
else
textInfo "$regx: No AWS Shield Advanced subscription found. Skipping check." "$regx"
fi
done
}

View File

@@ -25,11 +25,11 @@ CHECK_DOC_extra7171='https://docs.aws.amazon.com/waf/latest/developerguide/confi
CHECK_CAF_EPIC_extra7171='Infrastructure security'
extra7171() {
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
for regx in $REGIONS; do
for regx in $REGIONS; do
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
LIST_OF_CLASSIC_LOAD_BALANCERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName]' --output text |grep -v '^None$')
if [[ $LIST_OF_CLASSIC_LOAD_BALANCERS ]]; then
for elb in $LIST_OF_CLASSIC_LOAD_BALANCERS; do
@@ -41,10 +41,10 @@ extra7171() {
fi
done
else
textInfo "$regx: no classic load balancers found" "$regx"
textInfo "$regx: No classic load balancers found" "$regx"
fi
done
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
fi
else
textInfo "$regx: No AWS Shield Advanced subscription found. Skipping check." "$regx"
fi
done
}

66
checks/check_extra7172 Normal file
View File

@@ -0,0 +1,66 @@
#!/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_extra7172="7.172"
CHECK_TITLE_extra7172="[extra7172] Check if S3 buckets have ACLs enabled"
CHECK_SCORED_extra7172="NOT_SCORED"
CHECK_CIS_LEVEL_extra7172="EXTRA"
CHECK_SEVERITY_extra7172="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7172="AwsS3Bucket"
CHECK_ALTERNATE_check7172="extra7172"
CHECK_SERVICENAME_extra7172="s3"
CHECK_RISK_extra7172='S3 ACLs are a legacy access control mechanism that predates IAM. IAM and bucket policies are currently the preferred methods.'
CHECK_REMEDIATION_extra7172='Ensure that S3 ACLs are disabled (BucketOwnerEnforced). Use IAM policies and bucket policies to manage access.'
CHECK_DOC_extra7172='https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html'
CHECK_CAF_EPIC_extra7172='Logging and Monitoring'
extra7172(){
# "Check if S3 buckets have server access logging enabled"
LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --query Buckets[*].Name --region "${REGION}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "$LIST_OF_BUCKETS"; then
textInfo "${REGION}: Access Denied Trying to list buckets" "${REGION}"
exit
fi
if [[ $LIST_OF_BUCKETS ]]; then
for bucket in $LIST_OF_BUCKETS;do
# Recover Bucket region
BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --region "${REGION}" --query LocationConstraint --output text)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_REGION}"; then
textInfo "${REGION}: Access Denied trying to get bucket location for ${bucket}" "${REGION}"
fi
# If None use default region
if [[ "${BUCKET_REGION}" == "None" ]]; then
BUCKET_REGION="${REGION}"
fi
BUCKET_ACLS_DISABLED=$(${AWSCLI} ${PROFILE_OPT} s3api get-bucket-ownership-controls --bucket "${bucket}" --region "${BUCKET_REGION}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_ACLS_DISABLED}" ; then
textInfo "${BUCKET_REGION}: Access Denied Trying to Get Bucket Ownership Controls for ${bucket}" "${BUCKET_REGION}" "${bucket}"
continue
elif grep -q -E 'IllegalLocationConstraintException' <<< "${BUCKET_ACLS_DISABLED}"; then
textInfo "${BUCKET_REGION}: Location Constraint Trying to Get Bucket Ownership Controls for ${bucket}" "${BUCKET_REGION}" "${bucket}"
continue
fi
if grep -q "BucketOwnerEnforced" <<< "${BUCKET_ACLS_DISABLED}"; then
textPass "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs disabled!" "${BUCKET_REGION}" "${bucket}"
elif grep -q "BucketOwnerPreferred" <<< "${BUCKET_ACLS_DISABLED}"; then
textFail "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs enabled!" "${BUCKET_REGION}" "${bucket}"
elif grep -q "OwnershipControlsNotFoundError" <<< "${BUCKET_ACLS_DISABLED}"; then
textFail "${BUCKET_REGION}: Bucket ${bucket} has bucket ACLs enabled!" "${BUCKET_REGION}" "${bucket}"
else
textFail "${BUCKET_REGION}: Bucket ${bucket} returned an unknown error" "${BUCKET_REGION}" "${bucket}"
fi
done
else
textInfo "${REGION}: No S3 Buckets found" "${REGION}"
fi
}

44
checks/check_extra7173 Normal file
View File

@@ -0,0 +1,44 @@
#!/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_extra7173="7.173"
CHECK_TITLE_extra7173="[extra7173] Security Groups created by EC2 Launch Wizard"
CHECK_SCORED_extra7173="NOT_SCORED"
CHECK_CIS_LEVEL_extra7173="EXTRA"
CHECK_SEVERITY_extra7173="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7173="AwsEc2SecurityGroup"
CHECK_ALTERNATE_check7173="extra7173"
CHECK_SERVICENAME_extra7173="ec2"
CHECK_RISK_cextra7173="Security Groups Created on the AWS Console using the EC2 wizard may allow port 22 from 0.0.0.0/0"
CHECK_REMEDIATION_extra7173="Apply Zero Trust approach. Implement a process to scan and remediate security groups created by the EC2 Wizard. Recommended best practices is to use an authorized security group."
CHECK_DOC_extra7173="CHECK_DOC_extra7173='https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html'"
CHECK_CAF_EPIC_extra7173="Infrastructure Security"
extra7173(){
# Ensure no security groups are created using Console EC2 Wizard
for regx in $REGIONS; do
CHECK_SGDEFAULT_IDS=$("${AWSCLI}" ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --filters Name=group-name,Values='launch-wizard-*' --query 'SecurityGroups[*].GroupId[]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation' <<< "${CHECK_SGDEFAULT_IDS}"; then
textInfo "${regx}: Access Denied trying to describe security groups" "${regx}"
continue
fi
if [[ ${CHECK_SGDEFAULT_IDS} ]]; then
for CHECK_SGDEFAULT_ID in ${CHECK_SGDEFAULT_IDS}; do
SECURITY_GROUP_NAME=$(${AWSCLI} ec2 describe-security-groups ${PROFILE_OPT} --region "${regx}" --group-ids "${CHECK_SGDEFAULT_ID}" --query 'SecurityGroups[*].GroupName[]' --output text 2>&1)
textFail "${regx}: Security Group ${SECURITY_GROUP_NAME} (ID: ${CHECK_SGDEFAULT_ID}) was created using the EC2 Launch Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
done
else
textPass "${regx}: No Security Groups found that were created using the Wizard" "${regx}" "${CHECK_SGDEFAULT_ID}"
fi
done
}

63
checks/check_extra7174 Normal file
View File

@@ -0,0 +1,63 @@
#!/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_extra7174="7.174"
CHECK_TITLE_extra7174="[extra7174] CodeBuild Project last invoked greater than 90 days"
CHECK_SCORED_extra7174="NOT_SCORED"
CHECK_CIS_LEVEL_extra7174="EXTRA"
CHECK_SEVERITY_extra7174="High"
CHECK_ASFF_TYPE_extra7174="AwsCodebuildProject"
CHECK_ALTERNATE_check7174="extra7174"
CHECK_SERVICENAME_extra7174="codebuild"
CHECK_RISK_extra7174='Older CodeBuild projects can be checked to see if they are currently in use'
CHECK_REMEDIATION_extra7174='Check if CodeBuild project are really in use and remove the stale ones'
CHECK_DOC_extra7174='https://docs.aws.amazon.com/codebuild/latest/userguide/delete-project.html'
CHECK_CAF_EPIC_extra7174='Infrastructure Security'
extra7174(){
# "Looking for all build projects with last build invocation greater then 90 days in all regions"
for regx in ${REGIONS}; do
LIST_OF_PROJECTS=$("${AWSCLI}" codebuild list-projects ${PROFILE_OPT} --region "${regx}" --query 'projects[*]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_PROJECTS}"; then
textInfo "${regx}: Access Denied trying to list Codebuild projects" "${regx}"
continue
fi
if [[ "${LIST_OF_PROJECTS}" ]]; then
for project in ${LIST_OF_PROJECTS}; do
project_id=$("${AWSCLI}" codebuild list-builds-for-project ${PROFILE_OPT} --project-name "${project}" --query 'ids[0]' --region "${regx}" --output text | head -n 1 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${project_id}"; then
textInfo "${regx}: Access Denied trying to fetch Id for Codebuild project" "${regx}" "${project}"
continue
elif grep -q -E 'None' <<< "${project_id}"; then
textInfo "${regx}: Codebuild project ${project} has been never build" "${regx}" "${project}"
continue
fi
last_invoked_time=$("${AWSCLI}" codebuild batch-get-builds ${PROFILE_OPT} --ids "${project_id}" --region "${regx}" --query 'builds[0].endTime' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${last_invoked_time}"; then
textInfo "${regx}: Access Denied trying to get build details for Codebuild project ID" "${regx}" "${project}"
elif grep -q -E 'None' <<< "${last_invoked_time}"; then
textInfo "${regx}: Codebuild project ${project} has been never build" "${regx}" "${project}"
else
HOWOLDER=$(how_older_from_today "${last_invoked_time}")
if [ "${HOWOLDER}" -ge 90 ]; then
textFail "${regx}: CodeBuild project ${project} was last invoked greater then 90 days" "${regx}" "${project}"
else
textPass "${regx}: Codebuild project ${project} was last invoked in the past 90 days" "${regx}" "${project}"
fi
fi
done
else
textInfo "${regx}: No CodeBuild Projects found" "${regx}"
fi
done
}

52
checks/check_extra7175 Normal file
View File

@@ -0,0 +1,52 @@
#!/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_extra7175="7.175"
CHECK_TITLE_extra7175="[extra7175] CodeBuild Project with an user controlled buildspec"
CHECK_SCORED_extra7175="NOT_SCORED"
CHECK_CIS_LEVEL_extra7175="EXTRA"
CHECK_SEVERITY_extra7175="High"
CHECK_ASFF_TYPE_extra7175="AwsCodebuildProject"
CHECK_ALTERNATE_check7175="extra7175"
CHECK_SERVICENAME_extra7175="codebuild"
CHECK_RISK_extra7175='The CodeBuild projects with user controlled buildspec'
CHECK_REMEDIATION_extra7175='Use buildspec.yml from a trusted source which user cant interfere with'
CHECK_DOC_extra7175='https://docs.aws.amazon.com/codebuild/latest/userguide/security.html'
CHECK_CAF_EPIC_extra7175='Infrastructure Security'
extra7175(){
# "Looking for all build projects with user controlled buildspec files"
for regx in ${REGIONS}; do
LIST_OF_PROJECTS=$("${AWSCLI}" codebuild list-projects ${PROFILE_OPT} --region "${regx}" --query 'projects[*]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_PROJECTS}"; then
textInfo "${regx}: Access Denied trying to list Codebuild projects" "${regx}"
continue
fi
if [[ "${LIST_OF_PROJECTS}" ]]; then
for project in ${LIST_OF_PROJECTS}; do
buildspec_file=$("${AWSCLI}" codebuild batch-get-projects ${PROFILE_OPT} --name "${project}" --query 'projects[0].source.buildspec' --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${buildspec_file}"; then
textInfo "${regx}: Access Denied trying to fetch Id for Codebuild project" "${regx}" "${project}"
continue
fi
if [[ $buildspec_file == *.yml ]];then
textFail "${regx}: Codebuild project ${project} uses a user controlled buildspec" "${regx}" "${project}"
else
textPass "${regx}: Codebuild project ${project} not uses a user controlled buildspec" "${regx}" "${project}"
fi
done
else
textInfo "${regx}: No CodeBuild Projects found" "${regx}"
fi
done
}

55
checks/check_extra7176 Normal file
View File

@@ -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_extra7176="7.176"
CHECK_TITLE_extra7176="[extra7176] EMR Cluster without Public IP"
CHECK_SCORED_extra7176="NOT_SCORED"
CHECK_CIS_LEVEL_extra7176="EXTRA"
CHECK_SEVERITY_extra7176="Medium"
CHECK_ASFF_TYPE_extra7176="AwsEMR"
CHECK_ALTERNATE_check7176="extra7176"
CHECK_SERVICENAME_extra7176="emr"
CHECK_RISK_extra7176='EMR Cluster should not have Public IP'
CHECK_REMEDIATION_extra7176='Only make acceptable EMR clusters public'
CHECK_DOC_extra7176='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7176='Infrastructure Security'
extra7176(){
# Public EMR cluster have their DNS ending with .amazonaws.com while private ones have format of ip-xxx-xx-xx.us-east-1.compute.internal.
for regx in ${REGIONS}; do
# List only EMR clusters with the following states: STARTING, BOOTSTRAPPING, RUNNING, WAITING, TERMINATING
# [NOT TERMINATED AND TERMINATED_WITH_ERRORS]
LIST_OF_CLUSTERS=$("${AWSCLI}" emr list-clusters ${PROFILE_OPT} --region "${regx}" --query 'Clusters[?(Status.State!=`TERMINATED` && Status.State!=`TERMINATED_WITH_ERRORS`)].Id' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_CLUSTERS}"; then
textInfo "${regx}: Access Denied trying to list clusters of emr" "${regx}"
continue
fi
if [[ "${LIST_OF_CLUSTERS}" ]]
then
for cluster_id in ${LIST_OF_CLUSTERS}; do
master_public_dns=$("${AWSCLI}" emr describe-cluster ${PROFILE_OPT} --cluster-id "${cluster_id}" --query 'Cluster.MasterPublicDnsName' --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_public_dns}"; then
textInfo "${regx}: Access Denied trying to describe emr cluster" "${regx}" "${cluster_id}"
continue
fi
if [[ $master_public_dns != None && $master_public_dns != *.internal ]];then
textFail "${regx}: EMR Cluster ${cluster_id} has a Public IP" "${regx}" "${cluster_id}"
else
textPass "${regx}: EMR Cluster ${cluster_id} has not a Public IP" "${regx}" "${cluster_id}"
fi
done
else
textInfo "${regx}: No EMR Clusters found" "${regx}"
fi
done
}

124
checks/check_extra7177 Normal file
View File

@@ -0,0 +1,124 @@
#!/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_extra7177="7.177"
CHECK_TITLE_extra7177="[extra7177] Publicly accessible EMR Cluster"
CHECK_SCORED_extra7177="NOT_SCORED"
CHECK_CIS_LEVEL_extra7177="EXTRA"
CHECK_SEVERITY_extra7177="High"
CHECK_ASFF_TYPE_extra7177="AwsEMR"
CHECK_ALTERNATE_check7177="extra7177"
CHECK_SERVICENAME_extra7177="emr"
CHECK_RISK_extra7177='EMR Clusters should not be publicly accessible'
CHECK_REMEDIATION_extra7177='Only make acceptable EMR clusters public'
CHECK_DOC_extra7177='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7177='Infrastructure Security'
extra7177(){
for regx in ${REGIONS}; do
# List only EMR clusters with the following states: STARTING, BOOTSTRAPPING, RUNNING, WAITING, TERMINATING
# [NOT TERMINATED AND TERMINATED_WITH_ERRORS]
LIST_OF_CLUSTERS=$("${AWSCLI}" emr list-clusters ${PROFILE_OPT} --region "${regx}" --query 'Clusters[?(Status.State!=`TERMINATED` && Status.State!=`TERMINATED_WITH_ERRORS`)].Id' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_CLUSTERS}"; then
textInfo "${regx}: Access Denied trying to list EMR clusters" "${regx}"
continue
fi
if [[ "${LIST_OF_CLUSTERS}" ]]
then
for cluster_id in ${LIST_OF_CLUSTERS}; do
master_public_dns=$("${AWSCLI}" emr describe-cluster ${PROFILE_OPT} --cluster-id "${cluster_id}" --query 'Cluster.MasterPublicDnsName' --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_public_dns}"; then
textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
continue
fi
if [[ $master_public_dns != None && $master_public_dns != *.internal ]];then
# If EMR cluster is Public, it is required to check their Security Groups for the Master, the Slaves and the additional ones
# Retrive EMR Master Node Security Groups rules
master_node_sg=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.EmrManagedMasterSecurityGroup' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_node_sg}"; then
textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
continue
fi
master_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${master_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${master_node_sg_internet_open}"; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
fi
# Retrive EMR Slave Node Security Groups rules
slave_node_sg=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.EmrManagedSlaveSecurityGroup' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
continue
fi
slave_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${slave_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg_internet_open}"; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
fi
# Retrive EMR Additional Master node Security Groups rules
additional_master_node_sg_list=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.AdditionalMasterSecurityGroups' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
continue
fi
local additional_master_node_sg_internet_open_list
if [[ "${additional_master_node_sg_list}" != "None" ]]; then
for additional_master_node_sg in ${additional_master_node_sg_list}; do
additional_master_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${additional_master_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg_internet_open}"; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
fi
# Store additional master node security groups that allows access from the internet
additional_master_node_sg_internet_open_list+=( "${additional_master_node_sg_internet_open}" )
done
fi
# Retrive EMR Additional Slave node Security Groups rules
additional_slave_node_sg_list=$("${AWSCLI}" emr describe-cluster --cluster-id "${cluster_id}" ${PROFILE_OPT} --region "${regx}" --query 'Cluster.Ec2InstanceAttributes.AdditionalSlaveSecurityGroups' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${slave_node_sg}"; then
textInfo "${regx}: Access Denied trying to describe EMR cluster" "${regx}" "${cluster_id}"
continue
fi
local additional_slave_node_sg_internet_open_list
if [[ "${additional_slave_node_sg_list}" != "None" ]]; then
for additional_slave_node_sg in ${additional_master_node_sg_list}; do
additional_slave_node_sg_internet_open=$("${AWSCLI}" ec2 describe-security-groups --group-ids "${additional_slave_node_sg}" --query 'SecurityGroups[?length(IpPermissions[?(contains(IpRanges[].CidrIp, `0.0.0.0/0`) || contains(Ipv6Ranges[].CidrIpv6, `::/0`))]) > `0`].{GroupId:GroupId}' ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${additional_slave_node_sg_internet_open}"; then
textInfo "$regx: Access Denied trying to describe security groups" "$regx"
continue
fi
# Store additional slave node security groups that allows access from the internet
additional_slave_node_sg_internet_open_list+=( "${additional_slave_node_sg_internet_open}" )
done
fi
# Check if EMR Cluster is publicly accessible through a Security Group
if [[ -n "${master_node_sg_internet_open}" || -n "${slave_node_sg_internet_open}" || "${#additional_master_node_sg_internet_open_list[@]}" -ne 0 || "${#additional_slave_node_sg_internet_open_list[@]}" -ne 0 ]]; then
textFail "${regx}: EMR Cluster ${cluster_id} is publicly accessible through the following Security Groups: Master Node ${master_node_sg_internet_open} ${additional_master_node_sg_internet_open_list[*]} -- Slaves Nodes ${slave_node_sg_internet_open} ${additional_slave_node_sg_internet_open_list[*]}" "${regx}" "${cluster_id}"
else
textPass "${regx}: EMR Cluster ${cluster_id} is not publicly accessible" "${regx}" "${cluster_id}"
fi
else
textPass "${regx}: EMR Cluster ${cluster_id} is not publicly accessible" "${regx}" "${cluster_id}"
fi
done
else
textInfo "${regx}: No EMR Clusters found" "${regx}"
fi
done
}

41
checks/check_extra7178 Normal file
View File

@@ -0,0 +1,41 @@
#!/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_extra7178="7.178"
CHECK_TITLE_extra7178="[extra7178] EMR Account Public Access Block enabled"
CHECK_SCORED_extra7178="NOT_SCORED"
CHECK_CIS_LEVEL_extra7178="EXTRA"
CHECK_SEVERITY_extra7178="High"
CHECK_ASFF_TYPE_extra7178="AwsEMR"
CHECK_ALTERNATE_check7178="extra7178"
CHECK_SERVICENAME_extra7178="emr"
CHECK_RISK_extra7178='EMR Clusters must have Account Public Access Block enabled'
CHECK_REMEDIATION_extra7178='Enable EMR Account Public Access Block'
CHECK_DOC_extra7178='https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-block-public-access.html'
CHECK_CAF_EPIC_extra7178='Infrastructure Security'
extra7178(){
for regx in ${REGIONS}; do
block_public_access=$("${AWSCLI}" emr get-block-public-access-configuration ${PROFILE_OPT} --region "${regx}" --query 'BlockPublicAccessConfiguration.BlockPublicSecurityGroupRules' --output json 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${block_public_access}"; then
textInfo "${regx}: Access Denied trying to get block public access configuration for EMR clusters" "${regx}"
continue
fi
if [[ "${block_public_access}" == "true" ]]; then
textPass "${regx}: EMR Account has Block Public Access enabled" "${regx}"
else
textFail "${regx}: EMR Account has Block Public Access disabled" "${regx}"
fi
done
}

68
checks/check_extra7179 Normal file
View File

@@ -0,0 +1,68 @@
#!/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_extra7179="7.179"
CHECK_TITLE_extra7179="[extra7179] Check Public Lambda Function URL"
CHECK_SCORED_extra7179="NOT_SCORED"
CHECK_CIS_LEVEL_extra7179="EXTRA"
CHECK_SEVERITY_extra7179="High"
CHECK_ASFF_RESOURCE_TYPE_extra7179="AwsLambdaFunction"
CHECK_ALTERNATE_check7179="extra7179"
CHECK_SERVICENAME_extra7179="lambda"
CHECK_RISK_extra7179='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra7179='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra7179='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra7179='Infrastructure Security'
extra7179(){
# Check if Lambda function URL is public
# None --> Public
local PUBLIC_AUTH_TYPE="NONE"
# AWS_IAM --> Private
local PRIVATE_AUTH_TYPE="AWS_IAM"
for regx in ${REGIONS}; do
LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \
--region "${regx}" \
--query 'Functions[*].FunctionName' \
--output text 2>&1)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then
textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}"
continue
fi
if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then
for lambda_function in ${LIST_OF_FUNCTIONS}; do
AUTH_TYPE=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \
--function-name "${lambda_function}" \
--region "${regx}" \
--query 'FunctionUrlConfigs[0].AuthType' \
--output text)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}"
continue
fi
if [[ "${AUTH_TYPE}" == "${PUBLIC_AUTH_TYPE}" ]]; then
textFail "${regx}: Lambda function ${lambda_function} has a publicly accessible function URL" "${regx}" "${lambda_function}"
elif [[ "${AUTH_TYPE}" == "${PRIVATE_AUTH_TYPE}" ]]; then
textPass "${regx}: Lambda function ${lambda_function} has not a publicly accessible function URL" "${regx}" "${lambda_function}"
else
textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}"
fi
done
else
textInfo "${regx}: No Lambda functions found" "${regx}"
fi
done
}

View File

@@ -25,21 +25,32 @@ CHECK_CAF_EPIC_extra718='Logging and Monitoring'
extra718(){
# "Check if S3 buckets have server access logging enabled "
LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --query Buckets[*].Name --output text|xargs -n1)
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_SERVER_LOG_ENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --query [LoggingEnabled] --output text 2>&1)
if [[ $(echo "$BUCKET_SERVER_LOG_ENABLED" | grep AccessDenied) ]]; then
textInfo "$REGION: Access Denied Trying to Get Bucket Logging for $bucket" "$REGION" "$bucket"
# Recover Bucket region
BUCKET_REGION=$("${AWSCLI}" ${PROFILE_OPT} s3api get-bucket-location --bucket "${bucket}" --region "${REGION}" --query LocationConstraint --output text)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${BUCKET_REGION}"; then
textInfo "${REGION}: Access Denied trying to get bucket location for ${bucket}" "${REGION}"
continue
fi
# If None use default region
if [[ "${BUCKET_REGION}" == "None" ]]; then
BUCKET_REGION="${REGION}"
fi
BUCKET_SERVER_LOG_ENABLED=$("${AWSCLI}" s3api get-bucket-logging --bucket "${bucket}" ${PROFILE_OPT} --region "${BUCKET_REGION}" --query [LoggingEnabled] --output text 2>&1)
if grep -q AccessDenied <<< "${BUCKET_SERVER_LOG_ENABLED}"; then
textInfo "${BUCKET_REGION}: Access Denied Trying to Get Bucket Logging for ${bucket}" "${BUCKET_REGION}" "${bucket}"
continue
fi
if [[ $(echo "$BUCKET_SERVER_LOG_ENABLED" | grep "^None$") ]]; then
textFail "$REGION: Bucket $bucket has server access logging disabled!" "$REGION" "$bucket"
if grep -q "^None$" <<< "${BUCKET_SERVER_LOG_ENABLED}"; then
textFail "${BUCKET_REGION}: Bucket ${bucket} has server access logging disabled!" "${BUCKET_REGION}" "${bucket}"
else
textPass "$REGION: Bucket $bucket has server access logging enabled" "$REGION" "$bucket"
textPass "${BUCKET_REGION}: Bucket ${bucket} has server access logging enabled" "${BUCKET_REGION}" "${bucket}"
fi
done
else
textInfo "$REGION: No S3 Buckets found" "$REGION" "$bucket"
textInfo "${REGION}: No S3 Buckets found" "${REGION}" "${bucket}"
fi
}

81
checks/check_extra7180 Normal file
View File

@@ -0,0 +1,81 @@
#!/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_extra7180="7.180"
CHECK_TITLE_extra7180="[extra7180] Check Lambda Function URL CORS configuration"
CHECK_SCORED_extra7180="NOT_SCORED"
CHECK_CIS_LEVEL_extra7180="EXTRA"
CHECK_SEVERITY_extra7180="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7180="AwsLambdaFunction"
CHECK_ALTERNATE_check7180="extra7180"
CHECK_SERVICENAME_extra7180="lambda"
CHECK_RISK_extra7180='Publicly accessible services could expose sensitive data to bad actors.'
CHECK_REMEDIATION_extra7180='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra7180='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html'
CHECK_CAF_EPIC_extra7180='Infrastructure Security'
extra7180(){
for regx in ${REGIONS}; do
LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \
--region "${regx}" \
--query 'Functions[*].FunctionName' \
--output text 2>&1)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then
textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}"
continue
fi
if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then
for lambda_function in ${LIST_OF_FUNCTIONS}; do
# Check if Lambda function has an URL
LAMBDA_FUNCTION_URL=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \
--function-name "${lambda_function}" \
--region "${regx}" \
--query 'FunctionUrlConfigs[0].[FunctionUrl]' \
--output text)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
textInfo "${regx}: Access Denied trying to get Lambda functions URLs" "${regx}"
continue
fi
if [[ "${LAMBDA_FUNCTION_URL}" != "None" ]]; then
# Check CORS configuration
CORS_ALLOW_ORIGINS=$("${AWSCLI}" lambda get-function-url-config ${PROFILE_OPT} \
--function-name "${lambda_function}" \
--region "${regx}" \
--query 'Cors.AllowOrigins' \
--output text)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then
textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}"
continue
fi
# The * is on purpose to check allowed origins
if [[ "${CORS_ALLOW_ORIGINS}" =~ "*" ]]; then
textFail "$regx: Lambda function ${lambda_function} URL has a wide CORS configuration" "${regx}" "${lambda_function}"
elif [[ "${CORS_ALLOW_ORIGINS}" == "None" ]]; then
textFail "${regx}: Lambda function ${lambda_function} URL has not CORS configured" "${regx}" "${lambda_function}"
else
textPass "${regx}: Lambda function ${lambda_function} has not a wide CORS configuration" "${regx}" "${lambda_function}"
fi
else
textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}"
fi
done
else
textInfo "${regx}: No Lambda functions found" "${regx}"
fi
done
}

52
checks/check_extra7181 Normal file
View File

@@ -0,0 +1,52 @@
#!/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_extra7181="7.181"
CHECK_TITLE_extra7181="[extra7181] Directory Service monitoring with CloudWatch logs"
CHECK_SCORED_extra7181="NOT_SCORED"
CHECK_CIS_LEVEL_extra7181="EXTRA"
CHECK_SEVERITY_extra7181="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7181="AwsDirectoryService"
CHECK_ALTERNATE_extra7181="extra7181"
CHECK_SERVICENAME_extra7181="ds"
CHECK_RISK_cextra7181="As a best practice, monitor your organization to ensure that changes are logged. This helps you to ensure that any unexpected change can be investigated and unwanted changes can be rolled back."
CHECK_REMEDIATION_extra7181="It is recommended that that the export of logs is enabled"
CHECK_DOC_extra7181="CHECK_DOC_extra7181='https://docs.aws.amazon.com/directoryservice/latest/admin-guide/incident-response.html'"
CHECK_CAF_EPIC_extra7181="Infrastructure Security"
extra7181(){
for regx in $REGIONS; do
DIRECTORY_SERVICE_IDS=$("${AWSCLI}" ds describe-directories $PROFILE_OPT --region "${regx}" --query 'DirectoryDescriptions[*].DirectoryId[]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_IDS}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ ${DIRECTORY_SERVICE_IDS} ]]; then
for DIRECTORY_ID in ${DIRECTORY_SERVICE_IDS}; do
DIRECTORY_SERVICE_MONITORING=$("${AWSCLI}" ds list-log-subscriptions ${PROFILE_OPT} --region "${regx}" --directory-id "${DIRECTORY_ID}" --query 'LogSubscriptions' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_MONITORING}"; then
textInfo "${regx}: Access Denied trying to list Directory Service log subscriptions" "${regx}"
continue
fi
if [[ "${DIRECTORY_SERVICE_MONITORING}" ]]; then
textPass "${regx}: Directory Service ${DIRECTORY_ID} have log forwarding to CloudWatch enabled" "${regx}" "${DIRECTORY_ID}"
else
textFail "${regx}: Directory Service ${DIRECTORY_ID} does not have log forwarding to CloudWatch enabled" "${regx}" "${DIRECTORY_ID}"
fi
done
else
textInfo "${regx}: No Directory Service found" "${regx}"
fi
done
}

52
checks/check_extra7182 Normal file
View File

@@ -0,0 +1,52 @@
#!/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_extra7182="7.182"
CHECK_TITLE_extra7182="[extra7182] Directory Service SNS Notifications"
CHECK_SCORED_extra7182="NOT_SCORED"
CHECK_CIS_LEVEL_extra7182="EXTRA"
CHECK_SEVERITY_extra7182="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7182="AwsDirectoryService"
CHECK_ALTERNATE_check7182="extra7182"
CHECK_SERVICENAME_extra7182="ds"
CHECK_RISK_cextra7182="As a best practice, monitor status of Directory Service. This helps to avoid late actions to fix Directory Service issues"
CHECK_REMEDIATION_extra7182="It is recommended set up SNS messaging to send email or text messages when the status of your directory changes"
CHECK_DOC_extra7182="https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_enable_notifications.html"
CHECK_CAF_EPIC_extra7182="Infrastructure Security"
extra7182(){
for regx in $REGIONS; do
DIRECTORY_SERVICE_IDS=$("${AWSCLI}" ds describe-directories ${PROFILE_OPT} --region "${regx}" --query 'DirectoryDescriptions[*].DirectoryId[]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_IDS}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ ${DIRECTORY_SERVICE_IDS} ]]; then
for DIRECTORY_ID in ${DIRECTORY_SERVICE_IDS}; do
DIRECTORY_SERVICE_MONITORING=$("${AWSCLI}" ds describe-event-topics ${PROFILE_OPT} --region "${regx}" --directory-id "${DIRECTORY_ID}" --query 'EventTopics' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_MONITORING}"; then
textInfo "${regx}: Access Denied trying to describe Directory Service event topics" "${regx}"
continue
fi
if [[ "${DIRECTORY_SERVICE_MONITORING}" ]]; then
textPass "${regx}: Directory Service ${DIRECTORY_ID} have SNS messaging enabled" "${regx}" "${DIRECTORY_ID}"
else
textFail "${regx}: Directory Service ${DIRECTORY_ID} does not have SNS messaging enabled" "${regx}" "${DIRECTORY_ID}"
fi
done
else
textInfo "${regx}: No Directory Service found" "${regx}"
fi
done
}

71
checks/check_extra7183 Normal file
View File

@@ -0,0 +1,71 @@
#!/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_extra7183="7.183"
CHECK_TITLE_extra7183="[extra7183] Directory Service LDAP Certificates expiration"
CHECK_SCORED_extra7183="NOT_SCORED"
CHECK_CIS_LEVEL_extra7183="EXTRA"
CHECK_SEVERITY_extra7183="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7183="AwsDirectoryService"
CHECK_ALTERNATE_check7183="extra7183"
CHECK_SERVICENAME_extra7183="ds"
CHECK_RISK_cextra7183="Expired certificates can impact service availability."
CHECK_REMEDIATION_extra7183="Monitor certificate expiration and take automated action to alarm responsible team for taking care of the replacement or remove."
CHECK_DOC_extra7183="https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_ldap.html"
CHECK_CAF_EPIC_extra7183="Data Protection"
extra7183(){
local DAYS_TO_EXPIRE_THRESHOLD=90
for regx in $REGIONS; do
DIRECTORY_SERVICE_IDS=$("${AWSCLI}" ds describe-directories ${PROFILE_OPT} --region "${regx}" --query 'DirectoryDescriptions[*].DirectoryId[]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_IDS}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ ${DIRECTORY_SERVICE_IDS} ]]; then
for DIRECTORY_ID in ${DIRECTORY_SERVICE_IDS}; do
CERT_DATA=$("${AWSCLI}" ds list-certificates ${PROFILE_OPT} --region "${regx}" --directory-id "${DIRECTORY_ID}" --query 'CertificatesInfo[*].[CertificateId,ExpiryDateTime]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${CERT_DATA}"; then
textInfo "${regx}: Access Denied trying to list certificates" "${regx}"
continue
fi
if grep -q -E 'UnsupportedOperationException' <<< "${CERT_DATA}"; then
textInfo "${regx}: Error calling the ListCertificates operation: LDAPS operations are not supported for this Directory Type (directory id: ${DIRECTORY_ID})" "${regx}"
continue
fi
if [[ ${CERT_DATA} ]]; then
echo "${CERT_DATA}" | while read -r CERTIFICATE_ID NOTAFTER; do
EXPIRES_DATE=$(timestamp_to_date "${NOTAFTER}")
if [[ ${EXPIRES_DATE} == "" ]]
then
textInfo "${regx}: LDAP Certificate ${CERTIFICATE_ID} has an incorrect timestamp format: ${NOTAFTER}" "${regx}" "${CERTIFICATE_ID}"
else
COUNTER_DAYS=$(how_many_days_from_today "${EXPIRES_DATE}")
if [[ "${COUNTER_DAYS}" -le "${DAYS_TO_EXPIRE_THRESHOLD}" ]]; then
textFail "${regx}: LDAP Certificate ${CERTIFICATE_ID} configured at ${DIRECTORY_ID} is about to expire in ${COUNTER_DAYS} days!" "${regx}" "${CERTIFICATE_ID}"
else
textPass "${regx}: LDAP Certificate ${CERTIFICATE_ID} configured at ${DIRECTORY_ID} expires in ${COUNTER_DAYS} days" "${regx}" "${CERTIFICATE_ID}"
fi
fi
done
else
textFail "${regx}: Directory Service ${DIRECTORY_ID} does not have a LDAP Certificate configured" "${regx}" "${DIRECTORY_ID}"
fi
done
else
textInfo "${regx}: No Directory Service found" "${regx}"
fi
done
}

67
checks/check_extra7184 Normal file
View File

@@ -0,0 +1,67 @@
#!/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_extra7184="7.184"
CHECK_TITLE_extra7184="[extra7184] Directory Service Manual Snapshot Limit"
CHECK_SCORED_extra7184="NOT_SCORED"
CHECK_CIS_LEVEL_extra7184="EXTRA"
CHECK_SEVERITY_extra7184="Low"
CHECK_ASFF_RESOURCE_TYPE_extra7184="AwsDirectoryService"
CHECK_ALTERNATE_check7184="extra7184"
CHECK_SERVICENAME_extra7184="ds"
CHECK_RISK_extra7184="A limit reached can bring unwanted results. The maximum number of manual snapshots is a hard limit"
CHECK_REMEDIATION_extra7184="Monitor manual snapshots limit to ensure capacity when you need it."
CHECK_DOC_extra7184="https://docs.aws.amazon.com/general/latest/gr/ds_region.html"
CHECK_CAF_EPIC_extra7184="Infrastructure Security"
extra7184(){
local THRESHOLD="2"
for regx in ${REGIONS}; do
DIRECTORY_SERVICE_IDS=$("${AWSCLI}" ds describe-directories ${PROFILE_OPT} --region "${regx}" --query 'DirectoryDescriptions[*].DirectoryId[]' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${DIRECTORY_SERVICE_IDS}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ ${DIRECTORY_SERVICE_IDS} ]]; then
for DIRECTORY_ID in ${DIRECTORY_SERVICE_IDS}; do
LIMIT_DATA=$("${AWSCLI}" ds get-snapshot-limits ${PROFILE_OPT} --region "${regx}" --directory-id "${DIRECTORY_ID}" --query 'SnapshotLimits' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIMIT_DATA}"; then
textInfo "${regx}: Access Denied trying to get Directiory Service snapshot limits" "${regx}"
continue
fi
if grep -q -E 'ClientException' <<< "${LIMIT_DATA}"; then
textInfo "${regx}: Error calling the GetSnapshotLimits operation: Snapshot limits can be fetched only for VPC or Microsoft AD directories (directory id: ${DIRECTORY_ID})" "${regx}"
continue
fi
echo "${LIMIT_DATA}" | while read -r CURRENT_SNAPSHOTS_COUNT SNAPSHOTS_LIMIT SNAPSHOTS_LIMIT_REACHED; do
if [[ ${SNAPSHOTS_LIMIT_REACHED} == "true" ]]
then
textFail "${regx}: Directory Service ${DIRECTORY_ID} reached ${SNAPSHOTS_LIMIT} Snapshots Limit" "${regx}" "${DIRECTORY_ID}"
else
LIMIT_REMAIN=$(("${SNAPSHOTS_LIMIT}" - "${CURRENT_SNAPSHOTS_COUNT}"))
if [[ "${LIMIT_REMAIN}" -le "${THRESHOLD}" ]]; then
textFail "${regx}: Directory Service ${DIRECTORY_ID} is about to reach ${SNAPSHOTS_LIMIT} snapshots which is the limit" "${regx}" "${DIRECTORY_ID}"
else
textPass "${regx}: Directory Service ${DIRECTORY_ID} is using ${CURRENT_SNAPSHOTS_COUNT} out of ${SNAPSHOTS_LIMIT} from the Snapshot Limit" "${regx}" "{$DIRECTORY_ID}"
fi
fi
done
done
else
textInfo "${regx}: No Directory Service found" "${regx}"
fi
done
}

85
checks/check_extra7185 Normal file
View File

@@ -0,0 +1,85 @@
#!/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_extra7185="7.185"
CHECK_TITLE_extra7185="[extra7185] Ensure no Customer Managed IAM policies allow actions that may lead into Privilege Escalation"
CHECK_SCORED_extra7185="NOT_SCORED"
CHECK_CIS_LEVEL_extra7185="EXTRA"
CHECK_SEVERITY_extra7185="High"
CHECK_ASFF_RESOURCE_TYPE_extra7185="AwsIamPolicy"
CHECK_ALTERNATE_check7185="extra7185"
CHECK_SERVICENAME_extra7185="iam"
CHECK_RISK_extra7185='Users with some IAM permissions are allowed to elevate their privileges up to administrator rights.'
CHECK_REMEDIATION_extra7185='Grant usage permission on a per-resource basis and applying least privilege principle.'
CHECK_DOC_extra7185='https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateAccessKey.html'
CHECK_CAF_EPIC_extra7185='IAM'
# Does the tool analyze both users and roles, or just one or the other? --> Everything using AttachementCount.
# Does the tool take a principal-centric or policy-centric approach? --> Policy-centric approach.
# Does the tool handle resource constraints? --> We don't check if the policy affects all resources or not, we check everything.
# Does the tool consider the permissions of service roles? --> Just checks policies.
# Does the tool handle transitive privesc paths (i.e., attack chains)? --> Not yet.
# Does the tool handle the DENY effect as expected? --> Yes, it checks DENY's statements with Action and NotAction.
# Does the tool handle NotAction as expected? --> Yes
# Does the tool handle Condition constraints? --> Not yet.
# Does the tool handle service control policy (SCP) restrictions? --> No, SCP are within Organizations AWS API.
extra7185() {
local PRIVILEGE_ESCALATION_IAM_ACTIONS="iam:AttachGroupPolicy|iam:SetDefaultPolicyVersion2|iam:AddUserToGroup|iam:AttachRolePolicy|iam:AttachUserPolicy|iam:CreateAccessKey|iam:CreatePolicyVersion|iam:CreateLoginProfile|iam:PassRole|iam:PutGroupPolicy|iam:PutRolePolicy|iam:PutUserPolicy|iam:SetDefaultPolicyVersion|iam:UpdateAssumeRolePolicy|iam:UpdateLoginProfile|sts:AssumeRole|ec2:RunInstances|lambda:CreateEventSourceMapping|lambda:CreateFunction|lambda:InvokeFunction|lambda:UpdateFunctionCode|dynamodb:CreateTable|dynamodb:PutItem|glue:CreateDevEndpoint|glue:GetDevEndpoint|glue:GetDevEndpoints|glue:UpdateDevEndpoint|cloudformation:CreateStack|cloudformation:DescribeStacks|datapipeline:CreatePipeline|datapipeline:PutPipelineDefinition|datapipeline:ActivatePipeline"
# Use --scope Local to list only Customer Managed Policies
# Query 'Policies[?AttachmentCount > `0`]' to check if this policy is in use, so attached to any user, group or role
LIST_CUSTOM_POLICIES=$(${AWSCLI} iam list-policies ${PROFILE_OPT} \
--scope Local \
--query 'Policies[*].[Arn,DefaultVersionId]' \
--output text)
# Check errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_CUSTOM_POLICIES}"; then
textInfo "${REGION}: Access Denied trying to list IAM policies" "${REGION}"
else
if [[ $LIST_CUSTOM_POLICIES ]]; then
while read -r POLICY_ARN POLICY_DEFAULT_VERSION; do
POLICY_PRIVILEGED_ACTIONS=$($AWSCLI iam get-policy-version ${PROFILE_OPT} \
--policy-arn "${POLICY_ARN}" \
--version-id "${POLICY_DEFAULT_VERSION}" \
--query "PolicyVersion.Document.Statement[]" \
--output json)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${POLICY_PRIVILEGED_ACTIONS}"; then
textInfo "${REGION}: Access Denied trying to get policy version" "${REGION}"
continue
fi
ALLOWED_ACTIONS=$(jq -r '.[] | select(."Effect" == "Allow") | .Action // empty' <<< "${POLICY_PRIVILEGED_ACTIONS}" | sed 's/\[//;s/\]//;s/,/ /;s/ //g;/^$/d')
DENIED_ACTIONS=$(jq -r '.[] | select(."Effect" == "Deny") | .Action // empty' <<< "${POLICY_PRIVILEGED_ACTIONS}" | sed 's/\[//;s/\]//;s/,/ /;s/ //g;/^$/d')
DENIED_NOT_ACTIONS=$(jq -r '.[] | select(."Effect" == "Deny") | .NotAction // empty' <<< "${POLICY_PRIVILEGED_ACTIONS}" | sed 's/\[//;s/\]//;s/,/ /;s/ //g;/^$/d')
# First, we need to perform a left join with ALLOWED_ACTIONS and DENIED_ACTIONS
LEFT_ACTIONS=$(diff <(echo "${ALLOWED_ACTIONS}") <(echo "${DENIED_ACTIONS}") | grep "^<" | sed 's/< //;s/"//g')
# Then, we need to find the DENIED_NOT_ACTIONS in LEFT_ACTIONS
PRIVILEGED_ACTIONS=$(comm -1 -2 <(sort <<< "${DENIED_NOT_ACTIONS}") <(sort <<< "${LEFT_ACTIONS}"))
# Finally, check if there is a privilege escalation action within this policy
POLICY_PRIVILEGE_ESCALATION_ACTIONS=$(grep -o -E "${PRIVILEGE_ESCALATION_IAM_ACTIONS}" <<< "${PRIVILEGED_ACTIONS}")
if [[ -n "${POLICY_PRIVILEGE_ESCALATION_ACTIONS}" ]]; then
textFail "${REGION}: Customer Managed IAM Policy ${POLICY_ARN} allows for privilege escalation using the following actions: ${POLICY_PRIVILEGE_ESCALATION_ACTIONS//$'\n'/ }" "${REGION}" "${POLICY_NAME}"
else
textPass "${REGION}: Customer Managed IAM Policy ${POLICY_ARN} not allows for privilege escalation" "${REGION}" "${POLICY_NAME}"
fi
done<<<"${LIST_CUSTOM_POLICIES}"
else
textInfo "${REGION}: No Customer Managed IAM policies found" "${REGION}"
fi
fi
}

42
checks/check_extra7186 Normal file
View File

@@ -0,0 +1,42 @@
#!/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_extra7186="7.186"
CHECK_TITLE_extra7186="[extra7186] Check S3 Account Level Public Access Block"
CHECK_SCORED_extra7186="NOT_SCORED"
CHECK_CIS_LEVEL_extra7186="EXTRA"
CHECK_SEVERITY_extra7186="High"
CHECK_ASFF_RESOURCE_TYPE_extra7186="AwsS3Bucket"
CHECK_ALTERNATE_check7186="extra7186"
CHECK_SERVICENAME_extra7186="s3"
CHECK_RISK_extra7186='Public access policies may be applied to sensitive data buckets'
CHECK_REMEDIATION_extra7186='You can enable Public Access Block at the account level to prevent the exposure of your data stored in S3'
CHECK_DOC_extra7186='https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html'
CHECK_CAF_EPIC_extra7186='Data Protection'
extra7186(){
S3_PUBLIC_ACCESS_BLOCK=$("${AWSCLI}" ${PROFILE_OPT} s3control get-public-access-block \
--account-id "${ACCOUNT_NUM}" \
--region "${REGION}" \
--query 'PublicAccessBlockConfiguration.[IgnorePublicAcls,RestrictPublicBuckets]' \
--output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${S3_PUBLIC_ACCESS_BLOCK}"; then
textInfo "${REGION}: Access Denied trying to recover AWS account ID" "${REGION}"
exit
fi
if grep -q -E 'False|NoSuchPublicAccessBlockConfiguration' <<< "${S3_PUBLIC_ACCESS_BLOCK}"; then
textFail "${REGION}: Block Public Access is not configured for the account ${ACCOUNT_NUM}" "${REGION}" "${ACCOUNT_NUM}"
else
textPass "${REGION}: Block Public Access is configured for the account ${ACCOUNT_NUM}" "${REGION}" "${ACCOUNT_NUM}"
fi
}

53
checks/check_extra7187 Normal file
View File

@@ -0,0 +1,53 @@
#!/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_extra7187="7.187"
CHECK_TITLE_extra7187="[extra7187] Ensure that your Amazon WorkSpaces storage volumes are encrypted in order to meet security and compliance requirements"
CHECK_SCORED_extra7187="NOT_SCORED"
CHECK_CIS_LEVEL_extra7187="EXTRA"
CHECK_SEVERITY_extra7187="High"
CHECK_ASFF_RESOURCE_TYPE_extra7187="AwsWorkspaces"
CHECK_ALTERNATE_check7187="extra7187"
CHECK_SERVICENAME_extra7187="workspaces"
CHECK_RISK_extra7187='If the value listed in the Volume Encryption column is Disabled the selected AWS WorkSpaces instance volumes (root and user volumes) are not encrypted. Therefore your data-at-rest is not protected from unauthorized access and does not meet the compliance requirements regarding data encryption.'
CHECK_REMEDIATION_extra7187='WorkSpaces is integrated with the AWS Key Management Service (AWS KMS). This enables you to encrypt storage volumes of WorkSpaces using AWS KMS Key. When you launch a WorkSpace you can encrypt the root volume (for Microsoft Windows - the C drive; for Linux - /) and the user volume (for Windows - the D drive; for Linux - /home). Doing so ensures that the data stored at rest - disk I/O to the volume - and snapshots created from the volumes are all encrypted'
CHECK_DOC_extra7187='https://docs.aws.amazon.com/workspaces/latest/adminguide/encrypt-workspaces.html'
CHECK_CAF_EPIC_extra7187='Infrastructure Security'
extra7187(){
for regx in $REGIONS; do
RT_VOL_UNENCRYPTED_WORKSPACES_ID_LIST=$($AWSCLI workspaces describe-workspaces --query "Workspaces[?RootVolumeEncryptionEnabled!=\`true\`].WorkspaceId" ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|Could not connect to the endpoint URL|AuthorizationError' <<< "$RT_VOL_UNENCRYPTED_WORKSPACES_ID_LIST"; then
textInfo "$regx: Access Denied trying to describe workspaces" "$regx"
continue
fi
USERVOL_UNENCRYPTED_WORKSPACES_ID_LIST=$($AWSCLI workspaces describe-workspaces --query "Workspaces[?UserVolumeEncryptionEnabled!=\`true\`].WorkspaceId" ${PROFILE_OPT} --region "${regx}" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|Could not connect to the endpoint URL|AuthorizationError' <<< "$USERVOL_UNENCRYPTED_WORKSPACES_ID_LIST"; then
textInfo "$regx: Access Denied trying to describe workspaces" "$regx"
continue
fi
if [[ $RT_VOL_UNENCRYPTED_WORKSPACES_ID_LIST ]];then
for RTVL in $RT_VOL_UNENCRYPTED_WORKSPACES_ID_LIST;do
textFail "$regx: Found WorkSpaces: $RTVL with root volume unencrypted" "$regx" "$RTVL"
done
else
textPass "$regx: No Workspaces with unencrypted root volume found" "$regx" "$RTVL"
fi
if [[ $USERVOL_UNENCRYPTED_WORKSPACES_ID_LIST ]];then
for UVL in $USERVOL_UNENCRYPTED_WORKSPACES_ID_LIST;do
textFail "$regx: Found WorkSpaces: $UVL with user volume unencrypted" "$regx" "$UVL"
done
else
textPass "$regx: No Workspaces with unencrypted user volume found" "$regx" "$UVL"
fi
done
}

60
checks/check_extra7188 Normal file
View File

@@ -0,0 +1,60 @@
#!/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_extra7188="7.188"
CHECK_TITLE_extra7188="[extra7188] Ensure Radius server in DS is using the recommended security protocol"
CHECK_SCORED_extra7188="NOT_SCORED"
CHECK_CIS_LEVEL_extra7188="EXTRA"
CHECK_SEVERITY_extra7188="Medium"
CHECK_ASFF_TYPE_extra7188="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7188="AwsDirectoryService"
CHECK_ALTERNATE_check7188="extra7188"
CHECK_SERVICENAME_extra7188="ds"
CHECK_RISK_extra7188="As a best practice, you might need to configure the authentication protocol between the Microsoft AD DCs and the RADIUS/MFA server. Supported protocols are PAP, CHAP MS-CHAPv1, and MS-CHAPv2. MS-CHAPv2 is recommended because it provides the strongest security of the three options."
CHECK_REMEDIATION_extra7188="MS-CHAPv2 provides the strongest security of the options supported, and is therefore recommended"
CHECK_DOC_extra7188='https://aws.amazon.com/blogs/security/how-to-enable-multi-factor-authentication-for-amazon-workspaces-and-amazon-quicksight-by-using-microsoft-ad-and-on-premises-credentials/'
CHECK_CAF_EPIC_extra7188="Infrastructure Security"
extra7188(){
for regx in $REGIONS; do
LIST_OF_DIRECTORIES=$("${AWSCLI}" ds describe-directories $PROFILE_OPT --region "${regx}" --query 'DirectoryDescriptions[*]' --output json 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL' <<< "${LIST_OF_DIRECTORIES}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ $LIST_OF_DIRECTORIES && $LIST_OF_DIRECTORIES != '[]' ]]; then
LIST_OF_DIRECTORIES_WITHOUT_RADIUS=$(echo "${LIST_OF_DIRECTORIES}" | jq '.[] | select(.RadiusSettings == null) | {DirectoryId}' | jq -r '.DirectoryId')
LIST_OF_DIRECTORIES_WITH_RADIUS=$(echo "${LIST_OF_DIRECTORIES}" | jq '.[] | select(.RadiusSettings)')
LIST_OF_DIRECTORIES_WITH_RADIUS_RECOMMENDED_SECURITY_PROTOCOL=$(echo "${LIST_OF_DIRECTORIES_WITH_RADIUS}" | jq 'select(.RadiusSettings.AuthenticationProtocol=="MS-CHAPv2") | {DirectoryId}' | jq -r '.DirectoryId')
LIST_OF_DIRECTORIES_WITHOUT_RADIUS_RECOMMENDED_SECURITY_PROTOCOL=$(echo "${LIST_OF_DIRECTORIES_WITH_RADIUS}" | jq 'select(.RadiusSettings.AuthenticationProtocol!="MS-CHAPv2") | {DirectoryId}' | jq -r '.DirectoryId')
if [[ $LIST_OF_DIRECTORIES_WITHOUT_RADIUS_RECOMMENDED_SECURITY_PROTOCOL ]]; then
for directory in $LIST_OF_DIRECTORIES_WITHOUT_RADIUS_RECOMMENDED_SECURITY_PROTOCOL; do
textFail "$regx: Radius server of directory: ${directory} does not have recommended security protocol" "$regx" "${directory}"
done
fi
if [[ $LIST_OF_DIRECTORIES_WITH_RADIUS_RECOMMENDED_SECURITY_PROTOCOL ]]; then
for directory in $LIST_OF_DIRECTORIES_WITH_RADIUS_RECOMMENDED_SECURITY_PROTOCOL; do
textPass "$regx: Radius server of directory: ${directory} has recommended security protocol" "$regx" "${directory}"
done
fi
if [[ $LIST_OF_DIRECTORIES_WITHOUT_RADIUS ]]; then
for directory in $LIST_OF_DIRECTORIES_WITHOUT_RADIUS; do
textPass "${regx}: Directory ${directory} has not a Radius server" "${regx}" "${directory}"
done
fi
else
textPass "${regx}: No Directory Service directories found" "${regx}"
fi
done
}

60
checks/check_extra7189 Normal file
View File

@@ -0,0 +1,60 @@
#!/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_extra7189="7.189"
CHECK_TITLE_extra7189="[extra7189] Ensure Multi-Factor Authentication (MFA) using Radius Server is enabled in DS"
CHECK_SCORED_extra7189="NOT_SCORED"
CHECK_CIS_LEVEL_extra7189="EXTRA"
CHECK_SEVERITY_extra7189="Medium"
CHECK_ASFF_TYPE_extra7189="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7189="AwsDirectoryService"
CHECK_ALTERNATE_check7189="extra7189"
CHECK_SERVICENAME_extra7189="ds"
CHECK_RISK_extra7189="Multi-Factor Authentication (MFA) adds an extra layer of authentication assurance beyond traditional username and password."
CHECK_REMEDIATION_extra7189="Enabling MFA provides increased security to a user name and password as it requires the user to possess a solution that displays a time-sensitive authentication code."
CHECK_DOC_extra7189='https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_mfa.html'
CHECK_CAF_EPIC_extra7189="Infrastructure Security"
extra7189(){
for regx in $REGIONS; do
LIST_OF_DIRECTORIES=$("${AWSCLI}" ds describe-directories $PROFILE_OPT --region "${regx}" --query 'DirectoryDescriptions[*]' --output json 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL' <<< "${LIST_OF_DIRECTORIES}"; then
textInfo "${regx}: Access Denied trying to describe directories" "${regx}"
continue
fi
if [[ $LIST_OF_DIRECTORIES && $LIST_OF_DIRECTORIES != '[]' ]]; then
LIST_OF_DIRECTORIES_WITHOUT_RADIUS=$(echo "${LIST_OF_DIRECTORIES}" | jq '.[] | select(.RadiusSettings == null) | {DirectoryId}' | jq -r '.DirectoryId')
LIST_OF_DIRECTORIES_WITH_RADIUS=$(echo "${LIST_OF_DIRECTORIES}" | jq '.[] | select(.RadiusSettings)')
LIST_OF_DIRECTORIES_WITH_RADIUS_MFA_COMPLETED=$(echo "${LIST_OF_DIRECTORIES_WITH_RADIUS}" | jq 'select(.RadiusStatus=="Completed") | {DirectoryId}' | jq -r '.DirectoryId')
LIST_OF_DIRECTORIES_WITHOUT_RADIUS_MFA_COMPLETED=$(echo "${LIST_OF_DIRECTORIES_WITH_RADIUS}" | jq 'select(.RadiusStatus!="Completed") | {DirectoryId}' | jq -r '.DirectoryId')
if [[ $LIST_OF_DIRECTORIES_WITHOUT_RADIUS_MFA_COMPLETED ]]; then
for directory in $LIST_OF_DIRECTORIES_WITHOUT_RADIUS_MFA_COMPLETED; do
textFail "$regx: Directory: ${directory} does not have Radius MFA enabled successfully" "$regx" "${directory}"
done
fi
if [[ $LIST_OF_DIRECTORIES_WITH_RADIUS_MFA_COMPLETED ]]; then
for directory in $LIST_OF_DIRECTORIES_WITH_RADIUS_MFA_COMPLETED; do
textPass "$regx: Directory: ${directory} has Radius MFA enabled" "$regx" "${directory}"
done
fi
if [[ $LIST_OF_DIRECTORIES_WITHOUT_RADIUS ]]; then
for directory in $LIST_OF_DIRECTORIES_WITHOUT_RADIUS; do
textPass "${regx}: Directory ${directory} does not have Radius Server configured" "${regx}" "${directory}"
done
fi
else
textPass "${regx}: No Directory Service directories found" "${regx}"
fi
done
}

45
checks/check_extra7190 Normal file
View File

@@ -0,0 +1,45 @@
#!/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_extra7190="7.190"
CHECK_TITLE_extra7190="[extra7190] Ensure user maximum session duration is no longer than 10 hours."
CHECK_SCORED_extra7190="NOT_SCORED"
CHECK_CIS_LEVEL_extra7190="EXTRA"
CHECK_SEVERITY_extra7190="Medium"
CHECK_ASFF_TYPE_extra7190="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7190="AmazonAppStream"
CHECK_ALTERNATE_check7190="extra7190"
CHECK_SERVICENAME_extra7190="appstream"
CHECK_RISK_extra7190="Having a session duration lasting longer than 10 hours should not be necessary and if running for any malicious reasons provides a greater time for usage than should be allowed."
CHECK_REMEDIATION_extra7190="Change the Maximum session duration is set to 600 minutes or less for the AppStream Fleet"
CHECK_DOC_extra7190='https://docs.aws.amazon.com/appstream2/latest/developerguide/set-up-stacks-fleets.html'
CHECK_CAF_EPIC_extra7190="Infrastructure Security"
extra7190(){
for regx in $REGIONS; do
LIST_OF_FLEETS_WITH_MAX_SESSION_DURATION_ABOVE_RECOMMENDED=$("${AWSCLI}" appstream describe-fleets $PROFILE_OPT --region "${regx}" --query 'Fleets[?MaxUserDurationInSeconds>=`36000`].Arn' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|Connect timeout on endpoint URL' <<< "${LIST_OF_FLEETS_WITH_MAX_SESSION_DURATION_ABOVE_RECOMMENDED}"; then
textInfo "${regx}: Access Denied trying to describe appstream fleet(s)" "${regx}"
continue
fi
if [[ $LIST_OF_FLEETS_WITH_MAX_SESSION_DURATION_ABOVE_RECOMMENDED && $LIST_OF_FLEETS_WITH_MAX_SESSION_DURATION_ABOVE_RECOMMENDED != '[]' ]]; then
for Arn in $LIST_OF_FLEETS_WITH_MAX_SESSION_DURATION_ABOVE_RECOMMENDED; do
textFail "$regx: Fleet: ${Arn} has the maximum session duration configured for longer than 10 hours duration." "$regx" "${Arn}"
done
else
textPass "${regx}: No AppStream Fleets having a maximum session duration lasting longer than 10 hours found." "${regx}"
fi
done
}

45
checks/check_extra7191 Normal file
View File

@@ -0,0 +1,45 @@
#!/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_extra7191="7.191"
CHECK_TITLE_extra7191="[extra7191] Ensure session disconnect timeout is set to 5 minutes or less."
CHECK_SCORED_extra7191="NOT_SCORED"
CHECK_CIS_LEVEL_extra7191="EXTRA"
CHECK_SEVERITY_extra7191="Medium"
CHECK_ASFF_TYPE_extra7191="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7191="AmazonAppStream"
CHECK_ALTERNATE_check7191="extra7191"
CHECK_SERVICENAME_extra7191="appstream"
CHECK_RISK_extra7191="Disconnect timeout in minutes, is the amount of of time that a streaming session remains active after users disconnect."
CHECK_REMEDIATION_extra7191="Change the Disconnect timeout to 5 minutes or less for the AppStream Fleet"
CHECK_DOC_extra7191='https://docs.aws.amazon.com/appstream2/latest/developerguide/set-up-stacks-fleets.html'
CHECK_CAF_EPIC_extra7191="Infrastructure Security"
extra7191(){
for regx in $REGIONS; do
LIST_OF_FLEETS_WITH_SESSION_DISCONNECT_DURATION_ABOVE_RECOMMENDED=$("${AWSCLI}" appstream describe-fleets $PROFILE_OPT --region "${regx}" --query 'Fleets[?DisconnectTimeoutInSeconds>`300`].Arn' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|Connect timeout on endpoint URL' <<< "${LIST_OF_FLEETS_WITH_SESSION_DISCONNECT_DURATION_ABOVE_RECOMMENDED}"; then
textInfo "${regx}: Access Denied trying to describe appstream fleet(s)" "${regx}"
continue
fi
if [[ $LIST_OF_FLEETS_WITH_SESSION_DISCONNECT_DURATION_ABOVE_RECOMMENDED && $LIST_OF_FLEETS_WITH_SESSION_DISCONNECT_DURATION_ABOVE_RECOMMENDED != '[]' ]]; then
for Arn in $LIST_OF_FLEETS_WITH_SESSION_DISCONNECT_DURATION_ABOVE_RECOMMENDED; do
textFail "$regx: Fleet: ${Arn} has the session disconnect timeout is set to more than 5 minutes." "$regx" "${Arn}"
done
else
textPass "${regx}: No AppStream Fleets having the session disconnect timeout set to more than 5 minutes found." "${regx}"
fi
done
}

45
checks/check_extra7192 Normal file
View File

@@ -0,0 +1,45 @@
#!/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_extra7192="7.192"
CHECK_TITLE_extra7192="[extra7192] Ensure session idle disconnect timeout is set to 10 minutes or less."
CHECK_SCORED_extra7192="NOT_SCORED"
CHECK_CIS_LEVEL_extra7192="EXTRA"
CHECK_SEVERITY_extra7192="Medium"
CHECK_ASFF_TYPE_extra7192="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7192="AmazonAppStream"
CHECK_ALTERNATE_check7192="extra7192"
CHECK_SERVICENAME_extra7192="appstream"
CHECK_RISK_extra7192="Idle disconnect timeout in minutes is the amount of time that users can be inactive before they are disconnected from their streaming session and the Disconnect timeout in minutes time begins."
CHECK_REMEDIATION_extra7192="Change the session idle timeout to 10 minutes or less for the AppStream Fleet."
CHECK_DOC_extra7192='https://docs.aws.amazon.com/appstream2/latest/developerguide/set-up-stacks-fleets.html'
CHECK_CAF_EPIC_extra7192="Infrastructure Security"
extra7192(){
for regx in $REGIONS; do
LIST_OF_FLEETS_WITH_SESSION_IDLE_DISCONNECT_DURATION_ABOVE_RECOMMENDED=$("${AWSCLI}" appstream describe-fleets $PROFILE_OPT --region "${regx}" --query 'Fleets[?IdleDisconnectTimeoutInSeconds>`600`].Arn' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|Connect timeout on endpoint URL' <<< "${LIST_OF_FLEETS_WITH_SESSION_IDLE_DISCONNECT_DURATION_ABOVE_RECOMMENDED}"; then
textInfo "${regx}: Access Denied trying to describe appstream fleet(s)" "${regx}"
continue
fi
if [[ $LIST_OF_FLEETS_WITH_SESSION_IDLE_DISCONNECT_DURATION_ABOVE_RECOMMENDED && $LIST_OF_FLEETS_WITH_SESSION_IDLE_DISCONNECT_DURATION_ABOVE_RECOMMENDED != '[]' ]]; then
for Arn in $LIST_OF_FLEETS_WITH_SESSION_IDLE_DISCONNECT_DURATION_ABOVE_RECOMMENDED; do
textFail "$regx: Fleet: ${Arn} has the session idle disconnect timeout is set to more than 10 minutes." "$regx" "${Arn}"
done
else
textPass "${regx}: No AppStream Fleets having the session idle disconnect timeout set to more than 10 minutes found." "${regx}"
fi
done
}

45
checks/check_extra7193 Normal file
View File

@@ -0,0 +1,45 @@
#!/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_extra7193="7.193"
CHECK_TITLE_extra7193="[extra7193] Ensure default Internet Access from your Amazon AppStream fleet streaming instances should remain unchecked."
CHECK_SCORED_extra7193="NOT_SCORED"
CHECK_CIS_LEVEL_extra7193="EXTRA"
CHECK_SEVERITY_extra7193="Medium"
CHECK_ASFF_TYPE_extra7193="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_extra7193="AmazonAppStream"
CHECK_ALTERNATE_check7193="extra7193"
CHECK_SERVICENAME_extra7193="appstream"
CHECK_RISK_extra7193="Default Internet Access from your fleet streaming instances should be controlled using a NAT gateway in the VPC."
CHECK_REMEDIATION_extra7193="Uncheck the default internet access for the AppStream Fleet."
CHECK_DOC_extra7193='https://docs.aws.amazon.com/appstream2/latest/developerguide/set-up-stacks-fleets.html'
CHECK_CAF_EPIC_extra7193="Infrastructure Security"
extra7193(){
for regx in $REGIONS; do
LIST_OF_FLEETS_WITH_DEFAULT_INTERNET_ACCESS_ENABLED=$("${AWSCLI}" appstream describe-fleets $PROFILE_OPT --region "${regx}" --query 'Fleets[?EnableDefaultInternetAccess==`true`].Arn' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|Connect timeout on endpoint URL' <<< "${LIST_OF_FLEETS_WITH_DEFAULT_INTERNET_ACCESS_ENABLED}"; then
textInfo "${regx}: Access Denied trying to describe appstream fleet(s)" "${regx}"
continue
fi
if [[ $LIST_OF_FLEETS_WITH_DEFAULT_INTERNET_ACCESS_ENABLED && $LIST_OF_FLEETS_WITH_DEFAULT_INTERNET_ACCESS_ENABLED != '[]' ]]; then
for Arn in $LIST_OF_FLEETS_WITH_DEFAULT_INTERNET_ACCESS_ENABLED; do
textFail "$regx: Fleet: ${Arn} has default internet access enabled." "$regx" "${Arn}"
done
else
textPass "${regx}: No AppStream Fleets have default internet access enabled." "${regx}"
fi
done
}

61
checks/check_extra7194 Normal file
View File

@@ -0,0 +1,61 @@
#!/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.
# Remediation:
#
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/lp_creation.html
#
# aws ecr put-lifecycle-policy \
# --repository-name repository-name \
# --lifecycle-policy-text file://policy.json
CHECK_ID_extra7194="7.194"
CHECK_TITLE_extra7194="[extra7194] Check if ECR repositories have lifecycle policies enabled"
CHECK_SCORED_extra7194="NOT_SCORED"
CHECK_CIS_LEVEL_extra7194="EXTRA"
CHECK_SEVERITY_extra7194="Low"
CHECK_ALTERNATE_check776="extra7194"
CHECK_SERVICENAME_extra7194="ecr"
CHECK_ASFF_RESOURCE_TYPE_extra7194="AwsEcrRepository"
CHECK_RISK_extra7194='Amazon ECR repositories run the risk of retaining huge volumes of images, increasing unnecessary cost.'
CHECK_REMEDIATION_extra7194='Open the Amazon ECR console. Create an ECR lifecycle policy.'
CHECK_DOC_extra7194='https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html'
CHECK_CAF_EPIC_extra7194=''
extra7194(){
for region in ${REGIONS}; do
# List ECR repositories
LIST_ECR_REPOS=$(${AWSCLI} ecr describe-repositories ${PROFILE_OPT} --region "${region}" --query "repositories[*].[repositoryName]" --output text 2>&1)
# Handle authorization errors
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL' <<< "${LIST_ECR_REPOS}"; then
textInfo "${region}: Access Denied trying to describe ECR repositories" "${region}"
continue
fi
if [[ -n "${LIST_ECR_REPOS}" ]]; then
for repo in ${LIST_ECR_REPOS}; do
# Check if a lifecycle policy exists
LIFECYCLE_POLICY=$($AWSCLI ecr get-lifecycle-policy ${PROFILE_OPT} --region "${region}" --repository-name "${repo}" --query "repositoryName" --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL' <<< "${LIFECYCLE_POLICY}"; then
textInfo "${region}: Access Denied trying to get lifecycle policy from repository: ${repo}" "${region}"
continue
elif grep -q -E 'LifecyclePolicyNotFoundException' <<< "$LIFECYCLE_POLICY"; then
textFail "${region}: ECR repository ${repo} has no lifecycle policy" "${region}" "${repo}"
else
textPass "${region}: ECR repository ${repo} has a lifecycle policy" "${region}" "${repo}"
fi
done
else
textPass "${region}: No ECR repositories found" "${region}"
fi
done
}

105
checks/check_extra7195 Normal file
View File

@@ -0,0 +1,105 @@
#!/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.
# Remediation:
#
# here URL to the relevand/official documentation
# https://docs.aws.amazon.com/codeartifact/latest/ug/package-origin-controls.html
# https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d
# https://aws.amazon.com/blogs/devops/tighten-your-package-security-with-codeartifact-package-origin-control-toolkit/
#
#
# here commands or steps to fix it if avalable, like:
# aws codeartifact put-package-origin-configuration \
# --package "MyPackage" \
# --namespace "MyNamespace" \ #You don't need namespace for npm or pypi
# --domain "MyDomain" \
# --repository "MyRepository" \
# --domain-owner "MyOwnerAccount"
# --format "MyFormat" \ # npm/pypi/maven
# --restrictions 'publish=ALLOW,upstream=BLOCK'
CHECK_ID_extra7195="7.195"
CHECK_TITLE_extra7195="[extra7195] Ensure CodeArtifact internal packages do not allow external public source publishing."
CHECK_SCORED_extra7195="NOT_SCORED"
CHECK_CIS_LEVEL_extra7195="EXTRA"
CHECK_SEVERITY_extra7195="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra7195="Other"
CHECK_ALTERNATE_check7195="extra7195"
CHECK_SERVICENAME_extra7195="codeartifact"
CHECK_RISK_extra7195="Allowing package versions of a package to be added both by direct publishing and ingesting from public repositories makes you vulnerable to a dependency substitution attack."
CHECK_REMEDIATION_extra7195="Configure package origin controls on a package in a repository to limit how versions of that package can be added to the repository."
CHECK_DOC_extra7195="https://docs.aws.amazon.com/codeartifact/latest/ug/package-origin-controls.html"
CHECK_CAF_EPIC_extra7195=""
extra7195(){
# Checks Code Artifact packages for Dependency Confusion
# Looking for codeartifact repositories in all regions
for regx in ${REGIONS}; do
LIST_OF_REPOSITORIES=$("${AWSCLI}" codeartifact list-repositories ${PROFILE_OPT} --region "${regx}" --query 'repositories[*].[name,domainName,domainOwner]' --output text 2>&1)
if [[ $(echo "${LIST_OF_REPOSITORIES}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|ExpiredToken') ]]; then
textInfo "${regx}: Access Denied trying to list repositories" "${regx}"
continue
fi
if [[ "${LIST_OF_REPOSITORIES}" != "" && "${LIST_OF_REPOSITORIES}" != "none" ]]; then
while read -r REPOSITORY DOMAIN ACCOUNT; do
# Iterate over repositories to get packages
# Found repository scanning packages
LIST_OF_PACKAGES=$(aws codeartifact list-packages --repository "$REPOSITORY" --domain "$DOMAIN" --domain-owner "$ACCOUNT" ${PROFILE_OPT} --region "${regx}" --query 'packages[*].[package, namespace, format, originConfiguration.restrictions.upstream]' --output text 2>&1)
if [[ $(echo "${LIST_OF_PACKAGES}" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|ExpiredToken') ]]; then
textInfo "${regx}: Access Denied trying to list packages for repository: ${REPOSITORY}" "${regx}" "${REPOSITORY}"
continue
fi
if [[ "${LIST_OF_PACKAGES}" != "" && "${LIST_OF_PACKAGES}" != "none" ]]; then
while read -r PACKAGE NAMESPACE FORMAT UPSTREAM; do
# Get the latest version of the package we assume if the latest is internal the package is internal
# textInfo "Found package: $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE}"
LATEST=$(aws codeartifact list-package-versions --package "$PACKAGE" $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "--namespace $NAMESPACE"; fi) --domain "$DOMAIN" --repository "$REPOSITORY" --domain-owner "$ACCOUNT" --format "$FORMAT" ${PROFILE_OPT} --region "${regx}" --sort-by PUBLISHED_TIME --no-paginate --query 'versions[0].version' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|ExpiredToken' <<< "${LATEST}"; then
textInfo "${regx}: Access Denied trying to get latest version for packages: $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE}" "${regx}"
continue
fi
if grep -q -E 'ResourceNotFoundException' <<< "${LATEST}"; then
textInfo "${regx}: Package not found for package: $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE}" "${regx}"
continue
fi
LATEST=$(head -n 1 <<< $LATEST)
# textInfo "Latest version: ${LATEST}"
# Get the origin type for the latest version
ORIGIN_TYPE=$(aws codeartifact describe-package-version --package "$PACKAGE" $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "--namespace $NAMESPACE"; fi) --domain "$DOMAIN" --repository "$REPOSITORY" --domain-owner "$ACCOUNT" --format "$FORMAT" --package-version "$LATEST" ${PROFILE_OPT} --region "${regx}" --query 'packageVersion.origin.originType' --output text 2>&1)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError|Could not connect to the endpoint URL|ExpiredToken' <<< "${ORIGIN_TYPE}"; then
textInfo "${regx}: Access Denied trying to get origin type of package $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE}:${LATEST}" "${regx}" "${PACKAGE}"
continue
fi
if grep -q -E 'INTERNAL|UNKNOWN' <<< "${ORIGIN_TYPE}"; then
# The package is internal
if [[ "$UPSTREAM" == "ALLOW" ]]; then
# The package is not configured to block upstream fail check
textFail "${regx}: Internal package $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE} is vulnerable to dependency confusion in repository ${REPOSITORY}" "${regx}" "${PACKAGE}"
else
textPass "${regx}: Internal package $(if [[ "$NAMESPACE" != "" && "$NAMESPACE" != "None" ]]; then echo "${NAMESPACE}:"; fi)${PACKAGE} is NOT vulnerable to dependency confusion in repository ${REPOSITORY}" "${regx}" "${PACKAGE}"
fi
fi
done <<< "${LIST_OF_PACKAGES}"
else
textInfo "${regx}: No packages found in ${REPOSITORY}" "${regx}" "${REPOSITORY}"
fi
done <<< "${LIST_OF_REPOSITORIES}"
else
textPass "${regx}: No repositories found" "${regx}"
fi
done
}

View File

@@ -33,13 +33,18 @@ extra72(){
textInfo "$regx: Access Denied trying to describe snapshot" "$regx"
continue
fi
for snapshot in $LIST_OF_EBS_SNAPSHOTS; do
SNAPSHOT_IS_PUBLIC=$($AWSCLI ec2 describe-snapshot-attribute $PROFILE_OPT --region $regx --output text --snapshot-id $snapshot --attribute createVolumePermission --query "CreateVolumePermissions[?Group=='all']")
if [[ $SNAPSHOT_IS_PUBLIC ]];then
textFail "$regx: $snapshot is currently Public!" "$regx" "$snapshot"
else
textPass "$regx: $snapshot is not Public" "$regx" "$snapshot"
fi
done
if [[ ${LIST_OF_EBS_SNAPSHOTS} ]]
then
for snapshot in $LIST_OF_EBS_SNAPSHOTS; do
SNAPSHOT_IS_PUBLIC=$($AWSCLI ec2 describe-snapshot-attribute $PROFILE_OPT --region $regx --output text --snapshot-id $snapshot --attribute createVolumePermission --query "CreateVolumePermissions[?Group=='all']")
if [[ $SNAPSHOT_IS_PUBLIC ]];then
textFail "$regx: $snapshot is currently Public!" "$regx" "$snapshot"
else
textPass "$regx: $snapshot is not Public" "$regx" "$snapshot"
fi
done
else
textPass "$regx: There is no EBS Snapshots" "$regx" "No EBS Snapshots"
fi
done
}

View File

@@ -18,8 +18,8 @@ CHECK_SEVERITY_extra723="Critical"
CHECK_ASFF_RESOURCE_TYPE_extra723="AwsRdsDbSnapshot"
CHECK_ALTERNATE_check723="extra723"
CHECK_SERVICENAME_extra723="rds"
CHECK_RISK_extra723='Publicly accessible services could expose sensitive data to bad actors. t is recommended that your RDS snapshots should not be public in order to prevent potential leak or misuse of sensitive data or any other kind of security threat. If your RDS snapshot is public; then the data which is backed up in that snapshot is accessible to all other AWS accounts.'
CHECK_REMEDIATION_extra723='Use AWS Config to identify any sanpshot that is public.'
CHECK_RISK_extra723='Publicly accessible services could expose sensitive data to bad actors. It is recommended that your RDS snapshots should not be public in order to prevent potential leak or misuse of sensitive data or any other kind of security threat. If your RDS snapshot is public then the data which is backed up in that snapshot is accessible to all other AWS accounts.'
CHECK_REMEDIATION_extra723='Use AWS Config to identify any snapshot that is public.'
CHECK_DOC_extra723='https://docs.aws.amazon.com/config/latest/developerguide/rds-snapshots-public-prohibited.html'
CHECK_CAF_EPIC_extra723='Data Protection'

View File

@@ -27,17 +27,26 @@ CHECK_CAF_EPIC_extra728='Data Protection'
extra728(){
for regx in $REGIONS; do
LIST_SQS=$($AWSCLI sqs list-queues $PROFILE_OPT --region $regx --query QueueUrls --output text 2>&1|grep -v ^None )
if [[ $(echo "$LIST_SQS" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
LIST_SQS=$("$AWSCLI" sqs list-queues $PROFILE_OPT --region "$regx" --query QueueUrls --output text 2>&1|grep -v ^None )
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_SQS}"; then
textInfo "$regx: Access Denied trying to list queues" "$regx"
continue
fi
if [[ $LIST_SQS ]]; then
for queue in $LIST_SQS; do
# check if the policy has KmsMasterKeyId therefore SSE enabled
SSE_ENABLED_QUEUE=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.KmsMasterKeyId --output text|grep -v ^None)
if [[ $SSE_ENABLED_QUEUE ]]; then
textPass "$regx: SQS queue $queue is using Server Side Encryption" "$regx" "$queue"
SSE_KMS_ENABLED_QUEUE=$("$AWSCLI" sqs get-queue-attributes --queue-url "$queue" $PROFILE_OPT --region "$regx" --attribute-names All --query Attributes.KmsMasterKeyId --output text)
SSE_SQS_ENABLED_QUEUE=$("$AWSCLI" sqs get-queue-attributes --queue-url "$queue" $PROFILE_OPT --region "$regx" --attribute-names All --query Attributes.SqsManagedSseEnabled --output text)
if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${SSE_SQS_ENABLED_QUEUE}"; then
textInfo "$regx: Access Denied trying to list queues" "$regx"
continue
elif grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${SSE_KMS_ENABLED_QUEUE}"; then
textInfo "$regx: Access Denied trying to list queues" "$regx"
continue
elif [[ "$SSE_KMS_ENABLED_QUEUE" != 'None' ]]; then
textPass "$regx: SQS queue $queue is using KMS Server Side Encryption" "$regx" "$queue"
elif [[ "$SSE_SQS_ENABLED_QUEUE" == 'true' ]]; then
textPass "$regx: SQS queue $queue is using SQS Server Side Encryption" "$regx" "$queue"
else
textFail "$regx: SQS queue $queue is not using Server Side Encryption" "$regx" "$queue"
fi

View File

@@ -21,28 +21,32 @@ CHECK_ALTERNATE_check729="extra729"
CHECK_ASFF_COMPLIANCE_TYPE_extra729="ens-mp.info.3.aws.ebs.1"
CHECK_SERVICENAME_extra729="ec2"
CHECK_RISK_extra729='Data encryption at rest prevents data visibility in the event of its unauthorized access or theft.'
CHECK_REMEDIATION_extra729='Encrypt al EBS volumes and Enable Encryption by default You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.'
CHECK_REMEDIATION_extra729='Encrypt all EBS volumes and Enable Encryption by default You can configure your AWS account to enforce the encryption of the new EBS volumes and snapshot copies that you create. For example; Amazon EBS encrypts the EBS volumes created when you launch an instance and the snapshots that you copy from an unencrypted snapshot.'
CHECK_DOC_extra729='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html'
CHECK_CAF_EPIC_extra729='Data Protection'
extra729(){
# "Ensure there are no EBS Volumes unencrypted "
for regx in $REGIONS; do
LIST_OF_EBS_NON_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`false`].VolumeId' --output text 2>&1)
if [[ $(echo "$LIST_OF_EBS_NON_ENC_VOLUMES" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to describe volumes" "$regx"
continue
fi
if [[ $LIST_OF_EBS_NON_ENC_VOLUMES ]];then
for volume in $LIST_OF_EBS_NON_ENC_VOLUMES; do
textFail "$regx: $volume is not encrypted!" "$regx" "$volume"
done
fi
LIST_OF_EBS_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`true`].VolumeId' --output text)
if [[ $LIST_OF_EBS_ENC_VOLUMES ]];then
for volume in $LIST_OF_EBS_ENC_VOLUMES; do
textPass "$regx: $volume is encrypted" "$regx" "$volume"
done
fi
LIST_OF_EBS_NON_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`false`].VolumeId' --output text 2>&1)
if [[ $(echo "$LIST_OF_EBS_NON_ENC_VOLUMES" | grep -E 'AccessDenied|UnauthorizedOperation') ]]; then
textInfo "$regx: Access Denied trying to describe volumes" "$regx"
continue
fi
if [[ $LIST_OF_EBS_NON_ENC_VOLUMES ]];then
for volume in $LIST_OF_EBS_NON_ENC_VOLUMES; do
textFail "$regx: $volume is not encrypted!" "$regx" "$volume"
done
fi
LIST_OF_EBS_ENC_VOLUMES=$($AWSCLI ec2 describe-volumes $PROFILE_OPT --region $regx --query 'Volumes[?Encrypted==`true`].VolumeId' --output text)
if [[ $LIST_OF_EBS_ENC_VOLUMES ]];then
for volume in $LIST_OF_EBS_ENC_VOLUMES; do
textPass "$regx: $volume is encrypted" "$regx" "$volume"
done
fi
if [[ ! "${LIST_OF_EBS_NON_ENC_VOLUMES}" ]] && [[ ! "${LIST_OF_EBS_ENC_VOLUMES}" ]]
then
textPass "$regx: There are no ebs volumes" "$regx" "No ebs volumes"
fi
done
}

View File

@@ -27,24 +27,34 @@ CHECK_DOC_extra730='https://docs.aws.amazon.com/config/latest/developerguide/acm
CHECK_CAF_EPIC_extra730='Data Protection'
extra730(){
# Only RSA key types, needed to recover Amazon Issued, Imported and Private PKIs
local ACM_KEY_TYPES="RSA_1024,RSA_2048,RSA_3072,RSA_4096"
local ACM_CERTIFICATE_STATUSES="ISSUED"
# "Check if ACM Certificates are about to expire in $DAYS_TO_EXPIRE_THRESHOLD days or less"
for regx in $REGIONS; do
LIST_OF_ACM_CERTS=$($AWSCLI acm list-certificates $PROFILE_OPT --region $regx --query 'CertificateSummaryList[].CertificateArn' --output text)
if [[ $LIST_OF_ACM_CERTS ]];then
LIST_OF_ACM_CERTS=$("${AWSCLI}" acm list-certificates ${PROFILE_OPT} --region "${regx}" --include keyTypes="${ACM_KEY_TYPES}" --certificate-statuses "${ACM_CERTIFICATE_STATUSES}" --query 'CertificateSummaryList[].CertificateArn' --output text)
if [[ $LIST_OF_ACM_CERTS ]]; then
for cert in $LIST_OF_ACM_CERTS; do
CERT_DATA=$($AWSCLI acm describe-certificate $PROFILE_OPT --region $regx --certificate-arn $cert --query 'Certificate.[DomainName,NotAfter]' --output text)
echo "$CERT_DATA" | while read FQDN NOTAFTER; do
EXPIRES_DATE=$(timestamp_to_date $NOTAFTER)
COUNTER_DAYS=$(how_many_days_from_today $EXPIRES_DATE)
if [[ $COUNTER_DAYS -le $DAYS_TO_EXPIRE_THRESHOLD ]]; then
textFail "$regx: Certificate for $FQDN is about to expire in $COUNTER_DAYS days!" "$regx" "$FQDN"
CERT_DATA=$("${AWSCLI}" acm describe-certificate ${PROFILE_OPT} --region "${regx}" --certificate-arn "${cert}" --query 'Certificate.[DomainName,NotAfter]' --output text)
# Format: domain.test.com YYYY-MM-DDTHH:MM:SS
echo "$CERT_DATA" | while read -r FQDN NOTAFTER; do
EXPIRES_DATE=$(timestamp_to_date "${NOTAFTER}")
if [[ "${EXPIRES_DATE}" == "" ]]
then
textInfo "${regx}: Certificate for ${FQDN} has an incorrect timestamp format: ${NOTAFTER}" "${regx}" "${FQDN}"
else
textPass "$regx: Certificate for $FQDN expires in $COUNTER_DAYS days" "$regx" "$FQDN"
COUNTER_DAYS=$(how_many_days_from_today "${EXPIRES_DATE}")
if [[ $COUNTER_DAYS -le $DAYS_TO_EXPIRE_THRESHOLD ]]; then
textFail "${regx}: Certificate for ${FQDN} is about to expire in ${COUNTER_DAYS} days!" "${regx}" "${FQDN}"
else
textPass "${regx}: Certificate for ${FQDN} expires in ${COUNTER_DAYS} days" "${regx}" "{$FQDN}"
fi
fi
done
done
else
textInfo "$regx: No certificates found" "$regx"
textInfo "${regx}: No certificates found" "${regx}"
fi
done
}

View File

@@ -36,6 +36,6 @@ extra732(){
fi
done
else
textInfo "$REGION: No CloudFront distributions found"
textInfo "$REGION: No CloudFront distributions found" "$REGION" "$ACCOUNT_NUM"
fi
}

View File

@@ -25,12 +25,12 @@ CHECK_DOC_extra734='https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encry
CHECK_CAF_EPIC_extra734='Data Protection'
extra734(){
LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --region $REGION --query Buckets[*].Name --output text|xargs -n1)
LIST_OF_BUCKETS=$("${AWSCLI}" s3api list-buckets ${PROFILE_OPT} --region "${REGION}" --query Buckets[*].Name --output text|xargs -n1)
if [[ $LIST_OF_BUCKETS ]]; then
for bucket in $LIST_OF_BUCKETS;do
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $REGION --bucket $bucket --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Location for $bucket" "$BUCKET_LOCATION" "$bucket"
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location ${PROFILE_OPT} --region "${REGION}" --bucket "${bucket}" --output text 2>&1)
if grep -q 'AccessDenied' <<< "${BUCKET_LOCATION}"; then
textInfo "${REGION}: Access Denied Trying to Get Bucket Location for ${bucket}" "${REGION}" "${bucket}"
continue
fi
if [[ $BUCKET_LOCATION == "None" ]]; then
@@ -44,48 +44,52 @@ extra734(){
# OR
# - Have bucket policy denying s3:PutObject when s3:x-amz-server-side-encryption is absent
# query to get if has encryption enabled or not
RESULT=$($AWSCLI s3api get-bucket-encryption $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query ServerSideEncryptionConfiguration.Rules[].ApplyServerSideEncryptionByDefault[].SSEAlgorithm --output text 2>&1)
if [[ $(echo "$RESULT" | grep AccessDenied) ]]; then
textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Encryption for $bucket" "$BUCKET_LOCATION" "$bucket"
RESULT=$("${AWSCLI}" s3api get-bucket-encryption ${PROFILE_OPT} --region ${BUCKET_LOCATION} --bucket "${bucket}" --query ServerSideEncryptionConfiguration.Rules[].ApplyServerSideEncryptionByDefault[].SSEAlgorithm --output text 2>&1)
if grep -q 'AccessDenied' <<< "${RESULT}"; then
textInfo "${BUCKET_LOCATION}: Access Denied Trying to Get Encryption for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
continue
fi
if [[ $RESULT == "AES256" || $RESULT == "aws:kms" ]];
elif grep -q 'ServerSideEncryptionConfigurationNotFoundError' <<< "${RESULT}"
then
textPass "$BUCKET_LOCATION: Bucket $bucket is enabled for default encryption with $RESULT" "$BUCKET_LOCATION" "$bucket"
textFail "${BUCKET_LOCATION}: Server Side Encryption configuration is not configured for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
continue
fi
TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX)
if [[ "${RESULT}" == "AES256" || "${RESULT}" == "aws:kms" ]];
then
textPass "${BUCKET_LOCATION}: Bucket $bucket is enabled for default encryption with ${RESULT}" "${BUCKET_LOCATION}" "${bucket}"
continue
fi
TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-"${ACCOUNT_NUM}"-"${bucket}".policy.XXXXXXXXXX)
# get bucket policy
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --region $BUCKET_LOCATION --output text --query Policy > $TEMP_SSE_POLICY_FILE 2>&1
if [[ $(grep AccessDenied $TEMP_SSE_POLICY_FILE) ]]; then
textInfo "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Policy for $bucket" "$BUCKET_LOCATION" "$bucket"
rm -f $TEMP_SSE_POLICY_FILE
"${AWSCLI}" s3api get-bucket-policy ${PROFILE_OPT} --bucket "${bucket}" --region "${BUCKET_LOCATION}" --output text --query Policy > "${TEMP_SSE_POLICY_FILE}" 2>&1
if grep -q 'AccessDenied' <<< "${TEMP_SSE_POLICY_FILE}"; then
textInfo "${BUCKET_LOCATION}: Access Denied Trying to Get Bucket Policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
rm -f "${TEMP_SSE_POLICY_FILE}"
continue
fi
if [[ $(grep NoSuchBucketPolicy $TEMP_SSE_POLICY_FILE) ]]; then
textFail "$BUCKET_LOCATION: No bucket policy for $bucket" "$BUCKET_LOCATION" "$bucket"
rm -f $TEMP_SSE_POLICY_FILE
if grep -q 'NoSuchBucketPolicy' <<< "${TEMP_SSE_POLICY_FILE}"; then
textFail "${BUCKET_LOCATION}: No bucket policy for ${bucket}" "${BUCKET_LOCATION}" "${bucket}"
rm -f "${TEMP_SSE_POLICY_FILE}"
continue
fi
# check if the S3 policy forces SSE s3:x-amz-server-side-encryption:true
CHECK_BUCKET_SSE_POLICY_PRESENT=$(cat $TEMP_SSE_POLICY_FILE | jq --arg arn "arn:${AWS_PARTITION}:s3:::${bucket}/*" '.Statement[]|select(.Effect=="Deny" and ((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*") and .Action=="s3:PutObject" and .Resource==$arn and .Condition.StringNotEquals."s3:x-amz-server-side-encryption" != null)')
if [[ $CHECK_BUCKET_SSE_POLICY_PRESENT == "" ]]; then
textFail "$BUCKET_LOCATION: Bucket $bucket does not enforce encryption!" "$BUCKET_LOCATION" "$bucket"
rm -f $TEMP_SSE_POLICY_FILE
CHECK_BUCKET_SSE_POLICY_PRESENT=$(jq --arg arn "arn:${AWS_PARTITION}:s3:::${bucket}/*" '.Statement[]|select(.Effect=="Deny" and ((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*") and .Action=="s3:PutObject" and .Resource==$arn and .Condition.StringNotEquals."s3:x-amz-server-side-encryption" != null)' < "${TEMP_SSE_POLICY_FILE}")
if [[ "${CHECK_BUCKET_SSE_POLICY_PRESENT}" == "" ]]; then
textFail "${BUCKET_LOCATION}: Bucket ${bucket} does not enforce encryption!" "${BUCKET_LOCATION}" "${bucket}"
rm -f "${TEMP_SSE_POLICY_FILE}"
continue
fi
CHECK_BUCKET_SSE_POLICY_VALUE=$(echo "$CHECK_BUCKET_SSE_POLICY_PRESENT" | jq -r '.Condition.StringNotEquals."s3:x-amz-server-side-encryption"')
CHECK_BUCKET_SSE_POLICY_VALUE=$(jq -r '.Condition.StringNotEquals."s3:x-amz-server-side-encryption"' <<< "${CHECK_BUCKET_SSE_POLICY_PRESENT}")
textPass "$BUCKET_LOCATION: Bucket $bucket has S3 bucket policy to enforce encryption with $CHECK_BUCKET_SSE_POLICY_VALUE" "$BUCKET_LOCATION" "$bucket"
textPass "${BUCKET_LOCATION}: Bucket ${bucket} has S3 bucket policy to enforce encryption with ${CHECK_BUCKET_SSE_POLICY_VALUE}" "${BUCKET_LOCATION}" "${bucket}"
rm -f $TEMP_SSE_POLICY_FILE
rm -f "${TEMP_SSE_POLICY_FILE}"
done
else
textInfo "$REGION No S3 Buckets found" "$REGION"
textInfo "${REGION}: No S3 Buckets found" "${REGION}"
fi
}

Some files were not shown because too many files have changed in this diff Show More