Files
.pouch/skills/host-sec-check/SKILL.md
T

168 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: host-sec-check
description: >-
Linux/VPS 主机安全检查、入侵取证与用户确认后的分阶段加固:覆盖矿机、隐藏进程/ld.so.preload、对外端口、SSH/Docker/k8s、cron/systemd、hugepage/OOM、SSH/防火墙/Fail2ban。触发:安全检查、安全加固、新机初始化、被黑、入侵、矿机、排查端口、对公网放开、host security、hardening、compromise、/host-sec-check。
---
# host-sec-check
对一台 Linux 主机做安全检查。默认 **只读取证 + 出报告**。清理、关端口、重启、轮换密钥和安全加固必须用户明确同意后再做。
需要:SSH 目标(`user@host` 或 ssh config 别名)、哪张网卡/IP 算公网。没有公网 IP 就只报「监听面」,并标明未做外部探测。
## 不适用
- Web 应用代码审计、依赖 CVE 扫描(可在报告里列为后续)
- 云厂商安全组控制台(能查就查,查不到就写「未验证」)
- 未获准的破坏性操作(`rm -rf`、整盘 wipe、强制重启)
## 铁律
1. **先取证,后动手。** 重启、删文件、停服务会毁掉证据。
2. **不要把密钥打进对话。** token、密码、authorized_keys、kubeconfig 只写「存在/已轮换/路径」,不贴原文。
3. **sudo 失败不要停。** 用户在 `docker` 组时,用特权容器 `nsenter` 看宿主机(见 [references/access.md](references/access.md))。
4. **怀疑进程隐藏时,不要只信宿主机 `ps`。** 从**不加载宿主机 `ld.so.preload` 的容器**读 `/proc` 和文件。
5. 宿主机 `chattr` 可能被隐藏库挡住;改 immutable 文件用 **Ubuntu/Alpine 自带 chattr** 操作 bind-mount 的 `/host/...`。
6. 不要把 root 密码、临时密码、私钥、webhook 或 token 作为命令行参数、环境变量或聊天内容传递;优先使用现有密钥、控制台或交互式 `passwd`。
7. 加固不是“改 SSH 端口”竞赛:端口更改只减少噪声,必须先确认云安全组、主机防火墙和第二条登录路径。
收集脚本(可选,先跑再解读):
```bash
bash <skill-dir>/scripts/collect.sh USER@HOST
```
`<skill-dir>` 是本 skill 目录。脚本只采集,不修改系统。
自动修复默认先生成计划:
```bash
bash <skill-dir>/scripts/remediate.sh USER@HOST --plan --fix ssh-perms --fix fail2ban --ignore-ip ADMIN_CIDR
```
只有用户明确批准具体修复项后,才可使用 `--apply` 和精确确认串。每次应用前必须备份,应用后必须验证;脚本输出的 `BACKUP_ID` 可用于精确回滚。防火墙、SSH 登录策略、Docker/k8s 绑定、恶意文件、重启和密钥/token 轮换只生成计划,不由此入口自动执行。详细边界见自动修复资料。
## 流程
按顺序做。每步记下「正常 / 可疑 / 未查到」。需要逐项展开时再读检查清单。
如果用户明确要求「加固」「新机初始化」「配置 SSH/防火墙」,先完成只读快照,再阅读并执行加固资料。加固采用分阶段、可验证、可回滚流程;不能把远程一键脚本直接当作安全默认值。
### 1. 快照
- 主机名、发行版、uptime、负载、`free -h`、`df -h`、公网/内网/Tailscale IP
- `ss -lntup`(最好 root)
- `docker ps -a`、compose 项目
- 若有 k3s/k8s:agent 是否在跑、能否 `kubectl get pods -A`
### 2. 入口
- sshd:`Port`、`PermitRootLogin`、`PasswordAuthentication`、`authorized_keys`(root 与所有 UID≥1000)
- `last` / `lastb` / `journalctl -u ssh`:异常登录;**无交互登录却出现 docker run 也是入口信号**
- sudoers、`docker` 组(= root)、NOPASSWD
- 对公网的管理面:Portainer / Cockpit / 面板 / k8s apiserver / Docker 端口,尤其 `-p 0.0.0.0:…` 且挂了 `docker.sock` 或 `/:/host`
- 单元文件或 compose 里的明文 token(k3s `--token`、agent secret)
### 3. 持久化
发现持久化信号或需要深查时,再读持久化资料。至少查:
- crontab(root 与所有用户)、`/etc/cron.*`
- `/etc/systemd/system/*.service`、timers
- `/etc/ld.so.preload`(用**外部** cat/hexdump,宿主机 cat 可能被藏空)
- `chattr +i` 的异常二进制(`lsattr`)
- 隐藏 Docker 数据根(非 `/var/lib/docker` 的 dockerd `--data-root`)
### 4. 进程与隐藏库
- 从特权容器列 `/proc/*/cmdline`(绕过 preload)
- 对照宿主机 `ps`:只在容器视角出现的进程 = 隐藏
- `/proc/*/maps` 是否映射陌生 `.so`(`libgcwrap`、`libprocesshider`、随机名)
- 矿机常见名:`perfcc`、`xmrig`、`kdevtmpfsi`、`javavm64`;同体积多副本(例如都是 11MB)
- hugepage:`grep Huge /proc/meminfo`。`nr_hugepages>0` 且进程列表对不上,多半是矿机预留
### 5. 容器与编排
- `docker ps -a`:镜像名含 earnfm / packetstream / repocket / traffmonetizer / honeygain / proxyrack / bitping / antgain 等挂机池
- inspect:`Privileged`、Binds(`docker.sock`、`/:/host`)、Ports 是否 `0.0.0.0`
- 第二套 dockerd / 异常 `--bridge`
- k8s:节点上是否有与 Git 仓库无关的 workload;控制面不健康时从 kubelet `pods/*/etc-hosts` 读 Pod 名
### 6. 对外暴露
- 本机 `0.0.0.0` / `[::]` 监听
- **从另一台机器探测公网 IP**(含 IPv6)。只看 `ss` 会漏安全组,只看安全组会漏「监听但没防火墙」
- 高危:`rpcbind :111`、明文 HTTP 应用端口、Docker/Portainer、k8s 6443、数据库
- 管理 SSH 若可走 Tailscale,公网 SSH 算多余攻击面
### 7. 资源与日志
- 无 swap + 高 hugepage → 应用易 OOM
- `journalctl` / `auth.log` 在处置时间点附近;日志空也要记
- 出站连接:`ss -tp` 看陌生 IP(不要把结果里的密钥打出来)
### 8. 补漏(本次检查没做或做得浅的)
见 checklist 后半:补丁、SUID、世界可写、意外用户、IPv6、定时器、内核模块、出站 C2、云安全组、备份是否可用。
加固模式还要额外记录:
- SSH 的 `sshd -t` 与 `sshd -T` 生效结果,不只看配置文件中的 grep
- 防火墙实际后端(nftables/iptables/ufw)及 IPv4/IPv6 规则
- Fail2ban jail、日志后端、白名单和当前封禁状态
- 云安全组/平台防火墙是否已放行管理入口(查不到必须标未验证)
- 新管理员的密钥登录是否能在第二条会话中成功,再关闭 root/密码登录
- Docker 的端口绑定、特权、socket/宿主机挂载;不要无条件改 `daemon.json`
- 变更前备份、变更后验证、失败时的控制台回滚路径
## 检查清单(按需)
需要逐项展开时,读取 [references/checklist.md](references/checklist.md)。
## 持久化资料(按需)
发现持久化信号或需要深查时,读取 [references/persistence.md](references/persistence.md)。
## 加固资料(按需)
用户明确要求加固时,读取 [references/hardening.md](references/hardening.md)。
## 自动修复资料(按需)
用户批准执行修复时,读取 [references/remediation.md](references/remediation.md)。
## 报告格式
先给结论(是否沦陷、最可能入口、对外还开着什么),再给证据表,最后给 **需用户点头的处置清单**。不要默默清理。
```
结论: 沦陷 / 可疑 / 未见异常
最可能入口: …
对外端口: …
持久化: …
隐藏/矿机: …
容器/编排: …
未查项: …
建议处置: (等确认)
```
## 处置(仅在用户同意后)
原则见 [references/cleanup.md](references/cleanup.md)。
- 停并删除未授权容器;镜像也删
- 矿机二进制:删掉后用 **空文件 + `chattr +i` 占路径**
- 清空并锁定 `/etc/ld.so.preload`
- 关公网绑定:改成 `127.0.0.1` 或 Tailscale IP(若回源走 Tailscale 主机名,必须绑 TS IP,不能只绑 loopback)
- 停无用服务(如 `rpcbind.socket` + `rpcbind.service`,必要时 mask)
- 重启才能卸掉仍映射已删 `.so` 的老进程(sshd/dockerd)。先取证再重启
- 轮换:暴露过的 token、面板、k3s bootstrap。控制面 API 不通就不要假轮换,写阻塞原因
## 验证
- 公网探测:已关端口失败;该留的业务域名仍 200
- `ss`:无 `0.0.0.0:已关端口`
- `grep -l libgcwrap /proc/*/maps`(或同类库)重启后为 0
- 占位文件仍在且 immutable
- 业务容器 `restart: always` 仍在