Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update 20231211 #7

Merged
merged 20 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0ea9cb2
Bump actions/checkout from 3 to 4
dependabot[bot] Sep 4, 2023
7b80b0f
Merge pull request #232 from domain-protect/dependabot/github_actions…
paulschwarzenberger Sep 12, 2023
7aa9e4a
fix manual scan error
paulschwarzenberger Sep 29, 2023
59417df
align manual tests with lambda
paulschwarzenberger Sep 29, 2023
3b62010
Merge pull request #236 from domain-protect/fix-manual-scans
paulschwarzenberger Sep 29, 2023
afb2b7f
Elastic Beanstalk improvements
paulschwarzenberger Oct 8, 2023
9a110c5
formatting Python
paulschwarzenberger Oct 8, 2023
42f43d3
reorder python imports
paulschwarzenberger Oct 8, 2023
fc3efdc
add logging
paulschwarzenberger Oct 8, 2023
ae02905
Merge pull request #238 from domain-protect/eb-improvements
paulschwarzenberger Oct 8, 2023
aa1bd1b
add permissions_boundary_arn variable to set permissions boundary on …
com6056 Oct 12, 2023
88083d4
Merge pull request #240 from com6056/main
paulschwarzenberger Oct 15, 2023
6346a5d
bump version to 0.4.4
paulschwarzenberger Oct 15, 2023
47f498f
add iam:DeleteRolePermissionsBoundary/iam:PutRolePermissionsBoundary …
com6056 Oct 16, 2023
00da35f
Merge pull request #241 from com6056/main
paulschwarzenberger Oct 18, 2023
2425d2d
Manual scanning: avoid requiring an AWS profile for better usability
christophetd Dec 11, 2023
89b0072
Fix formatting
christophetd Dec 11, 2023
201b1b5
Remove --profile from screenshots
christophetd Dec 11, 2023
0d74d83
Merge pull request #245 from domain-protect/remove-aws-profile
christophetd Dec 11, 2023
a16d380
Merge branch 'domain-protect:main' into Update
eolvera-bc Dec 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
pull-requests: write # Required for Publish Test Results
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
pull-requests: write # Required for Publish Test Results
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# OWASP Domain Protect
![Release version](https://img.shields.io/badge/release-v0.4.2-blue.svg)
![Release version](https://img.shields.io/badge/release-v0.4.4-blue.svg)
[![Python 3.x](https://img.shields.io/badge/Python-3.x-blue.svg)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
![OWASP Maturity](https://img.shields.io/badge/owasp-incubator%20project-53AAE5.svg)
Expand Down
2 changes: 2 additions & 0 deletions aws-iam-policies/domain-protect-deploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"iam:CreateRole",
"iam:CreateServiceLinkedRole",
"iam:DeleteRole",
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteServiceLinkedRole",
"iam:DetachRolePolicy",
"iam:DeleteRolePolicy",
Expand All @@ -53,6 +54,7 @@
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:ListRolePolicies",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:PassRole"
],
Expand Down
1 change: 0 additions & 1 deletion docs/integration-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ We are using the moto python module for mocking out AWS, and setting these up us

Then in the test you require the mock you can use the function name (e.g. `moto_route53`) as the parameter. You can then use the mock as if it was the boto3 library to create the resources you need for testing, which will be created in a mocked out aws account.

Because we use aws profiles we require the profile name to be set up in boto3 for testing. A "mocked" aws profile is set up in `integration_tests/conftest.py` in the `aws_credentials` fixture (this is why the other moto fixtures have the `aws_credentials` parameter). If the code under test needs an aws profile name, please use "mocked".

[back to Automated Tests](automated-tests.md)<br>
[back to README](../README.md)
8 changes: 0 additions & 8 deletions integration_tests/manual_scans/aws/test_aws_alias_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ def test_main_detects_vulnerable_domains(arg_parse_mock, print_list_mock, moto_r

requests_mock.get("http://vulnerable.domain-protect.com.", status_code=404, text="Code: NoSuchBucket")

arg_parse_mock.return_value.parse_args.return_value.profile = "mocked"

main()

expected_vulnerable_call = call(["vulnerable.domain-protect.com."], "INSECURE_WS")
Expand All @@ -57,8 +55,6 @@ def test_main_ignores_non_vulnerable_domains(arg_parse_mock, print_list_mock, mo

requests_mock.get("http://vulnerable.domain-protect.com.", status_code=200, text="All good here")

arg_parse_mock.return_value.parse_args.return_value.profile = "mocked"

main()

print_list_mock.assert_not_called()
Expand All @@ -71,8 +67,6 @@ def test_main_ignores_non_s3_domains(arg_parse_mock, print_list_mock, moto_route

requests_mock.get("http://vulnerable.domain-protect.com.", status_code=404, text="Code: NoSuchBucket")

arg_parse_mock.return_value.parse_args.return_value.profile = "mocked"

main()

print_list_mock.assert_not_called()
Expand All @@ -85,8 +79,6 @@ def test_main_ignores_domains_with_connection_error(arg_parse_mock, print_list_m

requests_mock.get("http://vulnerable.domain-protect.com.", exc=requests.exceptions.ConnectionError)

arg_parse_mock.return_value.parse_args.return_value.profile = "mocked"

main()

print_list_mock.assert_not_called()
10 changes: 2 additions & 8 deletions lambda_code/cloudflare_scan/cloudflare_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import os

from utils.utils_aws import eb_susceptible
from utils.utils_aws import publish_to_sns
from utils.utils_bugcrowd import bugcrowd_create_issue
from utils.utils_cloudflare import list_cloudflare_records
Expand Down Expand Up @@ -182,14 +183,7 @@ def cf_s3(account_name, zone_name, records):


def cf_eb(account_name, zone_name, records):

vulnerability_list = [".elasticbeanstalk.com"]

records_filtered = [
r
for r in records
if r["Type"] in ["CNAME"] and any(vulnerability in r["Value"] for vulnerability in vulnerability_list)
]
records_filtered = [r for r in records if r["Type"] in ["CNAME"] and eb_susceptible(r["Value"])]

for record in records_filtered:

Expand Down
7 changes: 3 additions & 4 deletions lambda_code/scan/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import os

from utils.utils_aws import eb_susceptible
from utils.utils_aws import get_cloudfront_origin
from utils.utils_aws import list_domains
from utils.utils_aws import list_hosted_zones
Expand Down Expand Up @@ -120,7 +121,7 @@ def alias_cloudfront_s3(account_name, record_sets, account_id):
def alias_eb(account_name, record_sets):

record_sets_filtered = [
r for r in record_sets if "AliasTarget" in r and "elasticbeanstalk.com" in r["AliasTarget"]["DNSName"]
r for r in record_sets if "AliasTarget" in r and eb_susceptible(r["AliasTarget"]["DNSName"])
]

for record in record_sets_filtered:
Expand Down Expand Up @@ -192,9 +193,7 @@ def cname_eb(account_name, record_sets):
record_sets_filtered = [
r
for r in record_sets
if r["Type"] in ["CNAME"]
and "ResourceRecords" in r
and "elasticbeanstalk.com" in r["ResourceRecords"][0]["Value"]
if r["Type"] in ["CNAME"] and "ResourceRecords" in r and eb_susceptible(r["ResourceRecords"][0]["Value"])
]

for record in record_sets_filtered:
Expand Down
7 changes: 7 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module "lambda-role" {
region = var.region
security_audit_role_name = var.security_audit_role_name
kms_arn = module.kms.kms_arn
permissions_boundary_arn = var.permissions_boundary_arn
}

module "lambda-slack" {
Expand Down Expand Up @@ -72,6 +73,7 @@ module "accounts-role" {
kms_arn = module.kms.kms_arn
state_machine_arn = module.step-function.state_machine_arn
policy = "accounts"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "lambda-scan" {
Expand Down Expand Up @@ -118,6 +120,7 @@ module "takeover-role" {
kms_arn = module.kms.kms_arn
takeover = local.takeover
policy = "takeover"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "lambda-resources" {
Expand All @@ -141,6 +144,7 @@ module "resources-role" {
security_audit_role_name = var.security_audit_role_name
kms_arn = module.kms.kms_arn
policy = "resources"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "cloudwatch-event" {
Expand Down Expand Up @@ -248,6 +252,7 @@ module "step-function-role" {
kms_arn = module.kms.kms_arn
policy = "state"
assume_role_policy = "state"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "step-function" {
Expand Down Expand Up @@ -284,6 +289,7 @@ module "lambda-role-ips" {
kms_arn = module.kms.kms_arn
policy = "lambda"
role_name = "lambda-ips"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "lambda-scan-ips" {
Expand Down Expand Up @@ -321,6 +327,7 @@ module "accounts-role-ips" {
state_machine_arn = module.step-function-ips[0].state_machine_arn
policy = "accounts"
role_name = "accounts-ips"
permissions_boundary_arn = var.permissions_boundary_arn
}

module "lambda-accounts-ips" {
Expand Down
57 changes: 25 additions & 32 deletions manual_scans/aws/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,65 +32,66 @@ $ export PYTHONPATH="${PYTHONPATH}:/Users/paul/src/github.com/domain-protect/dom
* run manual scans from root of domain-protect folder

## CloudFront Alias with missing S3 origin
* replace PROFILE_NAME by your AWS CLI profile name


```
python manual_scans/aws/aws-alias-cloudfront-s3.py --profile PROFILE_NAME
python manual_scans/aws/aws-alias-cloudfront-s3.py
```

![Alt text](images/aws-cloudfront-s3-alias.png?raw=true "CloudFront Alias with missing S3 origin")

## CloudFront CNAME with missing S3 origin
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-cname-cloudfront-s3.py --profile PROFILE_NAME
python manual_scans/aws/aws-cname-cloudfront-s3.py
```

![Alt text](images/aws-cloudfront-s3-cname.png?raw=true "CloudFront CNAME with missing S3 origin")

## ElasticBeanstalk Alias
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-alias-eb.py --profile PROFILE_NAME
python manual_scans/aws/aws-alias-eb.py
```

![Alt text](images/aws-eb-alias.png?raw=true "Detect vulnerable S3 Aliases")

## ElasticBeanstalk CNAMES
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-cname-eb.py --profile PROFILE_NAME
python manual_scans/aws/aws-cname-eb.py
```

![Alt text](images/aws-eb-cnames.png?raw=true "Detect vulnerable ElasticBeanstalk CNAMEs")

## S3 Alias
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws_alias_s3.py --profile PROFILE_NAME
python manual_scans/aws/aws_alias_s3.py
```

![Alt text](images/aws-s3-alias.png?raw=true "Detect vulnerable S3 Aliases")

## S3 CNAMES
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-cname-s3.py --profile PROFILE_NAME
python manual_scans/aws/aws-cname-s3.py
```

![Alt text](images/aws-s3-cnames.png?raw=true "Detect vulnerable S3 CNAMEs")

## registered domains with missing hosted zone
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-ns-domain.py --profile PROFILE_NAME
python manual_scans/aws/aws-ns-domain.py
```

![Alt text](images/aws-ns-domain.png?raw=true "Detect vulnerable subdomains")

## subdomain NS delegations
* replace PROFILE_NAME by your AWS CLI profile name

```
python manual_scans/aws/aws-ns-subdomain.py --profile PROFILE_NAME
python manual_scans/aws/aws-ns-subdomain.py
```

![Alt text](images/aws-ns-subdomain.png?raw=true "Detect vulnerable subdomains")
Expand All @@ -103,26 +104,18 @@ python manual_scans/aws/aws-ns-subdomain.py --profile PROFILE_NAME
```
aws sts assume-role --role-arn arn:aws:iam::012345678901:role/securityaudit --role-session-name domainprotect
```
* copy and paste the returned temporary credentials to your desktop
* create AWS cli credentials in CloudShell
```
vi .aws/credentials
```
* enter details in the following format
```
[profile_name]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
aws_session_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```
* save and exit vi
```
:wq!
* set the returned temporary credentials in the environmebt variables of your local machine:

```bash
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
```

* install dependencies and proceed with the scans, e.g.
```
sudo pip3 install dnspython
python3 manual_scans/aws/aws-ns-domain.py --profile profile_name
python3 manual_scans/aws/aws-ns-domain.py
```

[back to README](../../README.md)
Expand Down
11 changes: 4 additions & 7 deletions manual_scans/aws/aws-alias-cloudfront-s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ def vulnerable_alias_cloudfront_s3(domain_name):
return False


def route53(profile):
def route53():

print("Searching for Route53 hosted zones")

session = boto3.Session(profile_name=profile)
session = boto3.Session()
route53 = session.client("route53")

hosted_zones = list_hosted_zones_manual_scan(profile)
hosted_zones = list_hosted_zones_manual_scan()
for hosted_zone in hosted_zones:
print(f"Searching for CloudFront Alias records in {hosted_zone['Name']}")
paginator_records = route53.get_paginator("list_resource_record_sets")
Expand Down Expand Up @@ -64,11 +64,8 @@ def route53(profile):
if __name__ == "__main__":

parser = argparse.ArgumentParser(description="Prevent Subdomain Takeover")
parser.add_argument("--profile", required=True)
args = parser.parse_args()
profile = args.profile

route53(profile)
route53()

count = len(vulnerable_domains)
my_print(f"\nTotal Vulnerable Domains Found: {str(count)}", "INFOB")
Expand Down
14 changes: 6 additions & 8 deletions manual_scans/aws/aws-alias-eb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import boto3

from utils.utils_aws import eb_susceptible
from utils.utils_aws_manual import list_hosted_zones_manual_scan
from utils.utils_dns import firewall_test
from utils.utils_dns import vulnerable_alias
Expand All @@ -13,14 +14,14 @@
missing_resources = []


def route53(profile):
def route53():

print("Searching for Route53 hosted zones")

session = boto3.Session(profile_name=profile)
session = boto3.Session()
route53 = session.client("route53")

hosted_zones = list_hosted_zones_manual_scan(profile)
hosted_zones = list_hosted_zones_manual_scan()
for hosted_zone in hosted_zones:
print(f"Searching for ElasticBeanststalk Alias records in hosted zone {hosted_zone['Name']}")
paginator_records = route53.get_paginator("list_resource_record_sets")
Expand All @@ -34,7 +35,7 @@ def route53(profile):
record_sets = [
r
for r in page_records["ResourceRecordSets"]
if "AliasTarget" in r and "elasticbeanstalk.com" in r["AliasTarget"]["DNSName"]
if "AliasTarget" in r and eb_susceptible(r["AliasTarget"]["DNSName"])
]

for record in record_sets:
Expand All @@ -52,12 +53,9 @@ def route53(profile):
if __name__ == "__main__":

parser = argparse.ArgumentParser(description="Prevent Subdomain Takeover")
parser.add_argument("--profile", required=True)
args = parser.parse_args()
profile = args.profile

firewall_test()
route53(profile)
route53()

count = len(vulnerable_domains)
my_print("\nTotal Vulnerable Domains Found: " + str(count), "INFOB")
Expand Down
Loading