"Que tal um truque de mágica?" HAHAHAHAHA
BOITA{Xpl01t1ng_Xt3ns10n5}
O desafio começa nos dando uma aplicação web que nos fornece um código de uma extensão que consome a API da aplicação. Sendo o código da aplicação:
from flask import Flask, jsonify, request, render_template, send_file
from flask_cors import CORS
from waitress import serve
import requests, re, os
app = Flask(__name__)
CORS(app)
def getContent(url):
try:
response = requests.get(url)
if response.status_code == 200:
return(response.text)
else:
print(f"Failed to retrieve content. Status code: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
@app.route('/api/', methods=['GET'])
def get_template_files():
template_dir = './shaco/templates'
template_files = [f for f in os.listdir(template_dir) if f.endswith('.html')]
return jsonify(template_files)
@app.route('/api/', methods=['POST'])
def post_data():
url = request.json.get('url')
content = getContent(url)
title_match = re.search(r'<title>(.*?)</title>', content, re.IGNORECASE)
if title_match:
title = title_match.group(1)
else:
title = 'null'
fileTitle = re.sub(r' ', '_', title)
fullPath = f'./shaco/templates/{fileTitle}'
if os.path.exists(fullPath):
with open(fullPath, 'r', encoding='utf-8') as file:
return jsonify({'message': 'File already exist', 'content': file.read()}), 200
else:
with open(fullPath+'.html', 'a', encoding='utf-8') as file:
file.write(content)
return jsonify({'message': 'File does not existe', 'content': f'File created: {fullPath}'}), 200
@app.route("/")
def home():
return render_template('index.html')
@app.route('/file/<file_name>')
def render_html(file_name):
template_file = f"{file_name}"
if os.path.exists(os.path.join("./shaco/templates/", template_file)):
return render_template(template_file)
else:
return "File not found", 404
@app.route('/shaco')
def download_file():
file_path = './shaco.zip'
return send_file(file_path, as_attachment=True)
if __name__ == '__main__':
print('[+]Name: Shaco\n[+]Host: 0.0.0.0\n[+]Port: 80\n[+]Status: Up')
os.popen("cron &")
serve(app, host='0.0.0.0', port=80)
É possível perceber que a aplicação pega todas as requisições feitas no endpoint /api
, com o parâmetro url
e criando um arquivo contendo o conteúdo da página e tendo o titulo da página (contido entre tags de <title>
) como nome do arquivo. Em caso que esse arquivo já exista, ele mostrará o conteúdo do arquivo. Entretanto, não existe nenhum tipo de verificação contra ataques do tipo (CWE-35) Path Transversal.
Nesse caso, temos um caso típico de Require Statement in PHP Program (PHP Remote File Inclusion), onde será possível (nesse caso) ler o conteúdo de arquivos em que já temos conhecimento do nome.
Para obter a flag, podemos criar um payload no Pastebin, contendo o seguinte conteúdo:
<title>../../flag.txt</title>
E realizando uma requisição para o endpoint /api
com o parâmetro url=[PASTE_BIN_URL]
, assim, obtendo a flag.