【CTF】命令注入绕过技术专题:变量比较与逻辑运算
命令注入(Command Injection)是一种高危安全漏洞,攻击者通过向应用程序注入恶意命令,直接在操作系统层执行任意代码。本文将深入探讨命令注入的绕过技术,系统化呈现绕过思路。
背景与风险模型
命令注入的输入源通常包括以下三类:
- Web 参数:如 GET/POST 参数、URL 路径、Cookie 等。
- 环境变量:如
$PATH
、$USER
或自定义变量。 - 文件名:如上传文件名、日志文件名等,可能被用在
find
或xargs
命令中。
典型触发场景包括:
system()
函数:如 PHP 的system("ping $ip")
。eval
或反引号:如 Bash 的eval "$user_input"
或`cmd`
。find -exec
和xargs -I
:常用于文件处理。awk
的system()
函数:如awk '{system($1)}'
。
一旦攻击者控制输入点,结合巧妙的绕过技巧,即可执行恶意命令,如下载恶意脚本、提权或泄露敏感数据。
绕过思路总图
命令注入绕过技术可归纳为以下几类:
- 字符过滤绕过:针对过滤空格、斜杠、引号、
$
、括号等字符的场景。 - 语法结构绕过:利用逻辑运算符、算术运算、here-doc、IFS(内部字段分隔符)、
$@
等特性。 - 上下文特性利用:重点探讨 shell 的变量比较、test/[ 命令、[[ 关键字、(( )) 算术命令。
- 其他技巧:包括通配符、编码(如 base64)、重定向、临时文件等。
本文将深入剖析“变量比较与逻辑运算”分支,揭示其在绕过过滤和 WAF(Web 应用防火墙)中的关键作用。
变量比较与逻辑运算专项
在 shell 环境中,变量比较和逻辑运算是命令注入的核心战场。攻击者通过构造特定表达式,绕过过滤规则,控制程序逻辑,最终执行恶意命令。本节将从语法差异、“万能真值/假值”、运算符混淆以及实战 payload 四个方面,深入讲解这一分支。
三种测试命令的语法差异
shell 提供了三种主要的测试命令:test
(或 [ ]
)、[[ ]]
和 (( ))
,每种命令在功能和语法上各有侧重。以下是它们的对比:
命令 | 支持正则 | 支持算术运算 | 典型场景 |
---|---|---|---|
test 或 [ ] | 否 | 弱(-eq 、-ne 等) | 传统脚本,兼容性强 |
[[ … ]] | 是(=~ 运算符) | 弱(依赖 -eq 等) | 现代 Bash,支持正则和复杂逻辑 |
(( … )) | 否 | 强(C 风格语法) | 算术运算,位运算支持 |
1. test
或 [ ]
test
命令是 POSIX 标准的一部分,兼容性极高,但功能有限。它支持基本的字符串和数值比较,如:
[ "a" = "a" ] # 字符串相等,返回 0(真)
[ 1 -eq 1 ] # 数值相等,返回 0(真)
[ -n "$var" ] # 变量非空,返回 0(真)
局限性:
- 不支持正则表达式。
- 需要显式空格分隔参数,如
[ 1 = 1 ]
,否则报错。 - 引号敏感,
$var
未加引号可能导致语法错误。
2. [[ … ]]
[[ … ]]
是 Bash 的扩展,支持更复杂的逻辑和正则匹配。例如:
[[ "abc" =~ ^a ]] # 正则匹配,返回 0(真)
[[ 1 == 1 ]] # 支持 ==,更直观
[[ -n $var ]] # 无需引号,变量展开更安全
优势:
- 支持
=~
进行正则匹配。 - 对未定义变量更宽容。
- 支持
&&
、||
等逻辑运算符直接在[[ ]]
内部使用。
3. (( … ))
(( ))
专为算术运算设计,语法类似 C 语言:
(( 1 + 1 == 2 )) # 算术运算,返回 0(真)
(( a = 1, b = 2, 3 )) # 逗号运算,返回最后一个值(3,true)
特点:
- 支持位运算(
&
、|
、^
)和复杂算术表达式。 - 非零值均为真,
(( 0 ))
为假。 - 不需要
$
前缀引用变量。
“万能真值”速查表
在命令注入中,构造“万能真值”表达式是绕过过滤的关键。这些表达式在任何布尔值场景下都能返回 0(真),适用于逻辑短路或条件跳转。常见构造如下:
-
字符串比较:
[ "x" = "x" ] # 始终为真 [[ "x" == "x" ]] # [[ ]] 写法
-
数值比较:
(( 1 )) # 非 0 即为真 (( 7 == 7 )) # 算术相等
-
逻辑短路:
[ 1 = 1 -o 1 ] # -o 表示或,恒真 [[ 1 -eq 1 || 1 ]] # || 表示或,恒真 (( 1 == 1 || 1 )) # 算术逻辑或
“万能假值”速查表
与真值类似,“万能假值”在需要返回非 0(假)的场景下有用:
-
字符串比较:
[ "" = "x" ] # 空字符串不等于非空,假 [ 1 = 2 ] # 不相等,假
-
数值比较:
(( 0 )) # 0 为假 (( 1 == 2 )) # 不相等,假
运算符混淆与 WAF 绕过
WAF 通常通过正则匹配过滤危险字符(如 &&
、||
、=
、;
等)。攻击者可以通过运算符混淆和替代语法绕过这些限制。
1. -a/-o
与 &&/||
的互换
在 test
命令中,-a
(与)和 -o
(或)可替代 &&
和 ||
:
[ 1 = 1 -a 2 = 2 ] # 等价于 [ 1 = 1 ] && [ 2 = 2 ]
[ 1 = 1 -o 2 = 3 ] # 等价于 [ 1 = 1 ] || [ 2 = 3 ]
当 WAF 过滤了 &&
或 ||
,使用 -a
或 -o
可有效绕过。
2. 逗号运算符
在 (( ))
中,逗号 ,
用于顺序求值,返回最后一个表达式的结果:
(( a=1, b=2, 3 )) # 返回 3(真)
(( a=1, b=2, 0 )) # 返回 0(假)
这在需要多步操作或绕过复杂过滤时非常有用。
3. 位运算替代逻辑运算
当 &&
和 ||
被过滤时,位运算符 &
(与)、|
(或)、^
(异或)可作为替代:
(( 1 & 1 )) # 返回 1(真)
(( 0 | 1 )) # 返回 1(真)
(( 1 ^ 0 )) # 返回 1(真)
位运算符在 WAF 规则中较少被关注,适合高限制场景。
4. 反引号与 $()
嵌套
在 [[ ]]
中,命令替换(`cmd`
或 $(cmd)
)可嵌入动态值:
[[ `echo 1` == 1 ]] # 命令替换返回 1,比较为真
[[ $(whoami) == root ]] # 动态比较当前用户
这允许攻击者在过滤严格的环境中构造动态 payload。
实战 Payload 演示
假设注入点为 sh -c "test $1"
,以下是几种典型场景的绕过 payload:
场景 A:单引号包裹,引号闭合失败
Payload:
1' -o '1'='1
解析:
- 输入被插入到
sh -c "test 1' -o '1'='1"
。 -o
表示逻辑或,1'='1
恒真,最终返回 0(真)。- 绕过单引号过滤,利用逻辑运算符短路。
场景 B:过滤空格
Payload:
1=1-o1
解析:
test
命令的参数解析不强制要求空格。1=1-o1
被解析为[ 1 = 1 -o 1 ]
,恒真。- 适合 WAF 过滤空格的场景。
场景 C:过滤 =
和 ==
Payload:
1-eq1
解析:
- 使用
-eq
替代=
,构造[ 1 -eq 1 ]
,返回真。 - 或者使用
[[ 1 -eq 1 ]]
,兼容性更强。
场景 D:复杂逻辑链
Payload:
1-eq1&&whoami
解析:
[ 1 -eq 1 ] && whoami
:前半部分为真,触发whoami
执行。- 如果
&&
被过滤,可用(( 1 & 1 )) && whoami
。
防御与检测
防御措施
- 允许列表:严格校验输入,只允许预定义的值。例如,限制
$ip
仅为合法 IP 地址格式。 - 参数数组:避免直接拼接用户输入,使用数组传递参数:
cmd=(ping -c 1 "$ip") "${cmd[@]}"
- 转义与清理:对输入进行严格转义,移除危险字符(如
;
、&
、|
)。
检测方法
- ShellCheck:静态分析脚本,检测潜在命令注入点:
shellcheck script.sh
- Grep 规则:扫描代码中的危险函数:
grep -RE '(eval|`|system|exec)' --include='*.sh' .
- 运行时监控:使用 eBPF 或 auditd 监控异常命令执行:
auditctl -a always,exit -F arch=b64 -S execve -k cmd_injection
可直接引用的示例片段
一行真值速记
#!/bin/bash
[ "$1" = "$1" ] && echo "always true" # 通用字符串比较
(( 1 )) && echo "numeric always true" # 算术真值
[[ a =~ a ]] && echo "regex always true" # 正则真值
构造命令链
# 绕过空格、&、| 被过滤
cmd=ls
$cmd$IFS-l$IFS/
ShellCheck 白名单规则
# 禁止反引号与 eval
grep -RE '(eval|`|exec)' --include='*.sh' .
总结
在 shell 命令注入中,变量比较与逻辑运算是绕过过滤的“核武器”。通过构造“万能真值/假值”、混淆运算符、利用命令替换,攻击者可突破严格的 WAF 规则。防御方面,允许列表、参数数组和运行时监控是关键。引用一句总结:
“在 shell 里,只要能让比较表达式返回 0,你就拥有了命令注入的第一把钥匙;而逻辑运算符、算术运算、正则、通配,都是复制这把钥匙的 3D 打印机。”