Skip to content

Go 主机安全面试:Linux DNS 隧道与异常域名外联检测

主机被入侵后,攻击者不一定直接连高危端口。DNS 因为常被放行,容易被用来做命令控制、数据外传、DGA 域名探测或低频心跳。面试官通常会追问:Go Agent 在主机侧怎么发现 DNS 隧道?只看 53 端口够不够?怎么把域名、进程、用户和网络连接串成可解释告警?

岗位场景

text
Linux 主机
  -> 采集进程、DNS 查询、UDP/TCP 连接、resolver 配置和网络命名空间
  -> 标准化 query、qtype、rcode、进程、用户、容器和目标 DNS 服务器
  -> 识别长子域、高熵标签、高频 NXDOMAIN、异常 DNS 服务器和可疑进程
  -> 关联 Web RCE、落地文件、反弹 Shell、账号异常和后续外联

这类题考的是 Linux 网络栈、DNS 协议基础、进程归因、规则降噪、攻击链还原和 Go Agent 低开销采集能力。

高频面试题

1. 为什么 DNS 隧道适合做主机安全面试题?

简洁答案:DNS 是基础网络协议,很多环境默认允许出站查询;攻击者可以把数据编码进子域名,通过 DNS 请求绕过部分网络访问控制。

关键知识点:

  • DNS 请求常走 UDP/53,也可能走 TCP/53,不能只看 UDP。
  • 隧道流量常见特征是长子域、高熵字符串、固定根域、高频查询和异常 qtype。
  • 正常业务也会有 CDN、监控、服务发现和安全产品查询,必须结合进程和基线判断。

Go 落地思路:

  • 事件模型保留 queryqtypercodedns.serverprocess.namepiduidcontainer.id
  • 不把“访问 53 端口”直接等价为告警,而是作为网络证据之一。
  • 对域名特征、查询频率、进程身份和历史基线做组合评分。

2. 主机侧可以从哪些位置采集 DNS 证据?

简洁答案:可以从进程网络事件、eBPF socket 事件、本地解析器日志、抓包、审计日志和配置文件变化里采集,优先选择对业务影响小且能做进程归因的来源。

常见来源:

证据来源可拿到的字段局限
eBPF socket/sendto进程、目标地址、端口、payload 摘要需要内核能力和降级方案
conntrack/netlink连接五元组、状态不一定有 DNS query 内容
本地 DNS 日志query、rcode、qtype可能缺少真实进程
pcap 抓包完整 DNS 包成本高,权限敏感
/etc/resolv.confDNS 服务器配置只能发现配置变化

Go 落地思路:

  • 能采 payload 时解析 DNS query;不能采 payload 时至少记录进程到 53 端口的连接。
  • 对采集能力上报 capability,例如 can_trace_dns_payloadcan_map_socket_process
  • 容器场景要保留网络命名空间和容器 ID,否则容易把不同业务的 DNS 行为混在一起。

3. DNS 隧道的域名特征通常怎么看?

简洁答案:重点看单个 label 长度、整体域名长度、字符分布、熵值、数字比例、重复根域和查询频率。

关键知识点:

  • 正常域名通常有可读单词、业务前缀或 CDN 结构。
  • 隧道会把数据编码成 base32base64 变体或十六进制字符串。
  • DGA 域名也可能高熵,但更偏大量根域轮询;DNS 隧道更常固定根域下生成大量长子域。

Go 落地思路:

  • 把域名拆成 label,分别计算最长 label、平均长度、数字比例和字符熵。
  • 对固定根域做滑动窗口统计,例如 5 分钟内子域数量、NXDOMAIN 数量、不同 label 数量。
  • 不用复杂模型也能先做可解释规则,后续再用样本数据调阈值。
go
func labelLooksEncoded(s string) bool {
	if len(s) < 24 {
		return false
	}
	var alphaNum int
	for _, r := range s {
		if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' {
			alphaNum++
		}
	}
	return alphaNum == len(s)
}

4. 为什么必须做进程归因?

简洁答案:同样是 DNS 查询,浏览器、系统服务、安全产品和 Web 子进程的风险完全不同;没有进程归因就很难降噪和复盘。

关键知识点:

  • systemd-resolveddnsmasq、容器 runtime 可能代理大量 DNS 请求。
  • 真正的可疑进程可能是 bashpythonperlphpjava、未知 ELF 或临时目录文件。
  • Web RCE 后的 DNS 隧道常表现为 Web 父进程拉起脚本,再由脚本发起异常 DNS 查询。

Go 落地思路:

  • 采集 socket 创建时记录 pid,再补充 /proc/<pid>/cmdlinecwdexeuid
  • 如果 DNS 请求经过本地 resolver,要尽量保留调用方进程和代理进程两层信息。
  • 规则输出写清楚“哪个进程查询了哪个根域、频率是多少、为什么异常”。

5. DNS 隧道和正常 CDN/服务发现怎么区分?

简洁答案:正常 CDN 和服务发现通常有稳定业务域、已知进程、规律 qtype 和可解释部署背景;DNS 隧道更容易出现异常进程、高熵子域、高失败率和攻击链邻近事件。

降噪思路:

  • 建立主机、业务、进程、根域四层基线。
  • 对已知安全产品、监控组件、服务发现组件配置白名单,但保留审计。
  • 结合相邻事件:Web RCE、临时目录执行、异常登录、反弹 Shell、文件落地。

Go 落地思路:

  • 规则不要写成“长域名即告警”,而是写成组合条件。
  • 维护小而明确的内置可信根域,客户环境差异交给策略配置。
  • 告警分级:单一异常域名做低危,高熵高频且关联可疑进程做高危。
text
可疑进程 + 高熵长子域 + 固定根域高频查询
  + NXDOMAIN 比例异常
  + 时间窗口内存在 Web RCE 或临时目录执行
  => DNS 隧道或异常域名外联

6. DNS 查询失败率为什么有检测价值?

简洁答案:DGA、探测和错误配置的隧道工具常会产生大量 NXDOMAIN;正常业务也会失败,但通常集中在固定组件和可解释域名上。

关键知识点:

  • NXDOMAIN 表示域名不存在,短时间大量出现需要关注。
  • SERVFAIL 可能来自 DNS 服务器异常,不一定是攻击。
  • 查询失败率必须按进程、根域和主机维度统计,不能只看全机总量。

Go 落地思路:

  • 用滑动窗口维护 totalnxdomainunique_subdomain
  • 同一个根域下大量随机子域失败,比全机失败数更有解释力。
  • 窗口状态要可控,例如只保留近 5 到 10 分钟,避免 Agent 内存被高基数域名拖垮。

7. 如何设计一条低误报的 DNS 隧道检测规则?

简洁答案:用“进程风险 + 域名形态 + 查询行为 + 攻击链上下文”组合,而不是靠单个特征。

规则字段:

维度示例字段作用
进程process.nameargvcwduid判断调用方是否异常
域名queryroot_domainlongest_labelentropy判断是否像编码数据
行为qpsunique_subdomainrcode_ratio判断是否高频或失败
上下文RCE、落地文件、登录、外联还原攻击链

Go 落地思路:

  • 先用结构体表达标准事件,再让规则只依赖标准字段。
  • 对高基数字段做限流和采样,避免单机恶意流量打爆 Agent。
  • 输出命中证据列表,方便客户判断是攻击、业务 SDK 还是安全工具。

8. 线上误报排查时应该问客户要哪些证据?

简洁答案:要拿到命中时间窗口内的进程树、DNS 查询样本、根域统计、主机角色、业务发布记录和相邻安全事件。

排查清单:

  • 命中进程的 execmdlinecwd、文件 hash、签名或包来源。
  • 同根域查询样本,包含成功、失败、qtype、DNS 服务器。
  • 主机角色:Web、数据库、CI、Kubernetes 节点、办公终端。
  • 相邻事件:异常登录、Web RCE、下载执行、反弹 Shell、敏感文件修改。
  • 是否存在监控、安全扫描、服务发现、日志 SDK 或灰度发布。

Go 落地思路:

  • Agent 本地保留短时间证据缓存,告警时附带前后窗口摘要。
  • 服务端支持按 host_id + root_domain + process 聚合查询。
  • 对确认为业务流量的样本生成基线规则,而不是简单关闭检测。

Go 实现设计要点

text
采集层
  -> eBPF/socket/netlink/resolver log 适配
标准化层
  -> DNSQueryEvent: query、qtype、rcode、server、pid、uid、container
富化层
  -> 进程画像、根域提取、label 特征、历史基线
检测层
  -> 滑动窗口统计、组合规则、限流采样
告警层
  -> 证据列表、攻击链关联、降噪建议

设计时优先保证三件事:

  • 字段稳定:规则不要依赖采集源私有字段。
  • 成本可控:高频 DNS 事件必须有窗口、上限和丢弃策略。
  • 证据可解释:告警里展示样本域名、进程、频率和关联事件。

学习要点

  • DNS 基础:query、qtype、rcode、UDP/TCP 53、递归解析。
  • Linux 网络采集:socket、netlink、eBPF、网络命名空间。
  • 进程归因:PID、PPID、/proc、用户、工作目录、可执行路径。
  • 异常检测:高熵 label、固定根域高频、NXDOMAIN 比例、DGA 与隧道差异。
  • 告警降噪:业务基线、可信进程、白名单、攻击链上下文。
  • Go 工程化:事件结构、滑动窗口、限流、内存上限、能力降级。

小练习

  1. 设计一个 DNSQueryEvent 结构,要求能表达域名、qtype、rcode、进程、用户、容器和 DNS 服务器。
  2. 写一个函数统计 5 分钟内同一根域的不同子域数量,并限制最多保留 1000 个子域样本。
  3. 给出一条规则:php-fpm 子进程在 /tmp 执行后,短时间内对同一根域发起大量高熵子域查询。
  4. 解释为什么 systemd-resolved 发出的 DNS 请求不能直接归因到业务进程。
  5. 设计一个误报排查流程,区分 DNS 隧道、CDN SDK、服务发现和安全扫描。
  6. 思考如何在不抓完整 payload 的情况下,仍然保留足够证据支撑告警解释。

复盘题

  • DNS 隧道检测为什么不能只看 53 端口?
  • 高熵长子域一定是攻击吗?哪些业务场景会产生类似特征?
  • 主机侧 DNS 检测和网络侧 DNS 检测各有什么优势?
  • Go Agent 如何避免被高频 DNS 事件拖垮?
  • 一条高质量 DNS 隧道告警应该包含哪些证据?
最近更新