有点搞笑了,重新搞完站点之后最先要发的竟然是一个被攻击的记录。下面的修复过程和记录大部分都是ai辅助的,当然有我的补充和修改。
一切都源于一次"内网穿透" #
家里有一台 Orange Pi CM4,平时就跑着一些测试的小玩意。我想的是为了在外面ssh访问方便,而且这个这个站点的云主机闲着也是闲着,于是就配了 frp。然后安装完frp测试通了以后懒得查文档了,就让glm老师帮我优化了一下配置文件。主要就是小服务器上的 frpc 主动连云主机上的 frps,把 SSH 隧道映射到云主机的 6000 端口,顺手还配了域名、文件管理器、WireGuard 全家桶。
配完那天,手机上Termux ssh myserver 一条命令直达家里,HTTPS 域名证书自动签发,WireGuard 一开直连家里网络,一切都很好。
第一丝不对劲 #
这不今天我想着登一下这个小服务器,密码错误。反复试了好几次好几个密码,还是错。想着之前好像还配了密钥登录,然后用密钥试了一下还是登录不进去。这下坏了,我猜着估计是被爆破了,因为没改默认密码,很容易破。
于是让glm老师帮我看看是咋回事,还是懒,我其实直接拔掉电源看一下的事。然后ai发现了关键线索:
debug1: Authentications that can continue: passwordsshd 只提供密码认证了——公钥认证被整个禁用。而主机指纹没变、机器活着、frp 隧道正常。这不是"忘了密码",这明显是 sshd 配置被人改了。
自救通道:adb #
SSH 进不去,端口扫描发现小服务器上跑着 adbd(Android 调试桥的守护进程,监听 0.0.0.0:5555,这什么时候配置的我自己都忘了)——这东西平时是个安全隐患,此刻却是唯一的救生通道:它给 root shell,不经过 sshd。
正好手机 Termux 里有现成的 adb,两条命令
$ adb connect 192.168.31.9:5555
$ adb shell id
uid=0(root) gid=0(root)root 到手,开始取证。
下面是ai整理的现场 #
进到机器里,触目惊心的东西一样接一样:
1. 挖矿进程,伪装成内核线程:
root 3950652 362% ... [kworker/u8:2]方括号进程名看似内核线程,实则是 memfd 无文件驻留的矿马(/proc/PID/exe 指向 /memfd:kworker/u8:2 (deleted),磁盘上根本没有本体)。已连续挖了 4.8 天 CPU,正连着一台新加坡 VPS 和一台阿姆斯特丹"防弹主机"。
2. 持久化,伪装成 DHCP 服务:
/etc/systemd/system/dhcpcd.service 在攻击发生那一分钟被重写,ExecStart 指向 /usr/local/bin/firewalld——一个伪装成防火墙的 UPX 加壳加载器,Restart=always。
3. 锁门:
/etc/ssh/sshd_config 和 /etc/shadow 的 mtime 是同一秒——改密码 + 禁公钥 + 塞一个伪装成 cloud-init 的 drop-in(99-cloud-init.conf 写着 PubkeyAuthentication no),一套组合拳把主人和贼一起锁在外面,只留密码通道给爆破蠕虫自己用。
4. 灭口:
wtmp 被清零、journald 历史只剩当天、auth.log 轮转文件被删、root 的 .history 清空。贼很专业。
5. 案发时贼还在场:
取证时 sshd 进程里还挂着十几个 unknown [priv]——爆破还在进行,而来源全是 127.0.0.1,也就是经由我自己的 frp 隧道打进来的。用户名字典是 gpu 这种。
时间线还原 #
- D0:配置 frp,安全组开放 6000/TCP。此时小服务器的 sshd(默认密码
orangepi/orangepi)等于被挂上了公网。 - D2 20:15:自动化 SSH 蠕虫扫到云主机 6000 端口,撞开默认密码 → sudo 提权 root → 部署矿马 + 持久化 + 锁门 + 清日志。前后不超过一分钟。
- D2 ~ D4:矿马稳定运行,烧掉 4.8 天 CPU。
- D4 凌晨:我尝试登录发现异常,adb 自救,清理加固。
云主机侧日志显示,同一时期还有至少 7 个扫描器 IP 在扫 frps 的 7000 控制端口——全部被 token 挡住,一次都没进来。真正的大门只有 6000 那扇。
恶意样本 #
SHA256: c7929e0ad9394a7a42d23df8007cbd421a06cac63146aec65c38f98bef9b8787
类型: ELF 64bit aarch64, UPX 加壳, Go 编写 + garble 混淆
伪装: 进程名 [kworker/u8:2],文件名 firewalld,服务名 dhcpcd
驻留: memfd_create 无文件执行
行为: 本机 SSH 蠕虫式爆破 + 挖矿
C2: 172.86.80.31:30245 (SG, RouterHosting)
45.153.34.138:21629 (NL, TechTies — 著名防弹主机网段)脱壳后没有明文矿池/钱包配置(运行时解密),全网威胁情报无该哈希记录——新变种。定性:商业化的自动蠕虫,不是定向攻击。被盯上不是因为我是谁,只因为我把一扇弱密码的门开在了公网上。
清理与加固 #
清理(当场):
systemctl stop frpc # 先断贼的隧道
systemctl stop dhcpcd && systemctl disable dhcpcd # 断持久化
kill -9 <矿马PID> # 杀进程
mv /usr/local/bin/firewalld /root/incident/ # 隔离样本
rm /etc/ssh/sshd_config.d/99-cloud-init.conf # 摘掉假 drop-in加固(当晚全部完成):
| 项目 | 措施 |
|---|---|
| SSH(两台机器) | 全部改为纯密钥登录,PasswordAuthentication no |
| 小服务器 | 重新生成 SSH 主机密钥(防中间人) |
| 所有密码 | 随机 16 位起,全部轮换 |
| frp token | 轮换(攻击者有 root 期间能读到) |
| WireGuard 密钥对 | 轮换(手机端私钥曾在被入侵机器上生成) |
| 安全组 | 关闭 6000,SSH 类需求走 WireGuard 或 stcp |
| 持久化面 | 确认无残留 cron/timer/新用户/恶意公钥 |
泄露凭据盘点 #
被 root 级失守 1.5 天,磁盘上所有凭据都当泄露处理:
- 腾讯云的DNSPod API 密钥(之前给ddns-go 用的)
- 两个大模型 API key (配置了pi agent)
- 机场订阅链接(Clash 配置里)
- 还有shell history 里手敲过的敏感命令
好在机器上没有 别的SSH 私钥、证书、数据库这类"重要文件",损失也就是电费。
教训 #
给所有人(也是给我自己)的自查清单:
- 内网服务映射到公网 = 该服务直接面对全网扫描器。frp 的 tcp 代理不提供任何认证,套在 sshd 上就是裸奔
- 默认密码不是密码,是邀请函。出厂密码当天就该改
- SSH 只留密钥登录,
PasswordAuthentication no一劳永逸 - 云主机安全组最小化开放,能限源 IP 就限,临时开了之后记得及时关
- 真需要"在外面 SSH 进家",用 WireGuard 组网或 frp 的 stcp(带共享密钥,服务器不开公网监听),别用裸 tcp 代理
- adbd 这类免认证 root 通道(
:5555)平时要关——这次它是我的救生索,但平时它是别人的进攻快捷通道 - 日志要做异地留存,本地日志在入侵者手里活不过一分钟
- root 级失守后,所有落盘凭据一律轮换,机器最稳妥是重刷
番外:重刷系统实录(比入侵本身还曲折) #
这个也是ai整理的,我换完所有api key之后怕ai不太靠谱,它还想备份现有数据重刷系统,并且还没想起来对数据检测有无恶意代码。真是不太靠谱。下面是ai的操作,你也不知道它是笨还是聪明。
root 级失守后,最彻底的方案只有一个:重刷。本以为是个体力活,结果比入侵本身还精彩。
镜像都下载不到 #
官方国际站和下载子域全球性 NXDOMAIN(DNS 记录干脆没了),第三方镜像站 502,国内官网纯 JS 渲染爬不动——绕了一大圈,最终答案是最朴素的:手机浏览器打开官网,人肉从百度网盘下载官方 Debian12 服务器版。723MB 压缩包,手机下载反而最快。
第一个坑:SD 卡优先启动 #
RK3566 的 BootROM 优先从 SD 卡引导。兴冲冲把空 SD 卡插上开机——直接不开机。空卡等于把引导流程卡死,连 eMMC 里的旧系统都不回退。正确姿势:先拔卡开机进旧系统,运行中再热插 SD。
传输:adb 和 scp 的云泥之别 #
往板子传 723MB 镜像:adb push 十分钟超时没传完(实测不到 1MB/s),换 scp——15 秒传完,约 48MB/s。同一块 WiFi,差距全在协议开销。以后大文件老老实实走 scp/ssh。
eMMC 克隆:教科书式翻车 #
SD 系统配好后(254 个包升级 + 换全国内源 + 全套开发工具),要把系统同步进 eMMC。我设计了套"精致"的方案:
sfdisk 写入按 eMMC 容量调整的新 GPT 表
dd 复制 loader 区(扇区 34-61439,避开新 GPT)
dd 整分区克隆 boot 分区(同文件系统 UUID)
mkfs.ext4 -U <同UUID> 根分区
rsync -aHAX 整个根文件系统拔 SD,开机——kernel panic,路由器里连 DHCP 请求都没有。
插回 SD 上去"验尸",一眼看到鬼:eMMC 的 boot 分区 UUID 还是旧系统的(9043-7404),我 dd 进去的新分区(9B29-4F81)根本没生效!
根因是一个经典竞态:sfdisk 写完新分区表,内核还没重新读取,我就对着旧几何位置的 p1 节点 dd 了——写进了旧位置,等于白写。开机时 u-boot 加载旧 boot.scr,找旧根分区 UUID,找不到,panic。
用户发现开不了机后第一反应"我猜是磁盘 UUID 的问题"——方向完全正确,只是没想到 UUID 错位的背后是分区表竞态。
修复只要三行:partprobe 强制内核重读分区表 → 重新 dd boot 分区 → 验证 UUID 链条(boot.scr→根分区、fstab→两分区全部对齐)。再开机,一次点亮。
重刷番外的教训 #
- 分区手术(改表/克隆)后必须
partprobe并验证节点几何,再写数据——sfdisk 返回成功 ≠ 内核已生效 - 空白 SD 卡插着开机 = 不开机(RK3566 SD 优先引导)
- adb push 传大文件是玄学,scp 是物理学
- 刷成功的 SD 卡别扔,写个标签收好——它就是这台机器的终身救援盘
后记 #
整个过程像一场小型的应急响应演练:发现 → 自救通道 → 取证 → 斩链 → 清理 → 轮换 → 复盘。损失可控(两天 CPU),学费交得算便宜——毕竟,被挖的只是矿,没被偷的是数据。
那台小服务器最终重刷了系统,从官方镜像到全部依赖源都是国内通道,跑得干干净净;CPU 负载 0.1,安全组里只剩 443 和一个 WireGuard 的 UDP 口。
如果这篇文章让你后背发凉,先去做两件事:改掉所有默认密码,关掉所有"临时开一下"的端口。
好了,ai写的狗屁看不看都行,这次主要还是懒了,没改密码,这下所有东西都得重新配置了。这是第二回中挖矿病毒了,之前第一次是云服务器,当时是腾讯云有入侵警告,进服务器之后抓到异常进程之后还搜了一下是什么东西,当时还没有ai呢。这次想着全程让ai操作,也测测它的能力,结果glm5.3也就一般般吧,不靠人提示是能完成任务,但是太绕远了。