Beyond the Eye: The Reality of IDN Homograph Attacks

medium.com · Muhammad Zeeshan · 10 days ago · research
quality 7/10 · good
0 net
Tags
Beyond the Eye: The Reality of IDN Homograph Attacks | by Muhammad Zeeshan - Freedium Milestone: 20GB Reached We’ve reached 20GB of stored data — thank you for helping us grow! Patreon Ko-fi Liberapay Close < Go to the original Beyond the Eye: The Reality of IDN Homograph Attacks Imagine receiving a password reset email from paypal.com. You click the link, enter your new password, and hand it directly to an attacker… Muhammad Zeeshan Follow ~4 min read · March 24, 2026 (Updated: March 24, 2026) · Free: Yes Imagine receiving a password reset email from paypal.com . You click the link, enter your new password, and hand it directly to an attacker. The domain looked exactly right. But it was not. This is the essence of an IDN Homograph Attack one of the most psychologically sophisticated vulnerabilities in modern web security. It does not break encryption. It does not need a leaked password database. It simply exploits the gap between what computers see and what humans see. What Is an IDN Homograph Attack? IDN stands for Internationalized Domain Name a system that allows domain names to contain non-ASCII characters from scripts like Arabic, Chinese, Cyrillic, Greek, and more. Internally, browsers encode these using Punycode : apple.com -> Normal ASCII domain apple.com -> Cyrillic 'a' (U+0430) — looks identical Punycode: xn--pple-43d.com When apple.com (with a Cyrillic 'a') appears in a browser address bar, the browser has decoded the Punycode back to its Unicode visual form. The human eye cannot tell the difference. An attacker registers the Punycode domain, hosts a phishing page, and the trap is set. How the Attack Works 1. Attacker identifies target domain: paypal.com 2. Substitutes ASCII characters with Unicode lookalikes 3. Registers: paypal.com (Cyrillic 'a') -> Punycode: xn--pypal-4ve.com 4. Hosts a pixel-perfect phishing page on the spoofed domain 5. Sends the link to victims the URL passes every visual inspection 6. Victim enters credentials -> captured by the attacker This is not theoretical. Browsers historically rendered these domains in full Unicode with zero warnings. Modern browsers have partially improved, but application-level validation remains the primary weak point. Attack Scenarios Classic Credential Phishing Legitimate: https://paypal.com/login Spoofed: https://paypal.com/login <- Cyrillic 'a' A victim receives a phishing email. The URL passes visual inspection. Credentials are harvested. OAuth redirect_uri Bypass OAuth flows validate redirect_uri to ensure authorization codes are only sent to trusted servers. If the server uses loose string comparison instead of Punycode normalization: Allowlisted: https://app.example.com/callback Attacker submits: https://app.example.com/callback <- Cyrillic 'a' Server check: 'app.example.com' == 'app.example.com' -> PASSES visually Punycode: xn--pp-8cd.example.com -> routes to attacker's server Result: Authorization code delivered to attacker The authorization code — or access token — lands in the attacker's hands. One OAuth misconfiguration combined with one homograph domain equals complete account compromise. Password Reset Token Hijack (Full Account Takeover) This is the most impactful vector. The complete attack flow: 1. Attacker creates a legitimate account: [email protected] 2. Updates profile email to: [email protected] (Cyrillic 'a') 3. Application stores the new email without Punycode normalization 4. Attacker triggers "Forgot Password" for the target account 5. Reset token sent to: [email protected] -> attacker's mail server 6. Attacker resets the password -> full account takeover The application willingly hands over account access. No brute-force. No rate limiting bypass. Pure logic abuse. Magic Link and Email Verification Hijack Attacker registers: [email protected] <- IPA Latin 'g' (U+0261) App sends OTP to: [email protected] -> routes to attacker's mail server Result: Attacker gains instant authenticated session CORS Policy Bypass If CORS Origin validation uses string matching without normalization: GET /api/user/profile HTTP/1.1 Host: target.com Origin: https://trusted.com <- Cyrillic 's' (U+0455) Cookie: session=VALID_SESSION Vulnerable response: Access-Control-Allow-Origin: https://trusted.com Access-Control-Allow-Credentials: true The attacker reads authenticated API responses cross-origin. Testing Methodology Step 1: Map the Attack Surface Profile email update Account registration email field OAuth redirect_uri parameter CORS Origin header handling Webhook notification URLs Share-by-email features Step 2: Generate Homograph Candidates pip install dnstwist dnstwist --homoglyph paypal.com whois paypal.com nslookup trusted.com Step 3: Test the Password Reset ATO Flow 1. Create a test account: [email protected] 2. Update profile email to: [email protected] (IPA 'g' U+0261) 3. Trigger password reset 4. Observe: does the reset email go to the homograph domain? 5. Confirmed if yes -> report as Critical Use interactsh or Burp Collaborator as out-of-band receiver. Step 4: Test OAuth redirect_uri GET /oauth/authorize ?client_id=CLIENT_ID &redirect_uri=https://app.legitimate.com/callback &response_type=code &scope=openid profile Expected: Error redirect_uri not in allowlist Vulnerable: Redirects to homograph domain Step 5 :Test CORS GET /api/user/profile HTTP/1.1 Host: target.com Origin: https://trusted.com Cookie: session=VALID_SESSION_TOKEN Access-Control-Allow-Origin: https://trusted.com Access-Control-Allow-Credentials: true Step 6: Verify Punycode Normalization new URL('https://app.example.com').hostname // xn--pp-8cd.example.com Payload Reference Email Domain Homographs [email protected] [email protected] [email protected] [email protected] [email protected] Punycode Equivalents [email protected] (IPA g) -> [email protected] [email protected] (Cyrillic) -> [email protected] paypal.com (Cyrillic a) -> xn--pypal-4ve.com OAuth redirect_uri https://app.legitimate.com/oauth/callback https://app.legitimate.com/callback https://legitimate.com/callback CORS Origin Headers Origin: https://trusted.com Origin: https://trusted.com Origin: https://trusted.com <- Cyrillic 't' (U+0442) Mixed-Script Payloads [email protected] [email protected] [email protected] Mitigation The core fix is to normalize all domain inputs to Punycode before comparison, storage, or sending. import idna def normalize_domain(domain): try: return idna.encode(domain).decode('ascii') except idna.core.InvalidCodepoint: raise ValueError("Invalid domain — contains disallowed characters") normalize_domain('app.example.com') normalize_domain('app.example.com') Conclusion IDN Homograph attacks are a powerful reminder that security is not only about code but also about perception. They exploit trust, not logic, and can bypass even strong technical defenses when applications fail to normalize input correctly. #bug-bounty #account-takeover #cybersecurity Reporting a Problem Sometimes we have problems displaying some Medium posts. If you have a problem that some images aren't loading - try using VPN. Probably you have problem with access to Medium CDN (or fucking Cloudflare's bot detection algorithms are blocking you).