什么是SSRF,它最基本的形式是什么(一)

前言

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

简单来说就是,利用带有缺陷的Web端,将其视为跳板,借此身份去访问内网那些限制性的内部系统和服务。

SSRF的范畴

在Web安全中,漏洞通常按攻击目标、信任边界来划分,相对比之前的XSS和CSRF都是客户端安全漏洞,而SSRF则属于服务端安全漏洞。

它属于输入验证不严(Improper Input Validation) 类别下的服务端漏洞,通常会打破内外网隔离边界,允许攻击者通过Web端构建恶意请求去尝试渗透内网服务。

SSRF的根本原因

SSRF的根本原因在于:服务端提供了从其他服务器或者本地获取数据的能力,但没有对这个请求作出严格的过滤、验证和限制。

在实际开发时,上述情况往往出现在下面这些场景:

  1. 危险函数滥用与未校验
    当要实现如"通过URL分析文章"、"远程图片拉取"、"Webhook机制"时往往会出现这种情况,表现为直接使用用户传入的URL交给底层HTTP去尝试请求,从而发生危险,如:
    Go如果这样写
go 复制代码
// 接收前端传入的 url 参数
targetURL := r.URL.Query().Get("url") 
// 没有任何白名单或内网 IP 过滤,直接发起请求
resp, err := http.Get(targetURL)

或者Python出现这种情况:

python 复制代码
import requests
from flask import request

@app.route('/fetch')
def fetch_url():
    url = request.args.get('url')
    # 直接请求用户可控的 url
    response = requests.get(url)
    return response.text
  1. 支持了非预期的URL协议(伪协议)

    开发时的初衷可能时希望服务器利用http://https:// 去外部尝试拉取一个图片,但是底层的HTTP库(如 PHP 的 cURL、file_get_contents 或 Java 的 URLConnection)往往支持更多协议。

    • file:///etc/passwd:利用file协议直接读取本地敏感文件
    • dict://127.0.0.1:6379/info :利用dict协议探测Redis服务状态
    • gopher://:在SSRF中被称为"万能协议",几乎可以构造任何基于TCP的应用层Payload
  2. 过滤机制被绕过(黑名单的脆弱性)

    在开发时,如果有意应对这个问题,写出了黑名单(比如禁止请求 127.0.0.1 或 192.168.x.x)。但是存在很多方案可以利用Web的特性绕过这些限制,甚至对于检测不清晰的白名单手段仍然有被绕过的可能。

    • IP进制转换:127.0.0.1 转换为十进制 http://2130706433
    • 0.0.0.0 与 localhost:使用替代的目标,同样会指向本机
    • DNS 重绑定(DNS Rebinding):通过设置很短的TTL,在服务器发起第二次解析时,直接凋亡返回127.0.0.1

典型SSRF执行过程

本文会通过几个非常简单的portswigger提供的Lab环境来演示最基本的SSRF,涉及它最典型的执行过程,简单的绕过和模糊测试。

Lab: Basic SSRF against the local server

这是最基本的SSRF,服务器不会对你输入的参数作出任何限制,你可以看到SSRF最本质的情况。

进入Lab环境,在任意一个Lab中点击Check按钮,可以看到最终被渲染的数字来自另外一个路由,而这个请求会通过stockApi来返回这个Check请求,也就是过程中,我们向当前的Web架构请求检查当前商品,而Web架构会向本地的目标URL去请求这个商品的检查,如果我们修改这个目标URL,修改为下面的情况:

可以在重放器中选中被编码的目标,在URL面板直接修改为:http://localhost/admin,点击发送可以发现返回200,之后构建删除Carlos用户即可完成Lab,这是最基本的SSRF,不存在任何限制,你可以很直观看到这个过程,我们本身未登录,只有访客身份无法访问admin,但是经过Web架构的请求,我们的身份就变为了目标Web服务器,显然它有权限访问这个/admin进而实现SSRF攻击。

Lab: Basic SSRF against another back-end system

这个Lab同样没有对请求的目标URL做任何限制,执行过程与上述完全一致,不过这个Lab的/admin,并不在目标服务器的本地,也就是无论是127.0.0.1还是localhost等是无法直接请求到目标的/admin,不过我们顺利的得到了目标内网范围:192.168.0.X:8080,我们可以直接使用模糊测试,测试1-255看看那个IP正确处理了我们的请求,进而就可以同上述的Lab一样处理了。
执行过程:

进入Lab环境,同样是点击商品页内的Check按钮,将其发送到Burp的攻击页

同样修改stockApi为192.168.0.X:8080/admin ,在IP地址的最后一位选中模糊测试,类型选择Number,范围从1-255,直接执行测试

根据返回码顺利得到目标是192.168.0.80,之后同样构建stockApi为这个IP的/admin/delete即可解决Lab

Lab: SSRF with blacklist-based input filter

这是通过黑名单绕过的Lab,通常为了安全服务端会对可能存在SSRF风险的参数位置加一些限制,如:防止127.0.0.1/localhost/0.0.0.0,因为本地的回环地址可以合法的访问所有本地服务,就会出现这样的限制,再者就是对整个URL的敏感字符限制,如admin、root等。
执行过程: 进入这个Lab后,按照同样的套路,执行检查商品,修改参数尝试访问admin

目标返回给我错误,提示这个是被限制的参数,那么我们尝试修改127.0.0.1为127.1继续尝试,这里发现仍然不行,可以继续尝试修改其他参数,比如admin改为%2561dmin,首先stockApi是在Body中的被编码的URL,这样我们将a字符双重编码,也就是变成了在过滤时是%61dmin,在目标Web服务器接受我们的参数后,发送的请求时回被再次解码最终变为admin,也就是说最终的payload为http://127.1/%2561dmin

你可以在上述右侧面板的URL编码面板看到这个双重URL编码

最终凭借双重URL绕过,也是顺利的让目标执行了我们的请求,其实双重URL绕过都是经典中的经典了,但凡和URL沾点边的时候都可一试并且一般化的过滤很容易中招。

Lab: SSRF with filter bypass via open redirection vulnerability

这是一个结合重定向和SSRF联合的漏洞链,它的实现非常容易,通常是SSRF的注入点带有及其严格的限制,比如我们要访问192.168.0.1:808,那么它的过滤或许会全屏蔽192.168.X.X,包括localhost等等本地系列回环IP,那么在这种情况下我们可以尝试在目标Web网站中找到一些带有重定向功能的点,利用站内的重定向结合SSRF让目标请求时自动重定向到我们需要的目标上去。
执行过程:

首先,在Lab中的文章页面的返回按钮上,它会将path参数后面的值,加入到头部的Location上,实现返回之前的页面,也就是我们可以通过修改Path的值,来重定向我们当前的页面URL,如果我们直接将我们需要请求的目标加入到这里,你会发现什么都不会发生,因为重定向是对当前服务器页面,由你发出的请求,你的身份自始至终都是你的本机,自然无法访问到目标内网,所以这里要结合之前到商品Check注入点来看。

之前到stockApi注入点存在严格的限制,不过我们可以将目标URL 修改为下面的URL,重点看右侧的URL编码面板

可以看到请求的当前网站的nextProduct,而它又回重定向到Path参数值的位置,我们就可以利用这个重定向,让目标请求nextProduct时,重定向到我们需要请求的/admin中,实现利用重定向绕过限制达成攻击内网的效果。

Lab: SSRF with whitelist-based input filter

这个Lab是利用了URL底层协议解析与组件特性,它通常被称为URL 解析器差异(URL Parser Differential),具体的是根据RFC 3986 中定义的 URL 标准结构:

javascript 复制代码
scheme://[user:password@]host[:port][/path][?query][#fragment]

在当前这个Lab的限制中,使用白名单机制,也就是说请求的目标一定是某一个域名,而这是一个外部域名并不是我们想要,那么我们就可以利用上述的URL标识符,通过操作构造一个恶意URL,来骗过过滤器。

首先我们此次绕过会利用到两个URL特殊字符:

  1. @ (凭证分隔符):在 @ 之前的内容会被当做用户名/密码(Auth),@ 之后才是真正要请求的 Host。
  2. #(片段标识符 / Hash):在 # 之后的内容通常被称为 Fragment,用于浏览器页面内的锚点定位。在后端的 HTTP 请求中,# 后面的内容通常会被 HTTP 客户端忽略,不会发送给目标服务器。

利用上述特殊字符的语义和校验器与请求器之间的差异来实现绕过:
执行过程:

进入Lab环境,同样通过商品Check的这个点进行注入,经过尝试我们确定这个是白名单限制,也就是说发送的域名必须是stock.weliketoshop.net,那么从表面来看这或许是完美无缺的限制,但是由于URL存在双重编码,也就是说URL校验时也就是触发限制时会将URL中的编码解析,如果通过在请求时会再次进行解析,而校验器先校验的情况下,我们利用@ 语法,构建一个:

javascript 复制代码
stockApi=http://127.0.0.1@stock.weliketoshop.net/

这样的话,127.0.0.1就会被忽略,从而绕过了限制,那么这样的URL很显然是不可能会被请求成功的,所以我们使用#来截断后方:

javascript 复制代码
stockApi=http://127.0.0.1:80%2523@stock.weliketoshop.net/admin

可以看到这里使用了%2523,而将它URL解码后是%23对应#,而此时会进行第一次URL校验,%23会被当前@前面的用户名而被忽略,紧接着进入URL请求时,会再次将%23解码为#,刚好截断后方的@stock.weliketoshop.net,从而请求到最终的127.0.0.1:80/admin。

这个 Lab 的灵魂在于 "欺骗校验器,喂食请求器" 。通过双重 URL 编码把特殊字符 # "偷运"过外层的过滤器,利用内部组件解析逻辑的差异,最终实现了在白名单限制下的 SSRF 攻击。在日常的代码审计中,但凡看到使用正则表达式或简单的 strings.Contains 来判断白名单域名的逻辑,往往都隐藏着这类危机。

总结

本文简单的讲述了SSRF,并且描述了它最基本的形式,在实际的SSRF攻击者,目标往往都不是这些内网中的管理员面板,而是mysql、Redis等等内网组件,因为SSRF有了通过Web直接与这些服务在内网对话的机会,利用如gopher://协议使用Gopherus工具尝试去渗透这些内网服务来获取更多敏感信息,甚至通过某些手段上传WebShell,进一步执行远程代码。

相关推荐
Aray123410 分钟前
浅析内网跨网段连通差异:ICMP不可达与静默丢包底层原理拆解
网络·ping
Unbelievabletobe11 分钟前
港股api的WebSocket推送如何订阅多只股票
网络·websocket·网络协议
幼儿园技术家18 分钟前
为什么 SSR 一定会有 hydration mismatch?
前端
FlyWIHTSKY20 分钟前
Vue 3 中 RouteRecord 详解(Vue Router 4)
前端·javascript·vue.js
TechWayfarer30 分钟前
IP归属地运营商能解决什么问题?风控/增长/数据平台落地实践(附API代码)
开发语言·网络·python·网络协议·tcp/ip
yingyima31 分钟前
用 cron 定时发送邮件报告:实战案例详解
前端
GAMC37 分钟前
从 “凭感觉写代码” 到 “按规范做开发”:OpenSpec 让 AI 编程回归工程化
前端·人工智能
TechWayfarer43 分钟前
IP归属地运营商生产落地进阶:缓存+降级+灰度对账全解析
网络·python·网络协议·tcp/ip·缓存
微学AI1 小时前
Claude-Code-python 前端改造项目工作流程详解
开发语言·前端·python
funnycoffee1231 小时前
华为USG防火墙修改tcp aging time , default is 1200S
网络·网络协议·tcp/ip·usg aging time