8.2 KiB
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、强制重启)
铁律
- 先取证,后动手。 重启、删文件、停服务会毁掉证据。
- 不要把密钥打进对话。 token、密码、authorized_keys、kubeconfig 只写「存在/已轮换/路径」,不贴原文。
- sudo 失败不要停。 用户在
docker组时,用特权容器nsenter看宿主机(见 references/access.md)。 - 怀疑进程隐藏时,不要只信宿主机
ps。 从不加载宿主机ld.so.preload的容器读/proc和文件。 - 宿主机
chattr可能被隐藏库挡住;改 immutable 文件用 Ubuntu/Alpine 自带 chattr 操作 bind-mount 的/host/...。 - 不要把 root 密码、临时密码、私钥、webhook 或 token 作为命令行参数、环境变量或聊天内容传递;优先使用现有密钥、控制台或交互式
passwd。 - 加固不是“改 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。
报告格式
先给结论(是否沦陷、最可能入口、对外还开着什么),再给证据表,最后给 需用户点头的处置清单。不要默默清理。
结论: 沦陷 / 可疑 / 未见异常
最可能入口: …
对外端口: …
持久化: …
隐藏/矿机: …
容器/编排: …
未查项: …
建议处置: (等确认)
处置(仅在用户同意后)
- 停并删除未授权容器;镜像也删
- 矿机二进制:删掉后用 空文件 +
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仍在