// 安全研究 · 2026-01-25

警惕 Shell 脚本中的逻辑陷阱:&&|| 替代 if-else 引发的安全漏洞

在 Shell 脚本编写中,开发者为了追求简洁,常使用 && 与 || 的短路组合逻辑替代结构化的 if-else 语句。这种看似便捷的写法,实则隐藏着极易被忽视的逻辑陷阱。本文将深入剖析这两种逻辑的核心差异,通过实战案例揭示漏洞成因。


一、核心逻辑:结构化 vs 链式执行

要理解陷阱的本质,首先需厘清 if-else 结构化逻辑与 &&|| 短路组合逻辑的底层执行规则。

1.1 if-else:结果唯一的结构化判断

if-else 是 Shell 中最基础的结构化条件判断语法,其核心特征是分支互斥、结果唯一:

  • 先执行 if 后的条件表达式,得到「真(0)」或「假(非0)」的明确结果;
  • 条件为真时,仅执行 then 分支代码,else 分支完全跳过;
  • 条件为假时,仅执行 else 分支代码,then 分支完全跳过;
  • 关键特性:then/else 分支内部命令的执行状态(成功/失败),不会改变分支选择的结果。

示例代码(结构化逻辑):

#!/bin/bash
if [ 1 -eq 1 ]; then
    # 条件为真,执行该分支
    echo "条件成立"
    # 即使分支内命令失败,也不会触发 else
    ls /non_exist_dir  # 该命令执行失败,返回非0
else
    echo "条件不成立"
fi

执行结果:仅输出「条件成立」,else 分支不会因 ls 命令失败而执行。

1.2 &&||:易断裂的短路执行链

&&(逻辑与)和 ||(逻辑或)是 Shell 的短路运算符,组合使用时形成「从左到右」的链式执行逻辑,其核心规则为:

  • A && B:仅当 A 执行成功(返回0)时,才执行 B;若 A 失败,直接跳过 B;
  • A || B:仅当 A 执行失败(返回非0)时,才执行 B;若 A 成功,直接跳过 B;
  • 对于组合表达式 A && B || C,Shell 会将 A && B 视为一个整体:仅当该整体执行失败时,才触发 C。

陷阱根源:开发者常误将 A && B || C 等价于 if A then B else C,但实际逻辑中,B 的执行状态会改变整体结果——即便 A 成功,若 B 失败,仍会触发 C,这与 if-else 的分支互斥逻辑完全不同。

二、实战攻击:利用逻辑陷阱突破脚本限制

下面通过一个真实场景的脚本案例,展示攻击者如何利用 &&|| 的逻辑漏洞,绕过正常验证获取系统权限。

2.1 存在漏洞的脚本

该脚本设计初衷是:用户输入的数字与随机数匹配时,才授予交互式 Shell;否则仅输出「Nop」提示。

#!/bin/bash
# 生成随机数作为验证密钥
My_guess=$RANDOM
# 开发者意图:猜错输出Nop,猜对获取Shell
[ "$1" != "$My_guess" ] && echo "Nop" || bash -i

2.2 开发者的错误认知

脚本编写者认为逻辑等价于:

if [ "$1" != "$My_guess" ]; then
    echo "Nop"
else
    bash -i
fi

即「猜错执行 echo,猜对执行 bash -i」,但忽略了 &&|| 组合中 echo 命令执行状态的影响。

2.3 攻击者的突破思路

攻击者无需猜测随机数,只需让 echo "Nop" 执行失败(返回非0状态),即可触发 || 后的 bash -i。核心技巧是关闭进程的标准输出,让 echo 写入失败。

攻击操作命令

# 向脚本传入任意数字,同时关闭标准输出(&>- 等价于 >/dev/null 2>&1)
./script.sh 12345 &>-

执行过程拆解(关键步骤)

  1. 条件判断:[ "$1" != "$My_guess" ] 为真(输入的12345≠随机数),返回0;
  2. 执行 && 后命令:echo "Nop";
  3. 核心漏洞触发:由于 &>- 关闭了标准输出,echo 无法写入,返回非0(执行失败);
  4. 整体逻辑结果:[ ... ] && echo "Nop" 因 echo 失败变为「假」;
  5. 触发 || 后命令:执行 bash -i,攻击者成功获取交互式 Shell。

补充:恢复完整 Shell 环境

由于攻击时关闭了标准输出,新获取的 Shell 可能无回显,需执行以下命令恢复:

/bin/bash -i >&2  # 将标准输出重定向到标准错误,恢复交互性

2.4 攻击结果

攻击者无需知晓随机数的真实值,仅通过操纵命令执行状态,就绕过了脚本的验证逻辑,成功获取系统交互式 Shell,造成权限泄露。

三、安全修复:回归结构化编程

解决该漏洞的核心是放弃 &&|| 替代 if-else 的简化写法,回归结构化的条件判断,确保分支逻辑的绝对互斥。

3.1 修复后的安全代码

#!/bin/bash
My_guess=$RANDOM
# 使用标准 if-else 结构化判断
if [ "$1" != "$My_guess" ]; then
    echo "Nop"
else
    bash -i
fi

3.2 修复原理

在 if-else 结构中:

  • 分支选择仅由 if 后的条件表达式结果决定,与分支内命令的执行状态无关;
  • 即便攻击者通过 &>- 让 echo "Nop" 执行失败,then 分支已完成执行,else 分支绝不会被触发;
  • 上述攻击手段完全失效,脚本逻辑回归预期。

四、扩展:通用避坑指南

除了上述安全场景,日常脚本编写中使用 &&|| 还需注意以下问题:

4.1 禁止用 A && B || C 替代 if-else

这是最核心的原则,尤其是涉及权限控制、敏感操作(如执行命令、修改文件)的场景,必须使用结构化 if-else/case 等语法。

4.2 明确命令执行状态

若确需使用 &&/||(如简单的命令串联),需确保前序命令的执行状态可控:

# 安全写法:明确处理前序命令的失败场景
command1 && command2 || { echo "command1或command2失败" >&2; exit 1; }

4.3 养成状态检查习惯

编写脚本时,可通过 set -e 让脚本在命令失败时立即退出,或通过 $? 手动检查命令执行状态,避免隐藏的逻辑错误。

五、总结

&&|| 短路组合逻辑不等价于 if-else:前者受中间命令执行状态影响,后者仅由条件表达式决定分支;

Shell 脚本的简洁性需以安全性为前提,看似微小的语法选择,可能成为系统安全的重大隐患。遵循结构化编程规范,才能从根源上规避此类逻辑陷阱。

原文 https://blog.csdn.net/2301_79518550/article/details/157362290