概念定义
主机名是指针加目标。目标失效而指针仍在时,谁认领了目标谁就继承主机名。用户看到的是相同域名、甚至相同的小锁图标,背后却换了主人。
工作机制
攻击者枚举指向服务端点的 CNAME——GitHub Pages、Heroku、S3 静态站点、客服厂商——确认目标无人认领后注册之。这里承载的内容带着你的域名进入钓鱼、旧式 Cookie 作用域与 OAuth 重定向白名单的误配。
使用场景
下线流程必须原子地同时移除 DNS 与厂商资源;定期扫描把 CNAME 目标与“可认领资源指纹库”比对,赶在攻击者之前对悬空记录告警。
常见误区
删除资源不等于删除风险——留在 DNS 里的记录才是漏洞。NXDOMAIN 也不是判定信号,目标是“CNAME 指向未认领”;部分接管类型需要特定响应且依赖厂商版本,自动指纹库才跟得上。厂商会自动为认领资源签发证书,CT 日志会暴露接管时间线;分片在子域上的客户继承每次接管的波及面。
代码示例
import re
from pathlib import PurePosixPath
from urllib.parse import unquote
NAME_STAR = re.compile(r"filename\*\s*=\s*([^;]+)", re.IGNORECASE)
NAME = re.compile(r'filename\s*=\s*(?:"([^"]*)"|([^;]+))', re.IGNORECASE)
def safe_basename(value: str) -> str | None:
candidate = PurePosixPath(value.replace("\\", "/")).name.strip()
if candidate in {"", ".", ".."} or "\x00" in candidate:
return None
return candidate
def attachment_filename(header: str) -> str | None:
match = NAME_STAR.search(header)
if match:
value = match.group(1).strip().strip('"')
charset, _, remainder = value.partition("'")
_, _, encoded = remainder.partition("'")
try:
decoded = unquote(
encoded or remainder,
encoding=charset or "utf-8",
errors="strict",
)
except (LookupError, UnicodeDecodeError):
decoded = unquote(encoded or remainder)
return safe_basename(decoded)
match = NAME.search(header)
if not match:
return None
value = match.group(1) if match.group(1) is not None else match.group(2)
return safe_basename(unquote(value.strip()))
def main() -> None:
headers = [
'attachment; filename="report final.pdf"',
"attachment; filename=../../etc/passwd",
"attachment; filename*=UTF-8''%E5%B9%B4%E6%8A%A5.pdf",
'attachment; filename=".."',
]
for header in headers:
print(attachment_filename(header))
if __name__ == "__main__":
main()
package main
import (
"crypto/sha256"
"crypto/subtle"
"fmt"
)
func ConstantTimeEqual(a, b string) bool {
left := sha256.Sum256([]byte(a))
right := sha256.Sum256([]byte(b))
return subtle.ConstantTimeCompare(left[:], right[:]) == 1
}
func main() {
secret := "s3cr3t-token"
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-token"))
fmt.Println(ConstantTimeEqual(secret, "s3cr3t-tokeX"))
fmt.Println(ConstantTimeEqual(secret, "short"))
}