CTF 中绕过过滤执行系统命令——运算符的妙用
在 CTF 竞赛中,命令注入是常见的一类攻击技巧,而在一些复杂的注入场景下,运算符的巧妙使用可以成为绕过防护、执行系统命令的重要手段。
1. 使用运算符链构造数字
在 CTF 中,我们常常需要通过特定的运算符技巧来绕过输入过滤器。一个经典的技巧是通过多层嵌套和运算符串联构造特定的数字或字符。在此,我们以构造数字18为例,展示如何利用 Shell 命令的算术运算符、取反符号以及多层嵌套结构巧妙地绕过复杂的过滤条件。
假设在一个 Web 应用中,存在如下的 PHP 代码,它接收用户输入并执行系统命令:
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\{|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){system("cat /".$c.".php");
}
在这个例子中,PHP preg_match()
函数用于过滤非法字符,防止用户输入中包含一些常见的恶意字符,如分号 (;
)、字母 ([a-z]
)、数字 ([0-9]
)、管道 (|
)、反引号 (\``)、
#(注释符号)、大括号 (
{})、引号 (
'或
"`),以及其他特殊字符。
攻击者的目标是绕过这些过滤规则,并通过 system()
函数执行如下命令:
system("cat /18.php");
为了实现这一点,攻击者需要通过精心设计的输入来绕过过滤器,并且构造出数字 18
。
下面是攻击者可能构造的输入:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
这段输入看起来极为复杂,包含了多个层次的嵌套和取反操作。让我们逐步分析数字构造的过程:
-
$() 运算符:这是 Shell 中的命令替换符号。它会先执行括号中的命令,然后将结果作为字符串返回。
-
~
(取反)运算符:在 Shell 中,~
是按位取反运算符。对一个数字取反会得到该数字的二进制反码。 -
嵌套结构:通过多层嵌套的
$()
和~
运算符,攻击者能将多个较小的值组合在一起,最终得出数字18
。- 例如,
$((~$(())))
会先计算最内层的命令,得到一个值,接着再取反,然后再通过外层的运算符进行计算。 - 通过逐层计算,最终能够得到我们需要的数字
18
。
- 例如,
这种嵌套和运算符链的构造方法,有效地绕过了数字和字母的过滤,因为过滤器并没有检测到 ~
或 $()
等符号的使用,它只关注了字母、数字等直接的字符。
2. 管道运算符妙用
一道例题如下:
system($cmd . " >/dev/null 2>&1");
这个 PHP 函数通过 >/dev/null 2>&1
将命令的标准输出和标准错误输出丢弃。表面上看,命令的执行结果无法被获取,但攻击者可以巧妙利用 Shell 的运算符来绕过这种限制。
在这种情况下,利用逻辑运算符 ||
可以绕过后续命令的执行限制。||
表示“逻辑或”操作符:如果左侧命令执行成功,右侧命令将不会执行。所以本题的payload如下:
cat /flag || ls
分析:
cat /flag
:尝试读取/flag
文件。||
:如果前面的cat /flag
执行成功,后面的ls
不会执行。ls
:如果cat /flag
执行失败,ls
会列出当前目录的文件。
其他管道运算符
Shell 命令注入攻击的强大之处在于,它不仅仅依赖于命令本身,还能利用 Shell 提供的各种运算符(如 ;
、&&
、||
、|
)来控制命令的执行流程。这些运算符可以帮助攻击者:
- 连接多个命令(如
&&
和||
) - 控制命令执行的顺序(如
;
分号) - 通过管道传递数据(如
|
)
总结
运算符的妙用在 CTF 中的命令注入攻防中起着至关重要的作用。通过对 Shell 运算符(如 ||
、&&
、;
和 |
)的巧妙组合,攻击者能够绕过简单的输出重定向、防止命令执行的措施。CTF 题目中的这种技术不仅能用于绕过输出抑制,还可以帮助攻击者突破命令过滤和安全限制,执行恶意命令。