一个 IP 地址的七十二变:IDNA 映射如何骗过所有字符串检查
来源:acid.vegas — 2026-11-04
📋 概述
acidvegas 用一整篇实测演示 getaddrinfo 的怪癖。IPv4 地址本质是 32 位整数,inet_aton 自 4.2BSD 起就接受十进制、八进制、十六进制,以及一到四段的写法(一段是整数值,两段是 8+24 位,三段是 8+8+16 位),混合进制也合法。真正好玩的是 AI_IDN:glibc 在数值解析之前先跑 UTS #46 映射,带圈数字、上下标、LCD 分段数字、数学字体、全角句号都被折叠成 ASCII,invisible 类别的软连字符、零宽空格、变体选择符则直接被删掉。于是 ⁰𝑋⁴5.🯰3₆。⁵₂₈𝟲🯶 就是 69.30.206.130。ping 中招因为 iputils 带 IDN 支持,curl/wget 中招因为链了 libidn2。作者还用它做 URL 伪装钓鱼(user:pass@ 前缀加十六进制主机)和 wget 的参数诱饵。防御只有一条:先解析,再检查解析出来的地址。
🔑 核心要点
- IPv4 只是一串数字,点分四段只是书写习惯,一段到四段的写法至今仍被接受。
- AI_IDN 在数值解析之前执行,所以 UTS #46 的兼容折叠会先把 Unicode 折成 ASCII。
- 带圈数字、上下标、LCD 分段数字、数学字体都能映射成 0–9、a–f、x、点。
- invisible 类别的字符直接删除:软连字符、零宽空格、变体选择符在解析前就消失了。
- 只有传了 AI_IDN 才会中招;Python 不带标志就解析失败,带上 0x40 立刻成功。
- curl 的 ipv4_normalize 在 IDN 转换之前运行,所以 Unicode 主机名会带着十六进制形式出网。
- 防线唯一:先解析成地址,再校验地址本身,绝不校验字符串。
💡 金句
⁰𝑋⁴5.🯰3₆。⁵₂₈𝟲🯶 是一个合法的 IP 地址,它解析到 69.30.206.130,这真的很离谱。
👍 0
👎 0
← 返回 Lobsters 首页