Beyond the Eye: The Reality of IDN Homograph Attacks
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).