fix(elbv2 desync check): Mixed elbv2 desync and smuggling (#2171)

This commit is contained in:
Nacho Rivera
2023-04-05 11:36:06 +02:00
committed by GitHub
parent ce4fadc378
commit dcb893e230
10 changed files with 77 additions and 223 deletions

View File

@@ -137,7 +137,6 @@ checks_v3_to_v2_mapping = {
"elbv2_internet_facing": "extra79",
"elbv2_listeners_underneath": "extra7158",
"elbv2_logging_enabled": "extra717",
"elbv2_request_smugling": "extra7142",
"elbv2_ssl_listeners": "extra793",
"elbv2_waf_acl_attached": "extra7129",
"emr_cluster_account_public_block_enabled": "extra7178",

2
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand.
[[package]]
name = "about-time"

View File

@@ -1,7 +1,7 @@
{
"Provider": "aws",
"CheckID": "elbv2_desync_mitigation_mode",
"CheckTitle": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"CheckTitle": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode, if not check if at least is configured with the drop_invalid_header_fields attribute",
"CheckType": [
"Data Protection"
],
@@ -10,7 +10,7 @@
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"Description": "Check whether the Application Load Balancer is configured with defensive or strictest desync mitigation mode, if not check if at least is configured with the drop_invalid_header_fields attribute",
"Risk": "HTTP Desync issues can lead to request smuggling and make your applications vulnerable to request queue or cache poisoning; which could lead to credential hijacking or execution of unauthorized commands.",
"RelatedUrl": "",
"Remediation": {
@@ -21,7 +21,7 @@
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure Application Load Balancer is configured with defensive or strictest desync mitigation mode.",
"Text": "Ensure Application Load Balancer is configured with defensive or strictest desync mitigation mode or with the drop_invalid_header_fields attribute enabled",
"Url": "https://aws.amazon.com/about-aws/whats-new/2020/08/application-and-classic-load-balancers-adding-defense-in-depth-with-introduction-of-desync-mitigation-mode/"
}
},

View File

@@ -15,9 +15,11 @@ class elbv2_desync_mitigation_mode(Check):
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is configured with correct desync mitigation mode."
if lb.desync_mitigation_mode == "monitor":
report.status = "FAIL"
report.status_extended = f"ELBv2 ALB {lb.name} does not have desync mitigation mode set as defensive or strictest."
if lb.drop_invalid_header_fields == "false":
report.status = "FAIL"
report.status_extended = f"ELBv2 ALB {lb.name} does not have desync mitigation mode set as defensive or strictest and is not dropping invalid header fields"
elif lb.drop_invalid_header_fields == "true":
report.status_extended = f"ELBv2 ALB {lb.name} does not have desync mitigation mode set as defensive or strictest but is dropping invalid header fields"
findings.append(report)
return findings

View File

@@ -1,32 +0,0 @@
{
"Provider": "aws",
"CheckID": "elbv2_request_smugling",
"CheckTitle": "Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling.",
"CheckType": [
"Data Protection"
],
"ServiceName": "elbv2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
"Description": "Check if Application Load Balancer is dropping invalid packets to prevent header based HTTP request smuggling.",
"Risk": "ALB can be target of actors sending bad HTTP headers.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "aws elbv2 modify-load-balancer-attributes --load-balancer-arn <lb_arn> --attributes Key=routing.http.drop_invalid_header_fields.enabled,Value=true",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/ELBv2/drop-invalid-header-fields-enabled.html",
"Terraform": "https://docs.bridgecrew.io/docs/ensure-that-alb-drops-http-headers#terraform"
},
"Recommendation": {
"Text": "Ensure Application Load Balancer is configured for HTTP headers with header fields that are not valid are removed by the load balancer (true).",
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#deletion-protection"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -1,27 +0,0 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.elbv2.elbv2_client import elbv2_client
class elbv2_request_smugling(Check):
def execute(self):
findings = []
for lb in elbv2_client.loadbalancersv2:
if lb.type == "application":
report = Check_Report_AWS(self.metadata())
report.region = lb.region
report.resource_id = lb.name
report.resource_arn = lb.arn
report.resource_tags = lb.tags
report.status = "FAIL"
report.status_extended = (
f"ELBv2 ALB {lb.name} is not dropping invalid header fields."
)
if lb.drop_invalid_header_fields == "true":
report.status = "PASS"
report.status_extended = (
f"ELBv2 ALB {lb.name} is dropping invalid header fields."
)
findings.append(report)
return findings

View File

@@ -11,7 +11,6 @@ AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_desync_mitigation_mode:
@mock_elbv2
def test_elb_no_balancers(self):
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
@@ -33,7 +32,7 @@ class Test_elbv2_desync_mitigation_mode:
@mock_ec2
@mock_elbv2
def test_elbv2_without_desync_mitigation_mode(self):
def test_elbv2_without_desync_mitigation_mode_and_not_dropping_headers(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
@@ -60,6 +59,10 @@ class Test_elbv2_desync_mitigation_mode:
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "routing.http.desync_mitigation_mode", "Value": "monitor"},
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "false",
},
],
)
@@ -82,7 +85,68 @@ class Test_elbv2_desync_mitigation_mode:
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"does not have desync mitigation mode set as defensive or strictest",
"does not have desync mitigation mode set as defensive or strictest and is not dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_without_desync_mitigation_mode_but_dropping_headers(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{"Key": "routing.http.desync_mitigation_mode", "Value": "monitor"},
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "true",
},
],
)
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode.elbv2_client",
new=ELBv2(current_audit_info),
):
from prowler.providers.aws.services.elbv2.elbv2_desync_mitigation_mode.elbv2_desync_mitigation_mode import (
elbv2_desync_mitigation_mode,
)
check = elbv2_desync_mitigation_mode()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"does not have desync mitigation mode set as defensive or strictest but is dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"

View File

@@ -8,10 +8,9 @@ AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_request_smugling:
class Test_elbv2_internet_facing:
@mock_elbv2
def test_elb_no_balancers(self):
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2

View File

@@ -1,151 +0,0 @@
from re import search
from unittest import mock
from boto3 import client, resource
from moto import mock_ec2, mock_elbv2
AWS_REGION = "eu-west-1"
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_elbv2_request_smugling:
@mock_elbv2
def test_elb_no_balancers(self):
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 0
@mock_ec2
@mock_elbv2
def test_elbv2_without_dropping(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
Type="application",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "false",
},
],
)
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
from prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"is not dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]
@mock_ec2
@mock_elbv2
def test_elbv2_with_dropping(self):
conn = client("elbv2", region_name=AWS_REGION)
ec2 = resource("ec2", region_name=AWS_REGION)
security_group = ec2.create_security_group(
GroupName="a-security-group", Description="First One"
)
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
subnet1 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
)
subnet2 = ec2.create_subnet(
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
)
lb = conn.create_load_balancer(
Name="my-lb",
Subnets=[subnet1.id, subnet2.id],
SecurityGroups=[security_group.id],
Scheme="internal",
)["LoadBalancers"][0]
conn.modify_load_balancer_attributes(
LoadBalancerArn=lb["LoadBalancerArn"],
Attributes=[
{
"Key": "routing.http.drop_invalid_header_fields.enabled",
"Value": "true",
},
],
)
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.elbv2.elbv2_service import ELBv2
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling.elbv2_client",
new=ELBv2(current_audit_info),
):
from prowler.providers.aws.services.elbv2.elbv2_request_smugling.elbv2_request_smugling import (
elbv2_request_smugling,
)
check = elbv2_request_smugling()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"is dropping invalid header fields",
result[0].status_extended,
)
assert result[0].resource_id == "my-lb"
assert result[0].resource_arn == lb["LoadBalancerArn"]