T09 · Insecure Skill Coding Practices
- Location
scripts/verify_email.py:31- Finding
MX Record Injection Enables Blind Internal SMTP Access
- Content
View full analysis
Vulnerability Details
File Location:
scripts/verify_email.py:31-73
Vulnerability Type: Server-Side Request Forgery through attacker-controlled DNS records
Risk Level: MediumVulnerable Code:
python def get_mx_host(domain): """Resolve the highest-priority MX record for a domain.""" try: mx_records = dns.resolver.resolve(domain, 'MX') return str(sorted(mx_records, key=lambda x: x.preference)[0].exchange).rstrip('.') except dns.resolver.NXDOMAIN: return None except dns.resolver.NoAnswer: return None except Exception: return None def verify_email(email, helo_domain="verify.local", timeout=10): """ Verify an email address via SMTP RCPT TO. Returns dict with: email, domain, mx_host, smtp_code, smtp_response, deliverable, error deliverable values: "yes" - Server accepted the recipient (250) "no" - Server rejected the recipient (550) "catch-all" - Server accepts everything (if detected) "unknown" - Could not determine (timeout, block, greylisting, etc.) """ domain = email.split('@')[-1].lower() result = {"email": email, "domain": domain} # Step 1: MX lookup mx_host = get_mx_host(domain) if not mx_host: result["mx_host"] = None result["deliverable"] = "unknown" result["error"] = "No MX record found" return result result["mx_host"] = mx_host # Step 2: SMTP verification try: smtp = smtplib.SMTP(timeout=timeout) smtp.connect(mx_host, 25)Technical Analysis
The email domain originates from untrusted command-line, standard-input, or CSV data. The application resolves that domain's MX record and passes the resulting hostname directly to
smtplib.SMTP.connect()without validating the destination IP address.An attacker wh ...[truncated 2083 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse and validate the submitted email address with a standards-aware email parser rather than extracting the domain with
split('@'). - Resolve every MX hostname to all associated IPv4 and IPv6 addresses.
- Reject any address for which Python's
ipaddressmodule reportsis_private,is_loopback,is_link_local,is_multicast,is_reserved, oris_unspecified. - Apply an explicit network-destination allow policy where feasible.
- Prevent DNS rebinding by connecting to a validated IP address rather than asking
SMTP.connect()to resolve the hostname again. - Validate all returned addresses and define secure behavior when a hostname resolves to a mixture of public and prohibited addresses.
- Enforce equivalent restrictions at the network layer through outbound firewall or proxy rules.
- Avoid returning unnecessary raw SMTP details to untrusted callers, as these details improve internal service enumeration.
- Add tests covering loopback, RFC 1918, link-local, reserved, IPv6, mixed-address, and DNS-rebinding scenarios.
- Parse and validate the submitted email address with a standards-aware email parser rather than extracting the domain with
