Skip to content

Commit

Permalink
Merge pull request #7 from brightcove/Update
Browse files Browse the repository at this point in the history
Update 20231211
  • Loading branch information
eolvera-bc authored Dec 12, 2023
2 parents e35fccd + a16d380 commit bc60490
Show file tree
Hide file tree
Showing 33 changed files with 156 additions and 120 deletions.
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

0 comments on commit bc60490

Please sign in to comment.