什么是命令执行漏洞

命令执行漏洞,就是指用户通过浏览器或其他辅助程序提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。

用户可以在命令执行函数内直接调用操作系统命令

以下内容默认为在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