Skip to content

Go 主机安全面试:WebShell 落地与文件篡改检测

WebShell 检测经常把文件系统、Web 目录、进程行为和告警降噪串起来考。面试官不只关心你知道 evalassert 这些关键字,还会追问 Agent 怎么采、怎么关联、怎么降低误报,以及 Go 侧如何控制资源。

岗位场景

text
攻击入口:Web 漏洞 / 弱口令 / 文件上传
  -> Web 目录写入脚本文件
  -> Web 进程解释执行
  -> 读取敏感文件 / 执行系统命令
  -> 外连、提权或持久化
  -> HIDS/EDR 还原证据链并告警

主机安全研发要做的是把“文件变化”和“进程行为”放到同一个时间线里,而不是只靠文件内容关键字打一个孤立告警。

1. WebShell 落地检测为什么不能只扫文件内容?

简答:内容扫描能发现一部分样本,但容易被混淆绕过,也解释不了文件是谁写的、何时执行的。

关键知识点:

  • WebShell 可能用编码、拼接、反射、动态加载绕过关键字。
  • 同一个危险函数在测试脚本、框架缓存里也可能合法出现。
  • 文件内容只能说明“像不像”,进程和时间关系才能说明“怎么来的”。
  • 检测证据最好包括写入进程、文件路径、文件 hash、后续执行行为。

Go 落地思路:

  • 文件内容扫描只做候选筛选。
  • 采集文件创建、修改、重命名事件,补充进程上下文。
  • 告警输出保留命中片段、文件元数据和关联进程证据。

2. Web 目录文件变化至少要采哪些字段?

简答:要能回答“谁在什么时间把什么文件写到了哪里,文件后来有没有被访问或执行”。

建议字段:

字段作用
path / old_path识别创建、覆盖、重命名
op区分 create、write、chmod、rename、delete
uid / gid判断写入身份
pid / ppid关联写入进程和父进程
sha256去重、样本分析和复核
mtime / ctime还原文件变化时间

Go 落地思路:

  • 文件事件里先记录路径和操作类型,hash 可以异步计算。
  • 对大文件设置 hash 大小上限,避免拖垮 Agent。
  • 采集失败要记录原因,例如权限不足、文件已删除、路径不可读。

3. Linux 上文件事件可以从哪里采集?

简答:inotify 简单,audit/eBPF 证据更强,生产上通常组合使用。

来源适合用途主要问题
inotify监控固定 Web 目录变化事件没有完整进程上下文
audit文件审计、进程关联配置复杂,规则量影响性能
eBPF低开销采集 open/write/rename内核版本和权限兼容成本
周期扫描hash 基线和兜底校验实时性差,IO 成本要控

Go 落地思路:

  • 小范围目录变化可以先用 inotify 类能力,复杂场景再上 audit/eBPF。
  • 采集层只负责把事件拿准,检测逻辑放到 Go 规则或服务端。
  • 对降级状态要上报,例如“当前只有周期扫描,没有进程上下文”。

4. 如何判断一次文件写入可能是 WebShell?

简答:看路径、后缀、内容特征、写入进程和后续访问行为的组合。

常见组合:

text
Web 目录新增 .php/.jsp/.asp 文件
  + 写入进程来自 web 服务或异常脚本
  + 内容命中危险函数或混淆特征
  + 文件随后被 web 进程读取或触发命令执行
  => 高风险 WebShell 落地

关键知识点:

  • .php.jsp.aspx.jspx.phtml 都是高关注后缀。
  • 上传目录、临时目录、静态资源目录出现脚本文件更可疑。
  • 只看后缀会误报,框架发布、插件安装也会写入脚本。

Go 实现要点:

go
func LooksLikeWebShell(path, content string) bool {
	p := strings.ToLower(path)
	c := strings.ToLower(content)
	return strings.HasSuffix(p, ".php") &&
		(strings.Contains(c, "eval(") || strings.Contains(c, "system("))
}

这类函数只适合做第一层筛选,真正告警还要叠加写入进程、目录画像和访问行为。

5. 如何降低文件篡改规则的误报?

简答:给 Web 目录建立变化画像,发布窗口和可信路径降级,异常写入再升级。

误报来源:

  • 正常上线发布写入大量文件。
  • CMS 插件安装、模板更新。
  • 日志、缓存、上传文件持续变化。
  • 安全扫描器投放测试样本。

降噪策略:

场景处理方式
发布窗口内批量变更聚合成一条低风险变更
可信 CI/CD 用户写入降级并保留审计
上传目录出现脚本文件升级风险
Web 用户写入可执行脚本升级风险

Go 落地思路:

  • 去重 key 可以用 host_id + path + sha256 + rule_id
  • 白名单要有范围:路径、用户、进程、时间窗口。
  • 告警里记录降噪原因,方便客户复盘。

6. 文件 hash 应该同步算还是异步算?

简答:小文件可以同步,大文件和高频变化要异步,否则采集链路会被 IO 卡住。

关键知识点:

  • WebShell 通常文件较小,但不能假设所有文件都小。
  • 同步 hash 会阻塞事件处理 goroutine。
  • 文件可能刚收到事件就被删除或覆盖,要处理读取失败。

Go 落地思路:

  • 事件采集 goroutine 只入队基础事件。
  • hash worker 有并发上限和文件大小上限。
  • 读取失败不丢事件,标记 hash_status=failed
go
const MaxHashBytes = 2 << 20

func ShouldHash(size int64) bool {
	return size > 0 && size <= MaxHashBytes
}

7. 怎么把文件事件和进程事件关联起来?

简答:用 pid + start_time + time_window 关联写入进程,再用路径和后续命令执行补证据。

关联链路:

text
file_write: /var/www/html/upload/a.php by pid=3021
  within 10s
process_exec: php-fpm -> sh -c whoami
  same path or same web vhost
  => WebShell 写入后被触发执行

Go 落地思路:

  • 进程上下文缓存要带启动时间,避免 PID 复用串案。
  • 文件事件先关联最近进程,关联不到也要保留原始事件。
  • 服务端可以做更长窗口的攻击链还原,Agent 不要无限缓存。

8. 客户反馈“WebShell 误报”时怎么定位?

简答:先复核证据链,再判断是发布行为、规则过宽、白名单缺失还是采集字段不准。

排查顺序:

  1. 看规则版本、命中文件、hash、命中片段。
  2. 看写入进程、用户、父进程和是否处于发布窗口。
  3. 看文件是否被 Web 进程访问,是否触发命令执行或外连。
  4. 对比同目录历史变化频率和客户发布系统路径。
  5. 如果确认误报,补上下文条件或白名单,不要直接删除规则。

Go 研发要点:

  • 告警结构保存 rule_idrule_versionevidence
  • 规则要能用历史事件回放,验证修改后是否漏报。
  • 日志区分采集失败、hash 失败、规则异常和上报失败。

学习要点

  • Linux 文件系统基础:inode、权限、mtime、ctime、rename 原子性。
  • WebShell 常见落地路径、脚本后缀和危险函数。
  • 文件事件采集来源:inotify、audit、eBPF、周期扫描。
  • Go Agent 资源控制:队列上限、worker 并发、hash 大小限制。
  • 检测工程:证据链、误报降噪、规则回放、攻击链还原。

小练习

  1. 设计一个 FileEvent 结构体,字段要能支持 WebShell 落地溯源。
  2. LooksLikeWebShell 补 3 个测试用例:普通 PHP、eval WebShell、非 PHP 文本。
  3. 设计一个去重 key,要求同一文件同一 hash 只告警一次。
  4. 如果发布系统会批量写入 PHP 文件,你会用哪些字段把它和 WebShell 区分开?
最近更新