远程命令执行

去年秋天,远程我帮一个做运维的命令朋友排查问题。他的执行服务器上多了一个从没见过的进程,名字伪装成 kworker,远程CPU 占用不高,命令但每隔几小时就往一个境外 IP 发几 KB 的执行数据。他以为是远程挖矿,查了半天没找到矿池地址。命令最后在 /tmp 下翻出一个 ELF 文件,执行逆向一看,远程是命令个反弹 shell。攻击者没做任何持久化,执行只是远程每隔一段时间回来看看,能连上就执行命令,命令连不上就走。执行

这就是远程命令执行,英文叫 RCE(Remote Command Execution)。攻击者不需要坐在你的机器前,只要目标系统有一个能接收输入并把它当命令跑的地方,就能隔着网线把指令送进来。它跟“远程控制”不是一回事——远程控制是合法的、有界面的,RCE 是偷偷摸摸的,通常只给你一个没有回显的 shell,或者干脆盲打。

RCE 的入口往往不起眼。前年爆出的 Log4j2 漏洞,一个 ${ jndi:ldap://...} 字符串就能让服务器主动去加载远程类文件。很多开发当时觉得“我日志里又不会写这种奇怪的东西”,可用户输入、HTTP 头、甚至设备型号都会被记进日志。攻击面比想象的大得多。

我见过最离谱的一例,是一家小公司的内部管理系统。登录框的“用户名”参数没做过滤,直接拼进了系统调用。输入 admin; cat /etc/passwd,页面上就真把密码文件内容吐出来了。开发者的本意是调用一个本地脚本去校验用户,结果分号一加,后面的命令照跑不误。这种叫命令注入,是 RCE 里最直白的一种。

另一种常见的是反序列化。Java 的 ObjectInputStream、PHP 的 unserialize(),如果输入可控,攻击者可以构造一条对象链,在反序列化过程中触发任意方法调用。2015 年 WebLogic 的那次反序列化 RCE,影响范围大到很多银行内网都中招。修复方式也粗暴——打补丁,或者干脆禁掉 T3 协议。

防御 RCE 没有银弹。我自己的习惯是:任何进入系统调用的参数,先过白名单,只允许字母数字和少数符号;能不用 Runtime.exec() 就不用,非要用就传数组而不是拼接字符串;依赖库定期扫 CVE,Log4j 那种事一次就够受了。还有一点常被忽略——容器里别用 root 跑应用。真被 RCE 了,攻击者拿到的权限越低,你反应的时间就越多。

说回朋友那台服务器。他最后没重装系统,只是删了文件、改了 SSH 端口、加了 fail2ban。我问他为什么不重装,他说“业务跑在上面,重装要停半天”。这大概是很多中小团队的现状——知道有风险,但停机的代价比风险更疼。RCE 可怕的地方不在于技术多高深,而在于它总在你觉得“应该没事”的地方冒出来。