SSTI

本文最后更新于:10 个月前

SSTI

sources:

https://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters

https://jinja.palletsprojects.com/en/3.1.x/templates/

https://dormousehole.readthedocs.io/en/latest/

https://xz.aliyun.com/t/9584

2023FCTF热身赛filechecker_mini

打开题目,让我们上传一个文件:

桌面上随便丢了个php文件进去提交看看会有啥情况:

判断文件类型,(MIME绕过预定)

附件下载下来先看源码:

index.html:

可以看出该网页使用模块渲染将result值渲染进index对应位置,那么就看下后端代码app.py:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
from flask import Flask, request, render_template, render_template_string
from waitress import serve
import os
import subprocess

app_dir = os.path.split(os.path.realpath(__file__))[0]
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = f'{app_dir}/upload/'

@app.route('/', methods=['GET','POST'])
def index():
try:
if request.method == 'GET':
return render_template('index.html',result="ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿 ヽ(=^・ω・^=)丿")

elif request.method == 'POST':
f = request.files['file-upload']
filepath = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)

if os.path.exists(filepath) and ".." in filepath:
return render_template('index.html', result="Don't (^=◕ᴥ◕=^) (^=◕ᴥ◕=^) (^=◕ᴥ◕=^)")
else:
f.save(filepath)
file_check_res = subprocess.check_output(
["/bin/file", "-b", filepath],
shell=False,
encoding='utf-8',
timeout=1
)
os.remove(filepath)
if "empty" in file_check_res or "cannot open" in file_check_res:
file_check_res="wafxixi ฅ•ω•ฅ ฅ•ω•ฅ ฅ•ω•ฅ"
return render_template_string(file_check_res)

except:
return render_template('index.html', result='Error ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ ฅ(๑*д*๑)ฅ')

if __name__ == '__main__':
serve(app, host="0.0.0.0", port=3000, threads=1000, cleanup_interval=30)

上面大家都用render_template()就你爱用render_template_string()是吧(指指点点,一眼模板注入,那么我们就希望file_check_res里有我们能够执行的rce代码。file_check_res哪来的?倒退往上看。

1
2
3
4
5
6
7
8
f.save(filepath)
file_check_res = subprocess.check_output(
["/bin/file", "-b", filepath],
shell=False,
encoding='utf-8',
imeout=1
)
os.remove(filepath)

先保存filepath这样一个文件,subprocess.check_output(command)返回Linux命令行输出,然后再把filepath文件删除,那么这里的file_check_res就是file -b {filepath}的结果。往上看filepath其实就是将上传文件目录的绝对路径和该文件的文件名拼接起来来标定用户上传的这个文件在容器中的绝对位置。逻辑搞明白了,现在的重点就在于如何对一个文件使用file -b 命令后返回值中能回显我们所期望的值。动手操作下flie命令,发现其不会输出文件的内容只会输出其类型,

代码中的-b参数作用:

1
-b  #列出辨识结果时,不显示文件名称。

所以在文件名上动手脚的想法也破灭了(悲。

卡住了,向大佬博客寻求帮助,去guthub查找file命令源码。第一个仓库点开。

点开tests里面是各种针对该file命令的测试结果

这8个分别分别是在文本中写入bash脚本的4种情况和对应的用file命令执行的输出结果,可以看出如果文本内容为#!/usr/bin开头的那么输出结果中会显示文本中的其他内容。

本地做测试:创建一个文本文件修改内容如下

测试结果如下:

显而易见,输出可控,可以进行模板渲染。新建一个文本文件内容如下,上传文件

存在ssti漏洞,开始利用,调用os模块

调用popen()方法。

上传文件,获取flag。