Skip to main content

Ensure No EC2 Instances Allow Ingress from the Internet to FTP Ports

Overview

This check identifies EC2 instances with security groups that allow inbound FTP traffic (TCP ports 20 and 21) from the internet. FTP is an insecure protocol that transmits data, including credentials, in plain text.

Risk

Severity: Critical

Exposing FTP ports to the internet creates serious security risks:

  • Credential theft: FTP transmits usernames and passwords in cleartext, making them easy to intercept
  • Data exposure: Attackers can read, upload, or modify files through unrestricted FTP access
  • Malware staging: Open FTP servers can be used to distribute malware
  • Brute-force attacks: Internet-exposed FTP services are constantly scanned and targeted

Remediation Steps

Prerequisites

You need permission to modify EC2 security groups. This typically requires the ec2:RevokeSecurityGroupIngress IAM permission.

Required IAM permissions
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeSecurityGroups",
"ec2:RevokeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupIngress"
],
"Resource": "*"
}
]
}

AWS Console Method

  1. Sign in to the AWS Console and go to EC2
  2. In the left sidebar, click Security Groups (under Network & Security)
  3. Find the security group attached to the affected EC2 instance
  4. Select the security group and click the Inbound rules tab
  5. Click Edit inbound rules
  6. Find any rules allowing TCP ports 20 or 21 from 0.0.0.0/0 or ::/0
  7. Either delete the rule entirely, or change the source to a specific trusted IP range
  8. Click Save rules

Important: If your application genuinely requires file transfer, consider using SFTP (port 22) or FTPS instead of FTP.

AWS CLI (optional)

First, identify the security group with exposed FTP ports:

aws ec2 describe-security-groups \
--region us-east-1 \
--query "SecurityGroups[?IpPermissions[?FromPort<=\`21\` && ToPort>=\`20\` && (IpRanges[?CidrIp=='0.0.0.0/0'] || Ipv6Ranges[?CidrIpv6=='::/0'])]].[GroupId,GroupName]" \
--output table

Remove the FTP rule allowing internet access (for port 20):

aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id <SECURITY_GROUP_ID> \
--protocol tcp \
--port 20 \
--cidr 0.0.0.0/0

Remove the FTP rule for port 21:

aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id <SECURITY_GROUP_ID> \
--protocol tcp \
--port 21 \
--cidr 0.0.0.0/0

For IPv6 rules, use:

aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id <SECURITY_GROUP_ID> \
--ip-permissions 'IpProtocol=tcp,FromPort=20,ToPort=21,Ipv6Ranges=[{CidrIpv6=::/0}]'

Replace <SECURITY_GROUP_ID> with your actual security group ID (e.g., sg-0abc123def456789).

CloudFormation (optional)

This template creates a security group without FTP internet exposure:

AWSTemplateFormatVersion: '2010-09-09'
Description: Secure security group without FTP internet access

Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID where the security group will be created

Resources:
SecureSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group without FTP internet exposure
VpcId: !Ref VpcId
SecurityGroupIngress:
# Example: Allow SSH from specific trusted IP only
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/8
Description: SSH from internal network only
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic
Tags:
- Key: Name
Value: secure-no-ftp-exposure

Outputs:
SecurityGroupId:
Description: ID of the secure security group
Value: !Ref SecureSecurityGroup

To update an existing security group, remove any SecurityGroupIngress entries that allow ports 20-21 from 0.0.0.0/0.

Terraform (optional)

This configuration creates a security group without FTP internet exposure:

terraform {
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1"
}

variable "vpc_id" {
description = "VPC ID where the security group will be created"
type = string
}

# Security group without FTP internet exposure
resource "aws_security_group" "secure_no_ftp" {
name = "secure-no-ftp-exposure"
description = "Security group without FTP internet exposure"
vpc_id = var.vpc_id

# Example: Allow SSH from internal network only
ingress {
description = "SSH from internal network"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}

egress {
description = "Allow all outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "secure-no-ftp-exposure"
}
}

output "security_group_id" {
description = "ID of the secure security group"
value = aws_security_group.secure_no_ftp.id
}

To remediate existing Terraform configurations, remove any ingress rules that allow ports 20-21 from 0.0.0.0/0 or ::/0.

Verification

After making changes, verify the FTP ports are no longer exposed:

  1. Go to EC2 > Security Groups in the AWS Console
  2. Select the modified security group
  3. Check the Inbound rules tab
  4. Confirm no rules allow TCP ports 20 or 21 from 0.0.0.0/0 or ::/0
CLI verification
aws ec2 describe-security-groups \
--region us-east-1 \
--group-ids <SECURITY_GROUP_ID> \
--query "SecurityGroups[].IpPermissions[?FromPort<=\`21\` && ToPort>=\`20\`]" \
--output json

The output should be empty ([]) or show only rules with restricted source CIDR blocks (not 0.0.0.0/0).

Additional Resources

Notes

  • FTP alternatives: If file transfer is required, use AWS Transfer Family for managed SFTP/FTPS, or configure SFTP over SSH (port 22) with proper access controls
  • Defense in depth: Consider implementing Network ACLs as an additional layer of protection
  • Monitoring: Enable VPC Flow Logs to monitor traffic patterns and detect unauthorized access attempts
  • Impact assessment: Before removing rules, ensure no legitimate applications depend on FTP access. Coordinate with application teams to migrate to secure alternatives