实验--PHP-FPM_未授权访问漏洞
摘要
PHP-FPM 服务暴漏导致的命令执行
### 实验--PHP-FPM_未授权访问漏洞
实验环境:
攻击机:kali-linux-2020.3 IP:192.168.64.129
服务机:Ubuntu 20 IP:192.168.64.128
环境准备-Ubuntu 20 准备PHP-FPM环境
安装Nginx
1 | sudo apt-get install nginx |
安装php、php-fpm以及一些插件
1 | sudo apt-get install software-properties-common |
稍有变动
PHP-FPM 设置
设置监听9000端口来处理 Nginx 的请求,并将 PHP-FPM 暴露在 0.0.0.0 上
/etc/php/7.4/fpm/pool.d/www.conf
:
1 | ;listen = /run/php/php7.4-fpm.sock |
此时将 PHP-FPM 的监听地址设置为了
0.0.0.0:9000
,便会产生PHP-FPM 未授权访问漏洞,此时攻击者可以直接与暴露在目标主机 9000 端口上的 PHP-FPM 进行通信,进而可以实现任意代码执行。
修改权限
1 | chmod 777 /run/php/php7.4-fpm.sock |
Nginx 配置
/etc/nginx/sites-available/default
:
1 | server { |
环境启动
php-fpm
1 | whereis php-fpm |
Nginx
1 | sudo systemctl restart nginx |
systemctl status nginx
检查Nginx状态
入口文件
/var/www/html
下设置index.php
1 | phpinfo(); |
服务开启效果:
原理准备
[浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法](https://whoamianony.top/2021/05/15/Web安全/浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法/)
Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
CGI
为了解决Web服务器与外部应用程序(CGI程序)之间数据互通,于是出现了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一个进程,效率提高了。
FastCGI
但是Web服务器有一个问题,就是它每收到一个请求,都会去Fork一个CGI进程,请求结束再kill掉这个进程,这样会很浪费资源。于是,便出现了CGI的改良版本——Fast-CGI。
FastCGI致力于减少网页服务器与CGI程序之间交互的开销,Fast-CGI每次处理完请求后,不会kill掉这个进程,而是保留这个进程,从而使服务器可以同时处理更多的网页请求。这样就会大大的提高效率。
PHP-FPM
官方对PHP-FPM的解释是 FastCGI 进程管理器,用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。PHP-FPM 默认监听的端口是 9000 端口。
也就是说 PHP-FPM 是 FastCGI 的一个具体实现,并且提供了进程管理的功能,在其中的进程中,包含了 master 和 worker 进程,这个在后面我们进行环境搭建的时候可以通过命令查看。其中master 进程负责与 Web 服务器中间件进行通信,接收服务器中间按照 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 对:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 {
'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-FPM 拿到 Fastcgi 的数据包后,进行解析,根据请求情况设置环境变量(如:$_SERVER
),动态执行 PHP 代码。
任意代码执行
SCRIPT_FILENAME
对应的是 PHP-FPM 将要执行哪个PHP文件 (正常情况下视请求而定,动态执行)
在 PHP 5.3.9 后来的版本中,PHP 增加了
security.limit_extensions
安全选项,导致只能控制 PHP-FPM 执行一些像 php、php3、php4、php5、php7 这样的文件,因此你必须找到一个已经存在的 PHP 文件,这也增大了攻击的难度。
我们熟悉的是:php.ini 有 auto_prepend_file
和auto_append_file
.htaccess马常使用这两个值操作
如果我们可以设置 auto_prepend_file
为 php://input
,那么就等于在执行任何 PHP 文件前都要包含一遍 POST 的内容。所以,我们只需要把需要执行的代码放在 Body 中,他们就能被执行了。(当然,这还需要开启远程文件包含选项 allow_url_include
)
PHP_VALUE 和 PHP_ADMIN_VALUE
PHP-FPM 的两个环境变量:PHP_VALUE
与PHP_ADMIN_VALUE
这两个环境变量可以用来设置 PHP 配置项
PHP_VALUE
可以设置模式为 PHP_INI_USER
和 PHP_INI_ALL
的选项
PHP_ADMIN_VALUE
可以设置所有选项。
完成攻击
FPM 请求
Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
1 | import socket |
成功命令执行!
参考文章:
[浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法](https://whoamianony.top/2021/05/15/Web安全/浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法/)
实验--PHP-FPM_未授权访问漏洞