Files

8.2 KiB
Raw Permalink Blame History

name, description
name description
host-sec-check 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)。
  4. 怀疑进程隐藏时,不要只信宿主机 ps。 从不加载宿主机 ld.so.preload 的容器读 /proc 和文件。
  5. 宿主机 chattr 可能被隐藏库挡住;改 immutable 文件用 Ubuntu/Alpine 自带 chattr 操作 bind-mount 的 /host/...。
  6. 不要把 root 密码、临时密码、私钥、webhook 或 token 作为命令行参数、环境变量或聊天内容传递;优先使用现有密钥、控制台或交互式 passwd。
  7. 加固不是“改 SSH 端口”竞赛:端口更改只减少噪声,必须先确认云安全组、主机防火墙和第二条登录路径。

收集脚本(可选,先跑再解读):

bash <skill-dir>/scripts/collect.sh USER@HOST

<skill-dir> 是本 skill 目录。脚本只采集,不修改系统。

自动修复默认先生成计划:

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/persistence.md。

加固资料(按需)

用户明确要求加固时,读取 references/hardening.md。

自动修复资料(按需)

用户批准执行修复时,读取 references/remediation.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 仍在