[하루한줄] CVE-2025-5277: AWS MCP Server의 검증 부족으로 인한 Command Injection 취약점

URL

https://github.com/advisories/GHSA-m4qw-j7mx-qv6h

Target

  • AWS MCP server

Explain

AI가 AWS CLI 명령어를 실행할 수 있도록 지원하는 AWS MCP 서버 프로젝트에서 발견된 Command Injection 취약점의 세부 정보가 공개되었습니다.

취약점은 클라이언트의 프롬프트를 통해 전달 혹은 생성되는 명령어가 execute_aws_command(), execute_pipe_command() 등의 함수를 통해 실행되기 전 검증을 수행하는 validate_aws_command() 함수에 존재합니다.

def validate_aws_command(command: str) -> None:
    """Validate that the command is a proper AWS CLI command.

    Args:
        command: The AWS CLI command to validate

    Raises:
        CommandValidationError: If the command is invalid
    """
    cmd_parts = shlex.split(command)
    if not cmd_parts or cmd_parts[0].lower() != "aws":
        raise CommandValidationError("Commands must start with 'aws'")

    if len(cmd_parts) < 2:
        raise CommandValidationError("Command must include an AWS service (e.g., aws s3)")

    # Optional: Add a deny list for potentially dangerous commands
    dangerous_commands = ["aws iam create-user", "aws iam create-access-key", "aws ec2 terminate-instances", "aws rds delete-db-instance"]
    if any(command.startswith(dangerous_cmd) for dangerous_cmd in dangerous_commands):
        raise CommandValidationError("This command is restricted for security reasons")

해당 함수는 다음 검증을 수행합니다.

  1. cmd_parts[0].lower() != "aws" : aws로 시작하는지 확인
  2. len(cmd_parts) < 2 : 두 번째 인자가 있는지 확인
  3. command.startswith(dangerous_cmd): 특정 위험한 명령어에 대한 deny list 검증

그러나 해당 함수에는 Command Injection에 악용될 수 있는 쉘 메타문자나, root, admin과 관련된 민감한 명령어가 포함되어 있는지 검사하지 않습니다.

async def execute_aws_command(command: str, timeout: int | None = None) -> CommandResult:
    """Execute an AWS CLI command and return the result.
    ...
    """
    # ...
    # Validate the command
    validate_aws_command(command)

    # ...
    try:
        # Split command safely for exec
        cmd_parts = shlex.split(command)
        # Create subprocess using exec (safer than shell=True)
        process = await asyncio.create_subprocess_exec(*cmd_parts, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
        # ...

validate_aws_command를 통해 명령어를 검증한 이후에는 shlex.split을 통해 공백을 기준으로 명령어를 분리한 후 create_subprocess_exec로 명령어를 실행하는데, shell=True를 사용하지 않고 *cmd_parts 를 직접 전달하는 방식으로 인해 쉘 메타 문자가 포함된 명령어를 쉘이 해석하는 과정에서 임의의 명령어 삽입이 발생합니다. 이를 악용하는 공격자는 root, admin 등 민감한 유저에 대한 정보 유출, 시스템 설정 파일 유출 등이 가능합니다.

def validate_aws_command(command: str) -> None:
    """Validate that the command is a proper AWS CLI command.
    ...
    """
#...
    # Check regex rules first (these apply regardless of service)
    error_message = check_regex_rules(command, service) 
    if error_message:
        raise ValueError(error_message)

    # Check against dangerous commands for this service
    if service in SECURITY_CONFIG.dangerous_commands:
        # Check each dangerous command pattern
        for dangerous_cmd in SECURITY_CONFIG.dangerous_commands[service]:
            if command.startswith(dangerous_cmd):
                # If it's a dangerous command, check if it's also in safe patterns
                if is_service_command_safe(command, service): 
                    return  # Command is safe despite matching dangerous pattern

                # Command is dangerous, raise an error
                raise ValueError(
                    f"This command ({dangerous_cmd}) is restricted for security reasons. "
                    f"Please use a more specific, read-only command or add 'help' or '--help' to see available options."
                )

    logger.debug(f"Command validation successful: {command}")

#... 

def check_regex_rules(command: str, service: Optional[str] = None) -> Optional[str]:More actions
    """Check command against regex rules.

    Args:
        command: The command to check
        service: The AWS service being used, if known

    Returns:
        Error message if command matches a regex rule, None otherwise
    """
    # Check general rules that apply to all commands
    if "general" in SECURITY_CONFIG.regex_rules:
        for rule in SECURITY_CONFIG.regex_rules["general"]:
            pattern = re.compile(rule.pattern)
            if pattern.search(command):
                logger.warning(f"Command matches regex rule: {rule.description}")
                return rule.error_message

    # Check service-specific rules if service is provided
    if service and service in SECURITY_CONFIG.regex_rules:
        for rule in SECURITY_CONFIG.regex_rules[service]:
            pattern = re.compile(rule.pattern)
            if pattern.search(command):
                logger.warning(f"Command matches service-specific regex rule for {service}: {rule.description}")
                return rule.error_message

    return None

패치는 기존에 validate_aws_command() 함수 내부에서 쓰였던 dangerous_command 리스트를 config로 분리함해 확장 가능하도록 변경한 동시에 검증할 명령어를 추가하였으며, 정규식을 사용한 검증을 수행하는 check_regex_rules 함수 또한 추가해 검증을 강화하였습니다. 아래는 정규식 검증과 dangerous_command 리스트를 포함하는 config 파일입니다.

  • SECURITY_CONFIG (example)
#...
# Commands considered dangerous by security category
# Keys are AWS service names, values are lists of command prefixes to block
dangerous_commands:
  # Identity and Access Management - core of security
  iam:
    # User management (potential backdoor accounts)
    - "aws iam create-user"              # Creates new IAM users that could persist after compromise
    - "aws iam update-user"              # Updates existing user properties
    
    # Credential management (theft risk)
    - "aws iam create-access-key"        # Creates long-term credentials that can be exfiltrated
    - "aws iam update-access-key"        # Changes status of access keys (enabling/disabling)
    - "aws iam create-login-profile"     # Creates console passwords for existing users
    - "aws iam update-login-profile"     # Updates console passwords
    
    # Authentication controls
    - "aws iam create-virtual-mfa-device" # Creates new MFA devices
    - "aws iam deactivate-mfa-device"    # Removes MFA protection from accounts
    - "aws iam delete-virtual-mfa-device" # Deletes MFA devices
    - "aws iam enable-mfa-device"        # Enables/associates MFA devices
    
    # Privilege escalation via policy manipulation
    - "aws iam attach-user-policy"       # Attaches managed policies to users
    - "aws iam attach-role-policy"       # Attaches managed policies to roles
    - "aws iam attach-group-policy"      # Attaches managed policies to groups
    - "aws iam create-policy"            # Creates new managed policies
    - "aws iam create-policy-version"    # Creates new versions of managed policies
    - "aws iam set-default-policy-version" # Changes active policy version
    
    # Inline policy manipulation (harder to detect)
    - "aws iam put-user-policy"          # Creates/updates inline policies for users
    - "aws iam put-role-policy"          # Creates/updates inline policies for roles
    - "aws iam put-group-policy"         # Creates/updates inline policies for groups
    
    # Trust relationship manipulation
    - "aws iam update-assume-role-policy" # Changes who can assume a role
    - "aws iam update-role"              # Updates role properties
  
  # Security Token Service - temporary credentials
  sts:
    - "aws sts assume-role"              # Assumes roles with potentially higher privileges
    - "aws sts get-federation-token"     # Gets federated access tokens
  
  # AWS Organizations - multi-account management
  organizations:
    - "aws organizations create-account"  # Creates new AWS accounts
    - "aws organizations invite-account-to-organization" # Brings accounts under org control
    - "aws organizations leave-organization" # Removes accounts from organization
    - "aws organizations remove-account-from-organization" # Removes accounts from organization
    - "aws organizations disable-policy-type" # Disables policy enforcement
    - "aws organizations create-policy"   # Creates organization policies
    - "aws organizations attach-policy"   # Attaches organization policies

  # ---------------------------------------------------------------------------------
  # 🔍 Audit and Logging Security Rules
  # ---------------------------------------------------------------------------------
  # These rules prevent attackers from covering their tracks by:
  # - Disabling or deleting audit logs (CloudTrail)
  # - Turning off compliance monitoring (Config)
  # - Disabling threat detection (GuardDuty)
  # - Removing alarm systems (CloudWatch)
  # ---------------------------------------------------------------------------------
  
  # CloudTrail - AWS activity logging
  cloudtrail:
    - "aws cloudtrail delete-trail"       # Removes audit trail completely
    - "aws cloudtrail stop-logging"       # Stops collecting audit logs
    - "aws cloudtrail update-trail"       # Modifies logging settings (e.g., disabling logging)
    - "aws cloudtrail put-event-selectors" # Changes what events are logged
    - "aws cloudtrail delete-event-data-store" # Deletes storage for CloudTrail events
  
  # AWS Config - configuration monitoring
  config:
    - "aws configservice delete-configuration-recorder" # Removes configuration tracking
    - "aws configservice stop-configuration-recorder"   # Stops recording configuration changes
    - "aws configservice delete-delivery-channel"       # Stops delivering configuration snapshots
    - "aws configservice delete-remediation-configuration" # Removes auto-remediation
  
  # GuardDuty - threat detection
  guardduty:
    - "aws guardduty delete-detector"     # Disables threat detection completely
    - "aws guardduty disable-organization-admin-account" # Disables central security
    - "aws guardduty update-detector"     # Modifies threat detection settings
  
  # CloudWatch - monitoring and alerting
  cloudwatch:
    - "aws cloudwatch delete-alarms"     # Removes security alarm configurations
    - "aws cloudwatch disable-alarm-actions" # Disables alarm action triggers
    - "aws cloudwatch delete-dashboards" # Removes monitoring dashboards

# Complex pattern matching using regular expressions
regex_rules:
  # Global security patterns (apply to all services)
  general:
    # Identity and authentication risks
    - pattern: "aws .* --profile\\s+(root|admin|administrator)"
      description: "Prevent use of sensitive profiles"
      error_message: "Using sensitive profiles (root, admin) is restricted for security reasons."
    
    # Protocol security risks
    - pattern: "aws .* --no-verify-ssl"
      description: "Prevent disabling SSL verification"
      error_message: "Disabling SSL verification is not allowed for security reasons."
    
    # Data exposure risks
    - pattern: "aws .* --output\\s+text\\s+.*--query\\s+.*Password"
      description: "Prevent password exposure in text output"
      error_message: "Outputting sensitive data like passwords in text format is restricted."
    
    # Debug mode risks
    - pattern: "aws .* --debug"
      description: "Prevent debug mode which shows sensitive info"
      error_message: "Debug mode is restricted as it may expose sensitive information."

  # IAM-specific security patterns
  iam:
    # Privileged user creation
    - pattern: "aws iam create-user.*--user-name\\s+(root|admin|administrator|backup|security|finance|billing)"
      description: "Prevent creation of privileged-sounding users"
      error_message: "Creating users with sensitive names is restricted for security reasons."
    
    # Privilege escalation via policies
    - pattern: "aws iam attach-user-policy.*--policy-arn\\s+.*Administrator"
      description: "Prevent attaching Administrator policies"
      error_message: "Attaching Administrator policies is restricted for security reasons."
    
    - pattern: "aws iam attach-user-policy.*--policy-arn\\s+.*FullAccess"
      description: "Prevent attaching FullAccess policies to users"
      error_message: "Attaching FullAccess policies directly to users is restricted (use roles instead)."
    
    # Unrestricted permissions in policies
    - pattern: "aws iam create-policy.*\"Effect\":\\s*\"Allow\".*\"Action\":\\s*\"\*\".*\"Resource\":\\s*\"\*\""
      description: "Prevent creation of policies with * permissions"
      error_message: "Creating policies with unrestricted (*) permissions is not allowed."
    
    # Password policy weakening
    - pattern: "aws iam create-login-profile.*--password-reset-required\\s+false"
      description: "Enforce password reset for new profiles"
      error_message: "Creating login profiles without requiring password reset is restricted."
    
    - pattern: "aws iam update-account-password-policy.*--require-uppercase-characters\\s+false"
      description: "Prevent weakening password policies"
      error_message: "Weakening account password policies is restricted."

  # S3 security patterns
  s3:
    # Public bucket exposure
    - pattern: "aws s3api put-bucket-policy.*\"Effect\":\\s*\"Allow\".*\"Principal\":\\s*\"\*\""
      description: "Prevent public bucket policies"
      error_message: "Creating public bucket policies is restricted for security reasons."
    
    # Disabling public access blocks
    - pattern: "aws s3api put-public-access-block.*--public-access-block-configuration\\s+.*\"BlockPublicAcls\":\\s*false"
      description: "Prevent disabling public access blocks"
      error_message: "Disabling S3 public access blocks is restricted for security reasons."
    
    # Public bucket creation outside approved regions
    - pattern: "aws s3api create-bucket.*--region\\s+(?!eu|us-east-1).*--acl\\s+public"
      description: "Prevent public buckets outside of allowed regions"
      error_message: "Creating public buckets outside allowed regions is restricted."

  # EC2 network security patterns
  ec2:
    # Open security groups for sensitive ports
    - pattern: "aws ec2 authorize-security-group-ingress.*--cidr\\s+0\\.0\\.0\\.0/0.*--port\\s+(?!80|443)[0-9]+"
      description: "Prevent open security groups for non-web ports"
      error_message: "Opening non-web ports to the entire internet (0.0.0.0/0) is restricted."
    
    # Unsafe user-data scripts
    - pattern: "aws ec2 run-instances.*--user-data\\s+.*curl.*\\|.*sh"
      description: "Detect potentially unsafe user-data scripts"
      error_message: "Running scripts from remote sources in user-data presents security risks."

  # CloudTrail integrity patterns
  cloudtrail:
    # Disabling global event logging
    - pattern: "aws cloudtrail update-trail.*--no-include-global-service-events"
      description: "Prevent disabling global event logging"
      error_message: "Disabling CloudTrail logging for global service events is restricted."
    
    # Making trails single-region
    - pattern: "aws cloudtrail update-trail.*--no-multi-region"
      description: "Prevent making trails single-region"
      error_message: "Changing CloudTrail trails from multi-region to single-region is restricted."