看着Y1ng师傅的博客复现一波
https://github.com/ByteBandits/bbctf-2020
docker环境就不说了docker-compose up -d 一把梭
就一个文件上传功能。暂时还没遇到过python文件上传直接getshell。
上传后可以查看图片
http://192.168.0.128:7003/view/f528764d624db129b32c21fbca0cb8d6/1.jpg
F12查看view/md5/1.jpg又是指向的/uploads/f528764d624db129b32c21fbca0cb8d6/1.jpg
直接访问图片直接爆500???
看了writeup都不知道为啥会爆500
这里Y1ng师傅猜测是以path:path这样传值。然后去读取对应的文件
直接给出payload
..%252f..%252f..%252f..%252f..%252f..%252etc%252fpasswd
这里没找到原因。我本地搭建环境时。两次URL编码并没有绕过。只解了一次URL编码
任意文件读取../app.py
得到源码
from flask import Flask, render_template, request, flash, redirect, send_file
from urllib.parse import urlparse
import re
import os
from hashlib import md5
import asyncio
import requests
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.path.join(os.curdir, "uploads")
# app.config['UPLOAD_FOLDER'] = "/uploads"
app.config['MAX_CONTENT_LENGTH'] = 1*1024*1024
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
ALLOWED_EXTENSIONS = {'png', 'jpg', 's'}
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.mkdir(app.config['UPLOAD_FOLDER'])
def secure_filename(filename):
return re.sub(r"(\.\.|/)", "", filename)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/")
def index():
return render_template("home.html")
@app.route("/upload", methods=["POST"])
def upload():
caption = request.form["caption"]
file = request.files["image"]
if file.filename == '':
flash('No selected file')
return redirect("/")
elif not allowed_file(file.filename):
flash('Please upload images only.')
return redirect("/")
else:
if not request.headers.get("X-Real-IP"):
ip = request.remote_addr
else:
ip = request.headers.get("X-Real-IP")
dirname = md5(ip.encode()).hexdigest()
filename = secure_filename(file.filename)
upload_directory = os.path.join(app.config['UPLOAD_FOLDER'], dirname)
if not os.path.exists(upload_directory):
os.mkdir(upload_directory)
upload_path = os.path.join(app.config['UPLOAD_FOLDER'], dirname, filename)
file.save(upload_path)
return render_template("uploaded.html", path = os.path.join(dirname, filename))
@app.route("/view/<path:path>")
def view(path):
return render_template("view.html", path = path)
@app.route("/uploads/<path:path>")
def uploads(path):
# TODO(noob):
# zevtnax told me use apache for static files. I've
# already configured it to serve /uploads_apache but it
# still needs testing. I'm a security noob anyways.
return send_file(os.path.join(app.config['UPLOAD_FOLDER'], path))
if __name__ == "__main__":
app.run(port=5000)
在最后有个uploads_apache。是用apache去访问
然后继续看文件上传代码
filename = secure_filename(file.filename)
upload_directory = os.path.join(app.config['UPLOAD_FOLDER'], dirname)
upload_path = os.path.join(app.config['UPLOAD_FOLDER'], dirname, filename)file.save(upload_path)
filename。经过secure_filename函数处理。最终变成文件名。然后保存
secure_filename会处理..或者/
所以我们可以传入.htacces..s
处理后..被置空。然后就变成了.htaccess
然后通过upload_apache去上传.htaccess和php的图片马就getshell了