iptables的string模块无法可靠阻断应用层命令注入,仅能在传输层对单包明文载荷做字节匹配,不解析协议、不解密、不重组tcp流,实际工作在网络层/传输层,仅对未加密、未编码、不分片且特征完整落于单包内的流量有效。

iptables 的 string 模块不能真正实现应用层命令注入漏洞的可靠阻断,它只能在传输层对单个数据包的原始载荷做字节级匹配,不解析协议、不解密、不重组 TCP 流。所谓“在应用层阻断”,是常见误解——它实际工作在网络层/传输层,仅能作为粗粒度辅助手段,且仅对明文、未编码、不分片、特征完整落在单包内的流量有效。
以下是你需要知道的关键事实和可行做法:
一、哪些命令注入特征勉强可试(仅限明文服务)
适用于 HTTP、Telnet、FTP 控制通道等未加密协议中,攻击载荷以固定 ASCII 字符串形式出现在请求开头几百字节内:
- 常见 shell 注入关键词(大小写不敏感):
"| /bin/sh""; ls -la""&& cat /etc/passwd""$(id)"-
"exec(","system("(PHP 类漏洞)
示例规则(拦截明文 HTTP POST 中含 ; cat /etc/passwd):
iptables -A INPUT -p tcp --dport 80 -m string --algo bm --from 0 --to 512 --string "; cat /etc/passwd" --icase -j DROP
⚠️ 注意:该规则只在攻击者用明文 HTTP、未压缩、未 URL 编码、且整条命令恰好落在同一个 TCP 包前 512 字节时才可能触发。
二、为什么绝大多数命令注入场景它会失效
-
HTTPS 流量完全不可见:TLS 加密后 payload 是密文,
--string "rm -rf /"扫不到任何明文。 -
URL 编码绕过:
%3B%20cat%20%2Fetc%2Fpasswd≠; cat /etc/passwd,字符串不一致即不匹配。 -
HTTP 分块或大请求体导致跨包:
;在包1末尾,cat /etc/passwd在包2开头 → 单包无完整匹配。 -
Base64/Hex/Unicode 混淆:如
Y2F0IC9ldGMvcGFzc3dk(base64)或\u003b cat不会被 ASCII 字符串规则捕获。 -
空格替换、分隔符变形:用
$IFS、、<code>|、换行符替代空格,或用{}、$()绕过固定模式。
三、更实际的防护组合建议
真正防御命令注入,应分层部署,iptables 仅作最外层“减速带”:
-
Web 层(首选)
- Nginx/Apache 配置
mod_security+ OWASP CRS 规则集,支持解码、正则、上下文感知(如识别exec(在 PHP 参数中)。 - 应用代码严格使用参数化查询、白名单校验、禁用
eval/system/exec等危险函数。
- Nginx/Apache 配置
-
代理层(推荐补充)
- 部署透明代理(如 Squid + ICAP 或 Envoy + WASM),可终止 TLS、解析 HTTP body、执行逻辑判断(如检测
cmd=参数值是否为白名单)。
- 部署透明代理(如 Squid + ICAP 或 Envoy + WASM),可终止 TLS、解析 HTTP body、执行逻辑判断(如检测
-
若坚持用 iptables 做初步过滤(仅限测试/临时缓解)
- 限定端口和服务协议(如
--dport 23for Telnet,--dport 21for FTP) - 多规则覆盖常见变体(大小写、空格/Tab/
$IFS、常见分隔符):iptables -A INPUT -p tcp --dport 23 -m string --string ";ls" --icase -j DROP iptables -A INPUT -p tcp --dport 23 -m string --string "|ls" --icase -j DROP iptables -A INPUT -p tcp --dport 23 -m string --string "$(" --icase -j DROP - 加
-m connlimit --connlimit-above 3防止高频试探性连接。
- 限定端口和服务协议(如
-
替代内核方案(进阶)
- 使用
nftables+@th,12,2提取 TCP payload 后接regex(需内核 ≥ 5.10); - 或用 eBPF 程序(如 Cilium)在 XDP 层提取并解析应用层字段,性能更高、灵活性更强。
- 使用
不复杂但容易忽略:命令注入的本质是应用逻辑缺陷,防火墙规则再密集,也挡不住一个拼接了合法参数的 system("ping -c 1 " . $_GET['host'])。真正的防线永远在代码里和 WAF 上。











