什么是命令执行漏洞
命令执行漏洞,就是指用户通过浏览器或其他辅助程序提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。
用户可以在命令执行函数内直接调用操作系统命令。
以下内容默认为在PHP中。
常见的命令执行函数
system
- 简介
执行一个外部函数,并显示输出
- 语法
system(string $command, int &$return_var = ?): string
- 参数
conmand:要执行的命令
return_var:如果提供此参数,则外部命令执行后的返回状态会被设置到此变量中
- 返回值
成功则返回命令输出的最后一行,失败则返回false
- 例子
exec
- 简介
执行一个外部程序
- 语法
exec(string $command, array &$output = ?, int &$return_var = ?): string
- 参数
command:要执行的命令
output:如果执行此参数,那么会用命令执行的输出填充此数组,每行输出填充数组中的一个元素,数据中不包含行尾的空白字符,例如\n
字符。如果数组中已经包含了部分元素,exec()函数会在此数组末尾进行追加。
return_var:如果同时提供output和次参数,命令执行后的状态会被写入到此变量
- 返回值
命令执行结果的最后一行内容
- 例子
popen
- 简介
打开一个指向进程的管道,该进程由派生给定的command命令执行而产生
- 语法
popen(string $command, string $mode): resource
- 参数
command:命令
mode:模式。
r
:只读
w
:只写(打开并清空已有文件或创建一个新文件)
- 返回值
返回一个和popen()
所返回的相同的文件指针,只不过它是单向的(只能用于读和写)并且必须用pclose()
来关闭,此指针可以用于fgets()
、fgetss()
和fwrite()
。当模式为r
时,返回的文件指针等于命令的 STDOUT,当模式为 w
,返回的文件指针等于命令的 STDIN。
- 例子
passthru
- 简介
执行外部程序并显示原始输出
- 语法
passthru(string $command, int &$return_var = ?): void
- 参数
command:要执行的命令
return_var:如果提供此参数,Unix命令的返回状态会被记录到此参数
- 返回值
无返回值
- 例子
shell_exec
- 简介
通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
- 语法
shell_exec(string $cmd): string
- 参数
cmd:要执行的命令
- 返回值
命令执行的输出,错误或无输出时返回null
- 例子
执行运算符(`)
- 简介
PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出),效果与函数shell_exec相同
常见的代码执行函数
eval
- 简介
把字符串作为PHP代码执行,该字符串必须是合法的,且以分号结尾
- 语法
eval(string $code)
- 参数
code:需要被执行的字符串
- 示例

assert
- 简介
检查一个断言是否为FALSE
- 语法
PHP5
assert(mixed $assertion, string $description = ?): bool
PHP7
assert(mixed $assertion, Throwable $exception = ?): bool
assert() 会检查指定的 assertion
并在结果为 false
时采取适当的行动。
- 参数
assertion:断言
description:如果 assertion
失败了,选项 description 将会包括在失败信息里
- 示例

call_user_func
- 简介
把第一个参数作为回调函数调用
- 语法
call_user_func(callable $callback, mixed $parameter = ?, mixed $... = ?): mixed
第一个参数 callback
是被调用的回调函数,其余参数是回调函数的参数。
- 参数
callback:将被调用的回调函数
parameter:0个或以上的参数,被传入回调函数。
- 示例

call_user_func_array
- 简介
调用回调函数,并把一个数组参数作为回调函数的参数
- 语法
call_user_func_array(callable $callback, array $param_arr): mixed
- 参数
callback:被调用的回调函数。
param_arr:要被传入回调函数的数组,这个数组得是索引数组。
- 示例

create_function
- 简介
根据传递的参数创建匿名函数,并为该匿名函数返回唯一名称。
- 语法
create_function(string $args, string $code): string
- 参数
args:被调用函数的参数
code:被调用的函数的代码
- 示例
在code处存在漏洞,闭合前面语句注释后面语句,中间可以执行任意指令。

array_map
- 简介
遍历数组,为每个数组的每个元素应用回调函数。
- 语法
array_map(callable $callback, array $array, array ...$arrays): array
- 参数
callback:回调函数
array:需要便利的数组
arrays:额外的数组
- 示例

preg_replace
- 简介
执行一个正则表达式的搜索和替换
Warning:此函数被移除于PHP7.0.0
- 语法
preg_replace(
string|array $pattern,
string|array $replacement,
string|array $subject,
int $limit = -1,
int &$count = null
): string|array|null
- 参数
pattern:要搜索的模式,可以是一个字符串或字符串数组
replacement:用于替换的字符串或字符串数组
subject:要搜索的和替换的字符串或字符串数组
limit:每个模式在每个 subject
上进行替换的最大次数。默认是 -1
(无限)。
count:如果指定,将会被填充为完成的替换次数。
搜索 subject
中匹配 pattern
的部分,以 replacement
进行替换。
- 示例
此函数存在模式修饰符,其中,修饰符e会让函数对替换字符串进行后向引用替换之后,将替换后的字符串作为PHP代码评估执行(以eval函数方式),并使用执行结果作为实际参与替换的字符串

Windows下的命令连接符
Windows下的命令连接符包括&
、&&
、|
、||
。
&
&
前面的语句为假,则直接执行&后面的语句

&
前面的语句为真,则前后语句都执行

&&
&&
前面的语句为假,则直接报错,后面的语句不执行

&&
前面的语句为真,前后语句都执行

|
|
前面的语句为假,则直接报错,后面的语句也不执行

|
前面的语句为真,则直接执行后面的语句

||
前面的语句为假,就执行后面的语句

||
前面的语句为真,则只执行前面的语句,后面的语句不执行

Linux下的命令连接符
Linux下的命令连接符包括;
、&
、&&
、|
、||
。
;
;
可以连接多条命令,前后的命令都会执行

&
&
使命令在后台运行,这样就可以同时执行多条命令

&&
- 如果前面的命令不能成功执行,后面的命令也不执行

- 如果前面的命令执行成功,那么前后的命令都执行

|
- 将前面的命令输出作为后面的命令输出,前面的命令和后面的命令都执行,但是只会显示后面的命令

- 类似于程序中的if-else语句,若前面的命令执行成功,则后面的命令就不会执行;若前面的命令执行不成功,则执行后面的命令

命令执行绕过
绕过空格
- ${IFS}
$IFS
是shell的特殊环境变量,是Linux下的内部域分隔符。
$IFS
的值可以是空格、制表符、换行符或者其他自定义符号。

- $IFS$9
空格过滤可以用$IFS$9
绕过

- 制表符
%09
是制表符的URL编码,可以用来代替空格,绕过空格过滤

- {}
空格过滤可以用{}
绕过

- <
空格过滤可以用<
绕过

绕过关键字
- 变量拼接
Linux支持变量赋值,在Linux环境下可以通过变量拼接来绕过过滤规则

- 空变量
可以在变量中插入空变量
来绕过命令过滤

- 系统变量
${SHELLOPTS}
是系统变量,通常是内置变量,用法也很简单,${SHELLOPTS:start
:length
}

可以利用系统变量的字符拼接绕过过滤

- \
c\a\t
=cat

- 通配符
Linux支持利用通配符进行字符匹配,利用通配符可以匹配文件中某些不确定的字符
*
:代表任意个字符

?
:代表一个字符

[]
:匹配[]
中任意一个字符

- Base64
在Linux下可以使用base64命令进行加密和解密,输出解码后的字符再执行就可以绕过关键字了

- expr和awk