Back to Checks
aws

Security group does not allow ingress from the Internet to TCP port 3389 (RDP)

ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389
Service: ec2Severity: high
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:AwsEc2SecurityGroup

EC2 security groups restrict inbound RDP on TCP 3389 to trusted sources, avoiding Internet-wide (0.0.0.0/0, ::/0) exposure.

Risk

Internet-exposed RDP enables brute force and credential stuffing and increases the chance of remote code execution via RDP flaws.

Adversaries can gain interactive access, exfiltrate data (confidentiality), tamper with systems (integrity), and trigger ransomware or service disruption (availability).

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389

Recommendation

Apply least privilege: disallow 0.0.0.0/0 and ::/0 to 3389; permit only specific IPs or private networks.

Prefer Session Manager, VPN, or a hardened bastion with MFA and just-in-time access. Use private subnets and add defense in depth with network controls and monitoring.

Remediation

CLI

aws ec2 revoke-security-group-ingress --group-id <SECURITY_GROUP_ID> --protocol tcp --port 3389 --cidr 0.0.0.0/0

Terraform

# Restrict RDP (3389) from the Internet
resource "aws_security_group" "<example_resource_name>" {
  vpc_id = "<example_resource_id>"

  ingress {
    from_port   = 3389
    to_port     = 3389
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"] # FIX: not 0.0.0.0/0; restricts RDP to internal range
  }
}

NativeIaC

# CloudFormation: restrict RDP (3389) from the Internet
Resources:
  <example_resource_name>:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Restrict RDP
      VpcId: vpc-<example_resource_id>
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: 10.0.0.0/8  # FIX: not 0.0.0.0/0; limits RDP to internal range, closing Internet access

Other

1In AWS Console, go to EC2 > Security Groups
2Select the security group attached to the instance
3In the Inbound rules tab, click Edit inbound rules
4Find any rule with Type RDP (TCP 3389) and Source 0.0.0.0/0 or ::/0
5Delete the rule or change Source to a specific trusted CIDR (e.g., your office IP)
6Click Save rules

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
from prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports import (
    ec2_securitygroup_allow_ingress_from_internet_to_all_ports,
)
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
from prowler.providers.aws.services.vpc.vpc_client import vpc_client


class ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389(Check):
    def execute(self):
        findings = []
        check_ports = [3389]
        for security_group_arn, security_group in ec2_client.security_groups.items():
            # Check if ignoring flag is set and if the VPC and the SG is in use
            if ec2_client.provider.scan_unused_services or (
                security_group.vpc_id in vpc_client.vpcs
                and vpc_client.vpcs[security_group.vpc_id].in_use
                and len(security_group.network_interfaces) > 0
            ):
                report = Check_Report_AWS(
                    metadata=self.metadata(), resource=security_group
                )
                report.resource_details = security_group.name
                report.status = "PASS"
                report.status_extended = f"Security group {security_group.name} ({security_group.id}) does not have Microsoft RDP port 3389 open to the Internet."

                # only proceed if check "..._to_all_ports" did not run or did not FAIL to avoid to report open ports twice
                if not ec2_client.is_failed_check(
                    ec2_securitygroup_allow_ingress_from_internet_to_all_ports.__name__,
                    security_group_arn,
                ):
                    # Loop through every security group's ingress rule and check it
                    for ingress_rule in security_group.ingress_rules:
                        if check_security_group(
                            ingress_rule, "tcp", check_ports, any_address=True
                        ):
                            report.status = "FAIL"
                            report.status_extended = f"Security group {security_group.name} ({security_group.id}) has Microsoft RDP port 3389 open to the Internet."
                            break
                else:
                    report.status_extended = f"Security group {security_group.name} ({security_group.id}) has all ports open to the Internet and therefore was not checked against the specific Microsoft RDP port 3389."

                findings.append(report)

        return findings