▸ 如何查找仿冒抢注域名
生成近似域名是容易的那一半。真正要紧的另一半,是弄清楚其中哪些确实被人注册了、哪些构成真实威胁。这里给出全部可行方法,以及配套命令与分级规则。
▸ 简短回答
先生成品牌名的各种排列,然后拿它们去比对一份已知存在的域名库,而不是为每个候选发一次 DNS 查询。对剩下的结果按三个字段排序:MX 记录、A 记录、TLS 证书。这三项能把值得处理的少数几个,与几百个只是停放着的字符串区分开。
- ▸ 只是给自家品牌做一次审计?dnstwist 加一次域名库比对。一个下午,不用预算。
- ▸ 需要持续检测?用你的排列列表筛查每日新域名数据集,同时跟读证书透明度日志。要在注册当天发现它,而不是在它被用来攻击你的那天。
- ▸ 要决定升级处理哪一个?从自己的解析日志入手。组织内部从未解析过的近似域名,优先级低于上周被十个人访问过的那个。
| 方法 | 能发现什么 | 速度 | 成本 | 投入 |
|---|---|---|---|---|
| 生成排列(dnstwist、urlcrazy) | 已知名称的经典拼写错误 | 按需运行 | 免费 | 中 |
| 检索已注册域名库 | 任何含有品牌字符串的已注册域名,含组合抢注 | 即时 | 免费 | 低 |
| 证书透明度监测 | 近似域名签发 TLS 证书后数秒内即可发现 | 数秒至数分钟 | 免费 | 中 |
| 筛查每日新域名数据集 | 昨天注册的全部域名,按你自己的规则过滤 | 每天 | 免费 | 中 |
| 自有解析日志 + 被动 DNS | 员工和客户实际访问到的那些近似域名 | 数小时 | 免费至付费 | 中 |
| 商业品牌保护服务 | 覆盖面广,并代为执行下架 | 每天 | 付费 | 低 |
| TMCH 商标提示 | 新 gTLD 中完全一致的注册,在注册当刻提示 | 注册时 | 付费(按年) | 低 |
▸ 仿冒抢注,以及另外四种被叫作仿冒抢注的东西
严格意义上的仿冒抢注,是注册一个别人打错字就会进入的域名。实际使用中,五个不同类别被归进同一个词,而它们需要不同的检测手段 - 排列生成器只能找到前两类,对其余的完全无感:
- 1. 拼写抢注 - 键盘距离造成的错误。
exapmle.com、exmple.com、examlpe.com。排列生成即可找到。 - 2. 后缀抢注 - 同一字符串换个后缀。
example.co、example.cm、example.app。同样属于排列,但 TLD 列表要你自己提供。 - 3. 组合抢注 - 品牌拼写完全正确,再加一个词。
example-login.com、secure-example.net、example-support.co。这是真实钓鱼中最常见的一类,任何拼写错误生成器都造不出来,只有在已注册域名中做子串检索才能找到。 - 4. 同形字 / IDN 仿冒 - 显示上完全一样的字符。用西里尔字母
a冒充拉丁字母a、用rn冒充m、用1冒充l。它们以 punycode(xn--)形式存储,所以逐字节比对永远发现不了。 - 5. 比特抢注 - 名称在内存或传输中被翻转了一个比特。
fxample.com之于example.com。少见但真实存在,生成成本也很低。
evil.com,然后提供 example.com.login.evil.com。任何注册监测都找不到它,因为没有新域名被注册。只有证书透明度和你自己的日志分析能发现。▸ 生成候选集合
dnstwist 是参考实现,也是几乎所有人最后都会用的工具。它一次性套用所有排列类别 - 漏字、插字、重复、换位、相邻按键替换、元音互换、加连字符、同形字、比特翻转 - 并可选地检查哪些候选能够解析。urlcrazy 随 Kali 附带,用不同的词表做同一件事。
三种运行方式,由省事到彻底:
# 1. Permutations only, no network - this is your candidate list dnstwist --format list example.com > candidates.txt wc -l candidates.txt # 2. Same sweep, but resolve as it goes and keep only what exists dnstwist --registered --format csv example.com > registered.csv # 3. The two fields that decide priority later: MX, and a fuzzy hash of the # live page compared against the real one (--ssdeep on older builds) dnstwist --registered --mx --lsh ssdeep --format json example.com > registered.json # 4. Cross the brand with a TLD dictionary, not just the suffix you own printf '%s\n' com net org co io app shop online site xyz > tlds.txt dnstwist --tld tlds.txt --format list example.com | sort -u > candidates.txt
像 example 这样一个词,在加上 TLD 之前大约会产生一千到三千条排列。再与一份 TLD 列表相乘,候选量就到了几万条 - 这正是下一步不能是“逐个解析”的原因。
example.com 做排列会连 .com 一起排列,那是噪声;对 example 做排列,再与你自己挑的 TLD 列表相乘,得到的集合才是可以推敲的。挑你所在行业真正被滥用的那些 TLD,而不是全部 1400 个。▸ 把候选变成已确认的注册
现在你手里有几千个字符串,却不知道哪些真的存在。有四种查证方式,其中三种比看上去更糟:
- ▸ DNS 解析 - 快且免费,但它只能证明该名称有 A 或 NS 记录。已注册但没有配置名称服务器的域名解析不出任何结果,你会漏掉它;而且负缓存会在 SOA 最小值的时长内持续返回 NXDOMAIN。
- ▸ 逐个候选查 RDAP 或 WHOIS - 权威,但各注册局的速率限制严到让两万条候选变成好几天的活。只用在候选短名单上,绝不要用在整个候选集合上。
- ▸ 在区域文件里搜索 - 对你拿到 CZDS 批准的那些 gTLD 是准确的,每个区域好几个 GB,而且对几乎所有 ccTLD 都是盲区。
- ▸ 到现成的域名库里去查 - 一次查询代替几千次,没有速率限制,还能覆盖你拿不到区域文件的 TLD。下一节讲的就是这个。
Example.CO.UK. 和 example.co.uk 是同一条发现;靠数点号来判断可注册部分,正是 foo.co.uk 被解析错的原因。▸ 检索已注册域名库
这里就是你正在浏览的站点,请据此权衡本节内容。它之所以属于这份清单,是因为问题的形状恰好如此:DNSniffer 把 gTLD 区域文件和覆盖所有 TLD 的证书透明度数据,汇入同一张可检索的域名表 - 目前收录 358,047,798 个活跃域名,覆盖 9,957 个 TLD,搜索接口公开、无需账号,并且支持通配符。于是问题从“这个候选存在吗”问两万遍,变成“有哪些长得像它的东西存在”只问一次。
通配符是这里的关键:* 匹配任意长度的字符,所以 exam*le 一次查询就能把词中间的插入、删除和替换全都覆盖到。
# 1. Every indexed domain containing your brand string, any TLD
curl -s 'https://dnsniffer.com/api/v1/domains?q=example&limit=1000' \
| jq -r '.data[].name'
# 2. "*" is a wildcard, so one query covers a missing, extra or swapped
# character anywhere in the middle of the token
curl -s 'https://dnsniffer.com/api/v1/domains?q=exam*le&limit=1000' \
| jq -r '.data[] | [.name, .tld, .cert_issuer.organization // "-"] | @tsv'
# 3. Narrow to one TLD
curl -s 'https://dnsniffer.com/api/v1/domains?q=example&tld=com&limit=1000'
# 4. Screen today's new domains against the candidate list from step 1
URL=$(curl -s 'https://dnsniffer.com/api/v1/datasets?category=newly_detected' \
| jq -r '.data[] | select(.periodicity=="daily") | .download_url')
curl -sL "https://dnsniffer.com$URL" -o nrd-daily.zip
unzip -p nrd-daily.zip | tail -n +2 | cut -d, -f1 | sort -u > today.txt
comm -12 today.txt <(sort -u candidates.txt) > hits.txt在依赖它之前要知道两个边界。这份数据由观测构成 - 区域委派与证书签发 - 所以一个既未委派、也从未申请过证书的已注册域名不在其中。另外,命中只说明该名称存在,并不说明它何时注册;凡是你要采取行动的对象,都用 RDAP 复核。
如果需要的是持续检测而不是一次性审计,有两样东西在服务端运行。监测为每个账号保留最多十条子串关注,新收录的域名一旦匹配就发邮件摘要。新发现域名导出把过去 24 小时内收录的全部域名做成 CSV,你的排列列表正好拿它来筛。最新域名页面则是同一条数据流的浏览器视图。
example-login.com,却会漏掉 exarnple.com,因为后者并不包含你的字符串。关注覆盖的是组合抢注那一类;拼写错误那一类,仍然要靠你生成的列表去筛每日数据集。▸ 在证书签发的那一刻抓住它们
近似域名一旦开始提供页面就变得危险,而如今几乎所有钓鱼页面都走 HTTPS - 也就意味着有证书,也就意味着签发后数秒内会出现一条公开的证书透明度记录。CT 是本页唯一能在第一个受害者到达之前就告诉你的方法,也是唯一能看见子域名仿冒的方法。
两种接入方式:接一个聚合后的实时流,或者按 RFC 6962 接口直接读日志。无论哪种,过滤器都由你自己写,而模糊匹配就应该放在过滤器里 - 你面对的是一条名称数据流,而不是在检验一份固定的候选清单。
# Aggregated firehose, filtered to names close to your brand
websocat wss://certstream.calidog.io \
| jq -r '.data.leaf_cert.all_domains[]?' \
| sed 's/^\*\.//' \
| python3 -c '
import sys, unicodedata, idna
from rapidfuzz.distance import Levenshtein
BRAND = "example"
def decode(name):
try:
return idna.decode(name) if "xn--" in name else name
except Exception:
return name
def fold(s):
# Cheap confusable folding. A real one uses the Unicode confusables table.
s = unicodedata.normalize("NFKD", s)
for a, b in (("rn", "m"), ("vv", "w"), ("1", "l"), ("0", "o"), ("5", "s")):
s = s.replace(a, b)
return s
for line in sys.stdin:
name = line.strip().lower()
label = fold(decode(name)).split(".")[0]
if BRAND in label: # combosquat
print("substring", name, flush=True)
elif Levenshtein.distance(label, BRAND) <= 2: # typo / homoglyph
print("distance ", name, flush=True)
'证书带有 SAN,所以一张证书会产生很多名称。先把每个都归约到可注册域名,然后跑三种检测,因为每一种都看不见另外两种能抓到的东西:与品牌词的编辑距离不超过 2 能找出拼写错误那一类,朴素的子串匹配能找出组合抢注那一类,而先解码 xn-- 标签再做易混淆字符归一化,才能找出同形字那一类。
▸ 先读自己的 DNS 日志
上面所有方法找的是存在的近似域名。你的解析日志找的是真正起作用的近似域名,那是一份短得多、也紧急得多的清单。对 DNS 或代理日志做两次查询,价值超过大多数付费数据源:
- ▸ 与自家域名只差一点的 NXDOMAIN 应答。那是你自己的员工和客户在打错字。其中每一个名字,别人明天就能注册下来,从此开始收走投递错的邮件。把这份清单的头部自己注册掉,是本页最便宜的一项措施。
- ▸ 成功解析了与你相近、但不属于你的名称。此刻就有人在你的网络里访问一个近似域名。那是安全事件,不是监测发现。
# Unbound / BIND query log -> names your users typed that do not exist,
# ranked by how many times they typed them
grep NXDOMAIN /var/log/unbound/query.log \
| awk '{print tolower($NF)}' \
| python3 -c '
import sys
from rapidfuzz.distance import Levenshtein
for name in sys.stdin:
name = name.strip().rstrip(".")
label = name.split(".")[0]
if 0 < Levenshtein.distance(label, "example") <= 2:
print(name)
' | sort | uniq -c | sort -rn | head -20被动 DNS 把同样的思路延伸到你的边界之外:服务商记录合作递归解析器观测到的解析行为,为每个名称给出首次出现时间。它适合用来确定某个近似域名何时上线,同时先天带有偏差 - 你只能看到他们传感器网络看到的部分。
▸ 付费服务、下架与仲裁
品牌保护服务商 - MarkMonitor、CSC、ZeroFox、Bolster 以及一长串其他公司 - 把近似域名监测和下架执行打包出售。监测并不是难的那部分,上面五节已经能复现其中大半。你买的是后半部分:与注册商和主机商长期建立的联系,以及提交并持续追进的滥用举报。如果你的团队里没人能腾出一个周二下午,去和另一个法域的注册商掰扯,那这笔钱是值的。
确认一个域名确实在作恶之后,有四条路,由快且便宜的排起:
- 1. 向注册商和主机商举报滥用 - 免费,而且对正在运行的钓鱼站点是最快的一条路。把证据同时发给注册商的滥用联系人和主机商:URL、截图、时间戳,以及它在冒充谁。一个正在窃取凭据的页面可能几小时内就被拿下;一个只是停放着的近似域名则通常被忽略。
- 2. 提交到拦截名单 - Google 安全浏览、APWG、PhishTank,以及你自己的 EDR 或 DNS 过滤。这能在几分钟内保护你的用户,而其余流程都按法律的节奏走。
- 3. URS - 约 375 美元起,数周内出结果。它只在域名剩余注册期内将其暂停,并不会转让给你,而且要求清楚而有说服力的证据。适合证据确凿的钓鱼,对任何存在争辩空间的情形都没用。
- 4. UDRP - 独任专家组、覆盖少量域名的案子,机构费用约 1500 美元,另加律师费和大约两个月时间。它会把域名转让给你。你必须同时证明三点:与你的商标构成混淆性近似、持有人对该域名没有正当权益、以及恶意注册并使用。
example-login 不会触发。▸ 给发现结果排序
对一个有知名度的品牌做一轮排列扫描,通常会返回几百个已注册的近似域名。它们绝大多数只是停放着,而挨个去追正是品牌保护项目走向死亡的方式。按“是否在实际运营”的证据排序,信号最强的排在前面:
- ▸ 存在 MX 记录。该域名能收邮件。这一项每次都排在最前 - 发票诈骗和商业邮件入侵正是这样开始的,而且完全不需要网站。
- ▸ A 记录指向一个活着的 HTTP 服务。把它抓下来看。注册商的停放页是噪声;一份你登录表单的复制品是安全事件。
- ▸ 已签发证书。说明有人做过配置。免费 DV 证书让这一项单独看时证据很弱,与 A 记录合起来看时证据很强。
- ▸ 页面相似度。把渲染后页面的模糊哈希,或者干脆只把 favicon 的哈希,与你自己的做比较。
dnstwist --lsh能帮你做前者;favicon 哈希比对很粗糙,却出奇地有效。 - ▸ 注册时间与注册商。上周刚在一家滥用记录不佳的注册商处注册,与某人从 2013 年起一直持有,是完全不同的风险。到这一步,RDAP 复核才值得去承受它的速率限制。
- ▸ 被你在意的人解析过。与自己的解析日志做交叉比对。这会把一条监测命中变成一张带有影响范围的工单。
# Rank confirmed lookalikes by how operational they are
while read -r d; do
a=$(dig +short A "$d" | head -1)
mx=$(dig +short MX "$d" | head -1)
[ -n "$a$mx" ] && printf '%s\tA=%s\tMX=%s\n' "$d" "${a:--}" "${mx:--}"
done < hits.txt | sort -t= -k3 -r
# Authoritative registration date for the shortlist only - RDAP rate-limits
while read -r d; do
curl -s "https://rdap.org/domain/$d" \
| jq -r --arg d "$d" '.events[]? | select(.eventAction=="registration")
| "\($d),\(.eventDate)"'
sleep 1
done < shortlist.txt > shortlist-dates.csv▸ 什么会让一轮近似域名扫描出错
- ▸ 拿 punycode 去算距离。
xn--exmple-4of.com按字节看离example.com十万八千里,在屏幕上却一模一样。比较任何东西之前,先解码每一个xn--标签。 - ▸ 相信 NXDOMAIN。已注册但没有名称服务器的域名解析不出来。没有 DNS 应答不等于没有注册。
- ▸ 数点号来判断可注册域名。
foo.co.uk和foo.uk遵循不同规则。请使用 Public Suffix List。 - ▸ 只生成,从不检索。没有任何排列生成器能造出
example-account-verify.com。省掉在已注册域名中做子串检索这一步,整个组合抢注类别对你就是不可见的 - 而那恰恰是真实钓鱼中出现的类别。 - ▸ 对续期重复告警。没有永久的首次出现集合,每一次证书续期看起来都像一条新发现,你的队列会被三月份就已经分级过的域名塞满。
- ▸ 没有章法地做防御性注册。把最常见的五十个错拼买下来是合理的;买五千个就是一笔没有出口的长期账单,而排列空间实际上是无限的。只注册你自己的 NXDOMAIN 日志能证明确实有人在打的那些,把它们跳转到真实站点,其余交给监测。
▸ 常见问题
? 什么是仿冒抢注(typosquatting)?
注册一个别人打错字就会进入的域名,比如用 exapmle.com 对应 example.com。目的各不相同:有的投放广告、把误入流量变现,有的跳转到竞争对手,有的把名字攥在手里等着卖给品牌方,还有的直接搭钓鱼页面,或者截收发往错拼地址的邮件。这套手法很老却一直有效,因为它利用的是人的失误,而不是技术漏洞。
? 怎样找出针对我品牌的仿冒抢注域名?
两轮,两轮都要做。第一轮:用 dnstwist 或 urlcrazy 生成品牌词的各种排列,查清其中哪些已被注册,这覆盖拼写错误、后缀替换和同形字三类。第二轮:把品牌当作子串,在已注册域名库里检索,这覆盖任何生成器都造不出来的组合抢注类。之后按 MX 记录、A 记录和证书给命中结果排序,只对你准备处理的那份短名单用 RDAP 核对注册日期。
? 仿冒抢注违法吗?
取决于意图和司法辖区,这也正是存在仲裁机制而非一刀切规则的原因。在美国,《反域名抢注消费者保护法》(ACPA)规定,出于恶意牟利意图注册与显著商标构成混淆性近似的域名可被追究。在国际层面,UDRP 要求同时满足三点:与你的商标构成混淆性近似、持有人对该域名没有正当权益、以及恶意注册并使用。仅仅注册一个与常用词相似的名称,本身并不违法。
? 仿冒抢注、组合抢注和域名抢注有什么区别?
域名抢注(cybersquatting)是上位概念,指出于恶意注册域名以利用他人商标。仿冒抢注是其中依靠拼写错误的那个子集。组合抢注则把你的品牌拼写正确,再加上一个词,例如 example-support.com、example-login.net;它在真实钓鱼中更常见,也更难检测,因为品牌字符串本身是正确的,而可以附加的词是无限的。
? 防御性注册应该买多少个错拼域名?
比服务商告诉你的要少。真正有回报的集合很小:两三个最常见的拼写错误、客户默认你会使用的那少数几个后缀,以及你自己的 NXDOMAIN 日志能证明确实有人在打的名称。把它们跳转到真实站点,其余的靠监测。防御性注册是按域名按年重复支出的成本,而排列空间实际上是无限的 - 你买不出去这个坑。
? 什么是同形字攻击,怎么检测?
用显示上完全一样的字符做替换:西里尔字母 a 冒充拉丁字母 a、希腊字母 omicron 冒充 o、字母组合 rn 冒充 m。这类名称以 punycode 存储,因此以 xn-- 开头,逐字节与你的品牌比对什么都找不到。检测的做法是:解码每一个 xn-- 标签,用 Unicode 的易混淆字符映射表、或者自己维护一张常见替换表,把这些字符归一化到规范形式,然后才去度量相似度。浏览器在显示层面做了缓解,当一个标签混用了多种文字时会直接显示 punycode,但这对你的监测没有帮助。
? 怎样让一个仿冒抢注域名被下架?
先做最快的。如果它正在钓鱼,就同时向注册商的滥用邮箱和主机商举报,附上截图、URL、时间戳以及它在冒充谁;这可能几小时内解决,而且不花钱。同时把该 URL 提交给 Google 安全浏览和各类钓鱼拦截名单,这能在其余流程按法律节奏推进时先保护你的用户。如果你要的是域名本身而不只是让它消失,就提起 UDRP:机构费用约 1500 美元,另加律师费和大约两个月时间,之后域名会转让给你。URS 更便宜也更快,但只会把域名暂停到其注册期结束。
? 可以免费监测新出现的仿冒抢注域名吗?
可以,而且主要是个调度问题。跟读证书透明度日志,对流过的名称跑你的相似度过滤器,代价是一个常驻的小进程。或者每天下载一份新发现域名导出,每天用你的排列列表和子串规则筛一遍,那就是一个定时任务。DNSniffer 同时提供每日导出和无需账号的公开搜索 API,免费账号还可以设置最多十条子串关注并收到邮件摘要。商业品牌保护值得买的时候,是你需要有人替你做下架工作,而不是你需要检测能力。